stardew-git
Version:
A tool that versions changes in Stardew Valley savegames with git.
160 lines (137 loc) • 3.78 kB
JavaScript
const chalk = require('chalk')
, chokidar = require('chokidar')
, fs = require('fs-extra')
, Git = require('simple-git')
, inquirer = require('inquirer')
, path = require('path')
, prettifyXml = require('prettify-xml')
, xml2js = require('xml2js')
require('manakin').global
const fileNames = {
savegameInfo: 'SaveGameInfo'
}
module.exports = class StardewWatcher {
constructor(directory) {
this.directory = path.normalize(directory)
this._files = {
savegame: path.basename(this.directory),
additional: [
fileNames.savegameInfo
]
}
this.wait = false
if(!fs.existsSync(path.normalize(this.directory))) {
throw new Error(`Path "${directory}" not found!`)
}
if(!this.isSdvSaveDirectory()) {
throw new Error(`Path "${directory} is not a Stardew Valley savegame directory!`)
}
}
get files() {
return [
this._files.savegame,
...this._files.additional
]
}
get filesAbsolute() {
return this.files
.map(x => {
return path.join(this.directory, x)
})
}
async init() {
return new Promise((resolve, reject) => {
this.git = Git(this.directory)
this.git.checkIsRepo((err, isRepo) => {
if(isRepo) return resolve(this)
this.git.init(err => {
if(err) return reject()
})
inquirer.prompt([{
type: 'input',
name: 'url',
message: 'Enter the URL for a remote git repository:'
}]).then(answers => {
this.git.addRemote('origin', answers.url, err => {
if(err) return reject()
resolve(this)
})
})
})
})
}
isSdvSaveDirectory() {
return fs.existsSync(path.join(this.directory, path.basename(this.directory)))
&& fs.existsSync(path.join(this.directory, fileNames.savegameInfo))
}
run(options) {
console.info('Watching!')
chokidar.watch(path.join(this.directory, this._files.savegame), { awaitWriteFinish: true })
.on('change', () => {
if(this.wait) return
this.wait = true
process.stdout.write(`Savegame "${chalk.green(this._files.savegame)}" changed!`)
const promises = []
try {
for(let file of this.filesAbsolute) {
promises.push(new Promise((resolve, reject) => {
return fs.readFile(file)
.then(data => {
data = data.toString()
if(file.endsWith(fileNames.savegameInfo)) {
new xml2js.Parser().parseString(data, (err, xml) => {
if(err) return reject(err)
this.day = parseInt(xml.Farmer.stats[0].daysPlayed[0])
process.stdout.write(`\u0008: ${chalk.yellow(`day #${this.day}!`)}`)
})
}
return fs.writeFile(file, prettifyXml(data.toString()))
})
.then(resolve)
.catch(err => {
console.log(' ')
reject(err)
})
}))
}
Promise.all(promises)
.then(() => {
return new Promise((resolve, reject) => {
this.git.silent(true)
.add(this.files, err => {
if(err) return reject(err)
process.stdout.write(`\u0008, ${chalk.cyan(`added`)}!`)
})
.commit(`Day #${this.day}.`, err => {
if(err) return reject(err)
process.stdout.write(`\u0008, ${chalk.cyan(`committed`)}!`)
if(!options.push) resolve()
})
if(options.push) {
this.git
.push('origin', err => {
if(err) return reject(err)
process.stdout.write(`\u0008, ${chalk.cyan(`pushed`)}!`)
resolve()
})
}
})
})
.then(() => {
console.log()
})
.catch(err => {
console.log()
console.error(err)
})
.then(() => {
setTimeout(() => {
this.wait = false
}, 3000)
})
} catch(e) {
console.error(e)
}
})
}
}