UNPKG

@bscotch/yy

Version:

Stringify, parse, read, and write GameMaker yy and yyp files.

82 lines 3.4 kB
import fsp from 'node:fs/promises'; import { assert, joinPaths, parsePath } from './types/utility.js'; const platformNames = [ 'amazonfire', 'android', 'html5', 'ios', 'linux', 'mac', 'operagx', 'ps4', 'switch', 'tvos', 'windows', 'windowsuap', 'xbone', 'xboxseriesxs', ]; /** * Set the project version in all options files. * (Note that the Switch options files do not include the version * -- that must be set outside of GameMaker in the *.nmeta file). * Can use one of: * + "0.0.0.0" syntax (exactly as GameMaker stores versions) * + "0.0.0" syntax (semver without prereleases -- the 4th value will always be 0) * + "0.0.0-rc.0" syntax (the 4th number will be the RC number) * The four numbers will appear in all cases as the string "major.minor.patch.candidate" */ export async function setProjectVersion(yypPath, versionString) { assert(yypPath.endsWith('.yyp'), 'First argument must be a path to a .yyp file'); const projectFolder = parsePath(yypPath).parent; const parts = versionString.match(/^(?<major>\d+)\.(?<minor>\d+)\.(?<patch>\d+)((\.(?<revision>\d+))|(-rc.(?<candidate>\d+)))?$/); if (!parts) { throw new Error(`Version string ${versionString} is not a valid format.`); } const { major, minor, patch, revision, candidate } = parts.groups; const normalizedVersionString = [ major, minor, patch, candidate || revision || '0', ].join('.'); const optionsDir = joinPaths(projectFolder, 'options'); const optionsFiles = await listFiles(optionsDir, ['.yy', '.nmeta']); for (const file of optionsFiles) { // Load it, change the version, and save const parsedPath = parsePath(file); if (parsedPath.ext == '.yy') { // Read the file and replace anything that looks like a version line with the provided version. A version line looks like "option_..._version":"x.y.z.w" const content = await fsp.readFile(file, 'utf8'); const newContent = content.replace(new RegExp(`("option[^"]*(?:${platformNames.join('|')})_version":\\s*")[\\d.]+"`), `$1${normalizedVersionString}"`); await fsp.writeFile(file, newContent); } // Switch *.nmeta file needs special treatment else if (parsedPath.ext == '.nmeta') { // Uses an XML-ish format const switchSearchRegex = new RegExp(`(?<pre><DisplayVersion>)(?<versionString>.*)(?<post></DisplayVersion>)`); const content = await fsp.readFile(file, 'utf8'); const newContent = content.replace(switchSearchRegex, `$1${normalizedVersionString}$3`); await fsp.writeFile(file, newContent); } } } /** * Get the files found in `dir`, recursively, as full paths. Only include * specified extensions. */ async function listFiles(dir, allowedExtensions) { let results = []; const list = await fsp.readdir(dir, { withFileTypes: true }); for (const dirent of list) { const fullPath = joinPaths(dir, dirent.name); if (dirent.isDirectory()) { results = results.concat(await listFiles(fullPath, allowedExtensions)); } else if (allowedExtensions.includes(parsePath(dirent.name).ext)) { results.push(fullPath); } } return results; } //# sourceMappingURL=versioning.js.map