#!/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. */ "use strict"; const path = require('path'); const fs = require('fs'); const crypto = require('crypto'); const child_process = require('child_process'); const compiler = require('./compiler'); const thisDir = __dirname; const rootDir = path.resolve(thisDir, '..'); process.chdir(rootDir); const AUTOCONFIG_START = '/*** Begin automatically generated configuration ***/'; const AUTOCONFIG_END = '/*** End automatically generated configuration ***/'; function escapeRegex(string) { return string.replace(/[\/\*\.]/g, '\\$&'); } /********************************************************* * Update version number *********************************************************/ process.stdout.write("Updating version... "); let version = require('../package.json').version; try { let commit = child_process.execSync('git rev-parse HEAD', { stdio: ['ignore', 'pipe', 'ignore'], }); const head = ('' + commit).trim(); commit = child_process.execSync('git merge-base origin/master HEAD', { stdio: ['ignore', 'pipe', 'ignore'], }); const origin = ('' + commit).trim(); version += ` (${head.slice(0, 8)}${head !== origin ? `/${origin.slice(0, 8)}` : ''})`; } catch (e) {} const routes = JSON.parse(fs.readFileSync('config/routes.json')); const autoconfigRegex = new RegExp(`${escapeRegex(AUTOCONFIG_START)}[^]+${escapeRegex(AUTOCONFIG_END)}`); const autoconfig = `${AUTOCONFIG_START} Config.version = ${JSON.stringify(version)}; Config.routes = { root: '${routes.root}', client: '${routes.client}', dex: '${routes.dex}', replays: '${routes.replays}', users: '${routes.users}', }; ${AUTOCONFIG_END}`; // remove old automatically generated configuration and add the new one let configBuf = fs.readFileSync('config/config.js', {encoding: 'utf8'}); if (autoconfigRegex.test(configBuf)) { configBuf = configBuf.replace(autoconfigRegex, autoconfig); } else { configBuf += autoconfig; } fs.writeFileSync('config/config.js', configBuf); console.log("DONE"); /********************************************************* * Compile TS files *********************************************************/ process.stdout.write("Compiling TS files... "); let compileStartTime = process.hrtime(); let compiledFiles = 0; let compileOpts = { incremental: true, ignore: ['src/battle-animations.js', 'src/battle-animations-moves.js'], }; if (process.argv[2] === 'full') { delete compileOpts.ignore; } else { try { fs.statSync('data/graphics.js'); // graphics.js exists, recompile it delete compileOpts.ignore; } catch (e) {} } compiledFiles += compiler.compileToDir(`src`, `js`, compileOpts); compiledFiles += compiler.compileToDir(`src`, `js`, compileOpts); compiledFiles += compiler.compileToFile( ['src/battle-dex.ts', 'src/battle-dex-data.ts', 'src/battle-log.ts', 'src/battle-log-misc.js', 'data/text.js', 'src/battle-text-parser.ts'], 'js/battledata.js', compileOpts ); if (!compileOpts.ignore) { compiledFiles += compiler.compileToFile( ['src/battle-animations.ts', 'src/battle-animations-moves.ts'], 'data/graphics.js', compileOpts ); } const diff = process.hrtime(compileStartTime); console.log( `(${compiledFiles} ${compiledFiles !== 1 ? "files" : "file"} in ${diff[0] + Math.round(diff[1] / 1e6) / 1e3}s) DONE` ); /********************************************************* * Update cachebuster and News *********************************************************/ process.stdout.write("Updating cachebuster and URLs... "); const URL_REGEX = /(src|href)="\/(.*?)(\?[a-z0-9]*?)?"/g; function updateURL(a, b, c, d) { c = c.replace('/replay.pokemonshowdown.com/', '/' + routes.replays + '/'); c = c.replace('/dex.pokemonshowdown.com/', '/' + routes.dex + '/'); c = c.replace('/play.pokemonshowdown.com/', '/' + routes.client + '/'); c = c.replace('/pokemonshowdown.com/', '/' + routes.root + '/'); if (d) { let hash = Math.random(); // just in case creating the hash fails try { const filename = c.replace('/' + routes.client + '/', ''); const fstr = fs.readFileSync(filename, {encoding: 'utf8'}); hash = crypto.createHash('md5').update(fstr).digest('hex').substr(0, 8); } catch (e) {} return b + '="/' + c + '?' + hash + '"'; } else { return b + '="/' + c + '"'; } } function writeFiles(indexContents, preactIndexContents, crossprotocolContents, replayEmbedContents) { fs.writeFileSync('index.html', indexContents); fs.writeFileSync('preactalpha.html', preactIndexContents); fs.writeFileSync('crossprotocol.html', crossprotocolContents); fs.writeFileSync('js/replay-embed.js', replayEmbedContents); console.log("DONE"); } function updateFiles() { // add hashes to js and css files and rewrite URLs let indexContents = fs.readFileSync('index.template.html', {encoding: 'utf8'}); indexContents = indexContents.replace(URL_REGEX, updateURL); let preactIndexContents = fs.readFileSync('preactalpha.template.html', {encoding: 'utf8'}); preactIndexContents = preactIndexContents.replace(URL_REGEX, updateURL); let crossprotocolContents = fs.readFileSync('crossprotocol.template.html', {encoding: 'utf8'}); crossprotocolContents = crossprotocolContents.replace(URL_REGEX, updateURL); let replayEmbedContents = fs.readFileSync('js/replay-embed.template.js', {encoding: 'utf8'}); replayEmbedContents = replayEmbedContents.replace(/play\.pokemonshowdown\.com/g, routes.client); // add news, only if it's actually likely to exist if (__dirname.endsWith('play.pokemonshowdown.com/build-tools')) { process.stdout.write("and news... "); child_process.exec('php ' + path.resolve(thisDir, 'news-data.php'), function (error, stdout, stderr) { let 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 + stdout)); } indexContents = indexContents.replace(//g, newsData[0]); indexContents = indexContents.replace(//g, newsData[1]); writeFiles(indexContents, preactIndexContents, crossprotocolContents, replayEmbedContents); }); } else { writeFiles(indexContents, preactIndexContents, crossprotocolContents, replayEmbedContents); } } updateFiles();