mirror of
https://github.com/smogon/pokemon-showdown-client.git
synced 2026-05-07 23:40:47 -05:00
253 lines
9.3 KiB
JavaScript
Executable File
253 lines
9.3 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
|
|
*/
|
|
|
|
var path = require('path');
|
|
var fs = require('fs');
|
|
var crypto = require('crypto');
|
|
var exec = require('child_process').exec;
|
|
var sugar = require('sugar');
|
|
|
|
var thisFile = __filename;
|
|
var thisDir = __dirname;
|
|
var rootDir = path.resolve(thisDir, '..');
|
|
|
|
function toId (text) {
|
|
if (text && text.id) text = text.id;
|
|
return ('' + text).toLowerCase().replace(/[^a-z0-9]+/g, '');
|
|
}
|
|
|
|
function updateIndex () {
|
|
var indexContents = fs.readFileSync(path.resolve(rootDir, 'index.template.html'), { encoding: 'utf8' });
|
|
|
|
// add hashes to js and css files
|
|
process.stdout.write("Updating hashes... ");
|
|
indexContents = indexContents.replace(/(src|href)="\/(.*?)\?[a-z0-9]*?"/g, function (a, b, c) {
|
|
var hash = Math.random(); // just in case creating the hash fails
|
|
try {
|
|
var filename = c.replace('/play.pokemonshowdown.com/', '');
|
|
var fstr = fs.readFileSync(path.resolve(rootDir, filename), { encoding: 'utf8' });
|
|
hash = crypto.createHash('md5').update(fstr).digest('hex').substr(0, 8);
|
|
} catch (e) {}
|
|
|
|
return b + '="/' + c + '?' + hash + '"';
|
|
});
|
|
console.log("DONE");
|
|
|
|
// add news
|
|
process.stdout.write("Updating news... ");
|
|
exec('php ' + path.resolve(thisDir, 'news-data.php'), function (error, stdout, stderr) {
|
|
var newsData = [0, '[failed to retrieve news]'];
|
|
if (!error && !stderr) {
|
|
try {
|
|
newsData = JSON.parse(stdout);
|
|
} catch (e) {
|
|
console.log("git hook failed to retrieve news (parsing JSON failed):\n" + e.stack);
|
|
}
|
|
} else {
|
|
console.log("git hook failed to retrieve news (exec command failed):\n" + (error || stderr));
|
|
}
|
|
|
|
indexContents = indexContents.replace(/<!-- newsid -->/g, newsData[0]);
|
|
indexContents = indexContents.replace(/<!-- news -->/g, newsData[1]);
|
|
console.log("DONE");
|
|
|
|
process.stdout.write("Writing new `index.html` file... ");
|
|
fs.writeFileSync(path.resolve(rootDir, 'index.html'), indexContents);
|
|
console.log("DONE");
|
|
});
|
|
}
|
|
|
|
function updateLearnsets (callback) {
|
|
var reservedKeywords = ['return']; // `return` is the only ES3+ reserved keyword that (currently) raises conflicts
|
|
var numberRegExp = new RegExp('^[0-9]*');
|
|
var reservedRegExp = new RegExp('^(' + reservedKeywords.join('|') + ')$', 'g');
|
|
var alphabetize = function (a, b) {return a.localeCompare(b);};
|
|
var getLsetGen = function (lset) {return lset.charAt(0);};
|
|
|
|
var padNumString = function (str) {
|
|
switch (str.length) {
|
|
case 1: return '00' + str;
|
|
case 2: return '0' + str;
|
|
case 3: return '' + str;
|
|
}
|
|
};
|
|
var inLearnset = function (lset, learnset) {
|
|
var secondChar = lset.charAt(1);
|
|
if (secondChar !== 'L') return learnset.indexOf(lset) >= 0;
|
|
|
|
var firstFragment = lset.substr(0, 2);
|
|
var levelFragment = lset.substring(2, 5);
|
|
var paddedLevel = padNumString(levelFragment);
|
|
for (var i = 0, len = learnset.length; i < len; i++) {
|
|
if (learnset[i].substring(0, 2) !== firstFragment) continue;
|
|
// ignore PSdex starter moves sorting data
|
|
if (paddedLevel === padNumString(learnset[i].substring(2, 5))) return true;
|
|
}
|
|
return false;
|
|
};
|
|
var formatLset = function (lset) {
|
|
var secondChar = lset.charAt(1);
|
|
if (secondChar !== 'L') return lset;
|
|
var firstFragment = lset.substr(0, 2);
|
|
var levelFragment = lset.substr(2).match(numberRegExp)[0];
|
|
var sortFragment = lset.substr(2 + levelFragment.length);
|
|
return firstFragment + padNumString(levelFragment) + sortFragment;
|
|
};
|
|
|
|
var Pokedex, Learnsets, oldLearnsetsG6, newLearnsetsG6;
|
|
newLearnsetsG6 = {};
|
|
try {
|
|
Pokedex = require(path.join(rootDir, 'data', 'pokedex.js')).BattlePokedex;
|
|
Learnsets = require(path.join(rootDir, 'data', 'learnsets.js')).BattleLearnsets;
|
|
} catch (err) {
|
|
return callback(err);
|
|
}
|
|
try {
|
|
oldLearnsetsG6 = require(path.join(rootDir, 'data', 'learnsets-g6.js')).BattleLearnsets;
|
|
} catch (err) {
|
|
if (err.code !== 'MODULE_NOT_FOUND') return callback(err);
|
|
oldLearnsetsG6 = {};
|
|
}
|
|
|
|
for (var speciesid in Learnsets) {
|
|
if (!oldLearnsetsG6[speciesid] || !oldLearnsetsG6[speciesid].learnset) {
|
|
console.log("NEW ENTRY at learnsets-g6.js: " + Pokedex[speciesid].species + ".");
|
|
oldLearnsetsG6[speciesid] = {learnset: {}};
|
|
}
|
|
}
|
|
for (var speciesid in oldLearnsetsG6) {
|
|
if (!oldLearnsetsG6[speciesid] || !oldLearnsetsG6[speciesid].learnset) return callback(new TypeError("Invalid `learnsets-g6.js` entry for " + speciesid + "."));
|
|
if (!Learnsets[speciesid]) {
|
|
console.log("REMOVED ENTRY at learnsets-g6.js: " + (Pokedex[speciesid] ? Pokedex[speciesid].species : speciesid) + ".");
|
|
continue;
|
|
}
|
|
var newLearnset = {};
|
|
var oldLearnset = oldLearnsetsG6[speciesid].learnset;
|
|
var fullLearnset = Learnsets[speciesid].learnset;
|
|
if (!fullLearnset) return callback(new TypeError("Invalid data at `learnsets.js` for " + speciesid + "."));
|
|
|
|
// copy, but ignore moves removed in main file
|
|
for (var moveid in oldLearnset) {
|
|
if (!Array.isArray(oldLearnset[moveid])) return callback(new TypeError("Invalid data at `learnsets-g6.js` for " + speciesid + ":" + moveid + "."));
|
|
if (!fullLearnset[moveid]) continue;
|
|
newLearnset[moveid] = [];
|
|
for (var i = 0, len = oldLearnset[moveid].length; i < len; i++) {
|
|
if (!inLearnset(oldLearnset[moveid][i], fullLearnset[moveid])) continue;
|
|
newLearnset[moveid].push(oldLearnset[moveid][i]);
|
|
}
|
|
}
|
|
|
|
for (var moveid in fullLearnset) {
|
|
if (!Array.isArray(fullLearnset[moveid])) return callback(new TypeError("Invalid data at `learnsets.js` for " + speciesid + ":" + moveid + "."));
|
|
|
|
if (!newLearnset[moveid]) newLearnset[moveid] = [];
|
|
newLearnset[moveid] = newLearnset[moveid].map(formatLset);
|
|
for (var i = 0, len = fullLearnset[moveid].length; i < len; i++) {
|
|
if (getLsetGen(fullLearnset[moveid][i]) !== '6') continue;
|
|
if (inLearnset(fullLearnset[moveid][i], newLearnset[moveid])) continue;
|
|
newLearnset[moveid].push(formatLset(fullLearnset[moveid][i]));
|
|
}
|
|
newLearnset[moveid].sort(alphabetize);
|
|
}
|
|
|
|
newLearnsetsG6[speciesid] = {learnset: newLearnset};
|
|
}
|
|
|
|
var buf = [];
|
|
var pokemonList = Object.values(Pokedex).sort(function (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').map(toId);
|
|
|
|
for (var i = 0, len = pokemonList.length; i < len; i++) {
|
|
var entry = newLearnsetsG6[pokemonList[i]];
|
|
if (!entry || !entry.learnset) continue;
|
|
var lsetSerialized = '{' + Object.keys(entry.learnset).sort(alphabetize).map(function (moveid) {
|
|
return moveid.replace(reservedRegExp, '"$1"') + ':' + JSON.stringify(entry.learnset[moveid]);
|
|
}).join(',') + '}';
|
|
buf.push(pokemonList[i] + ':{learnset:' + lsetSerialized + '}');
|
|
}
|
|
|
|
var 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);
|
|
}
|
|
|
|
var pendingFiles = 0;
|
|
var 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.
|
|
}
|
|
try {
|
|
updateStats = fs.statSync(thisFile);
|
|
updateMTime = updateStats.mtime.getTime();
|
|
} catch (err) {
|
|
throw err; // !!
|
|
}
|
|
|
|
// update learnsets-g6
|
|
process.stdout.write("Updating file `data/learnsets-g6`... ");
|
|
|
|
pendingFiles++;
|
|
var learnsetsStats;
|
|
var learnsetsG6Stats;
|
|
var learnsetsG6ToUpdate = true;
|
|
|
|
try {
|
|
learnsetsStats = fs.statSync(path.join(rootDir, 'data', 'learnsets.js'));
|
|
} catch (err) {
|
|
// 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 (!--pendingFiles) updateIndex();
|
|
}
|
|
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 (!--pendingFiles) updateIndex();
|
|
}
|
|
}
|
|
}
|
|
|
|
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(function (err) {
|
|
if (err) {
|
|
var stack = err.stack || '';
|
|
stack = "File `data/learnsets-g6` failed to update.\n" + stack;
|
|
console.error(stack);
|
|
} else {
|
|
console.log("DONE");
|
|
}
|
|
if (!--pendingFiles) updateIndex();
|
|
});
|
|
} else {
|
|
console.log("CACHED");
|
|
if (!--pendingFiles) updateIndex();
|
|
}
|