mirror of
https://github.com/misenhower/splatoon3.ink.git
synced 2026-03-21 17:54:13 -05:00
jsonpath v1.2.1 (published Feb 2025) fixed the security vulnerability that prompted the switch to jsonpath-plus. Reverting to the original package simplifies the code by removing the jsonpathQuery/jsonpathApply wrapper functions. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
114 lines
2.7 KiB
JavaScript
114 lines
2.7 KiB
JavaScript
import fs from 'fs/promises';
|
|
import path from 'path';
|
|
import { mkdirp } from '../common/fs.mjs';
|
|
import jsonpath from 'jsonpath';
|
|
import get from 'lodash/get.js';
|
|
import set from 'lodash/set.js';
|
|
import pLimit from 'p-limit';
|
|
|
|
const limit = pLimit(1);
|
|
|
|
function makeArray(value) {
|
|
return Array.isArray(value) ? value : [value];
|
|
}
|
|
|
|
export class LocalizationProcessor {
|
|
outputDirectory = 'dist/data/locale';
|
|
|
|
constructor(locale, rulesets) {
|
|
this.locale = locale;
|
|
this.rulesets = rulesets;
|
|
}
|
|
|
|
get filename() {
|
|
return `${this.outputDirectory}/${this.locale.code}.json`;
|
|
}
|
|
|
|
*rulesetIterations() {
|
|
for (let ruleset of this.rulesets) {
|
|
for (let node of makeArray(ruleset.nodes)) {
|
|
for (let value of makeArray(ruleset.values)) {
|
|
yield {
|
|
key: ruleset.key,
|
|
node,
|
|
id: ruleset.id,
|
|
value,
|
|
};
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
*dataIterations(data) {
|
|
for (let ruleset of this.rulesetIterations()) {
|
|
for (let node of jsonpath.query(data, ruleset.node)) {
|
|
if (!node) continue;
|
|
|
|
let id = get(node, ruleset.id);
|
|
|
|
yield {
|
|
ruleset,
|
|
node,
|
|
id,
|
|
value: get(node, ruleset.value),
|
|
path: `${ruleset.key}.${id}.${ruleset.value}`,
|
|
};
|
|
}
|
|
}
|
|
}
|
|
|
|
async updateLocalizations(data) {
|
|
// We're reading, modifying, and writing back to the same file,
|
|
// so we have to make sure the whole operation is atomic.
|
|
return limit(() => this._updateLocalizations(data));
|
|
}
|
|
|
|
async _updateLocalizations(data) {
|
|
let localizations = await this.readData();
|
|
|
|
for (let { path, value } of this.dataIterations(data)) {
|
|
set(localizations, path, value);
|
|
}
|
|
|
|
await this.writeData(localizations);
|
|
}
|
|
|
|
async hasMissingLocalizations(data) {
|
|
let localizations = await this.readData();
|
|
|
|
for (let { path } of this.dataIterations(data)) {
|
|
if (get(localizations, path) === undefined) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
async writeData(data) {
|
|
// If we're running in debug mode, format the JSON output so it's easier to read
|
|
let debug = !!process.env.DEBUG;
|
|
let space = debug ? 2 : undefined;
|
|
|
|
data = JSON.stringify(data, undefined, space);
|
|
|
|
await mkdirp(path.dirname(this.filename));
|
|
await fs.writeFile(this.filename, data);
|
|
}
|
|
|
|
async readData() {
|
|
try {
|
|
let result = await fs.readFile(this.filename);
|
|
|
|
return JSON.parse(result) || {};
|
|
} catch (e) {
|
|
// File doesn't exist yet or is invalid - return empty object
|
|
if (e.code !== 'ENOENT') {
|
|
console.warn(`Failed to read localization file ${this.filename}:`, e.message);
|
|
}
|
|
}
|
|
|
|
return {};
|
|
}
|
|
}
|