Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ insert_final_newline = true
max_line_length = 120

; applies only to specified files
[*.{css,scss,less,js,ts,yml,resx}]
[*.{css,scss,less,js,mjs,ts,mts,yml,resx}]
tab_width = 2
indent_size = 2
continuation_indent_size = 2

[*.{css,scss,less,js,ts}]
[*.{css,scss,less,js,mjs,ts,mts}]
quote_type = single

; applies only to JSON files
Expand Down
3 changes: 3 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
# Declare files that will always have LF line endings on checkout.
*.css text eol=lf
*.js text eol=lf
*.mjs text eol=lf
*.ts text eol=lf
*.mts text eol=lf
*.json text eol=lf
*.html text eol=lf
*.php text eol=lf
Expand Down
2 changes: 1 addition & 1 deletion .lintstagedrc.yml
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
"*.{ts,js,json,css,scss,less,html,md,yml}": "prettier --write"
"*.{ts,mts,js,mjs,json,css,scss,less,html,md,yml}": "prettier --write"
"*.cs": ["dotnet format analyzers --include", "dotnet format style --include", "dotnet csharpier format"]
12 changes: 6 additions & 6 deletions scripts/update_font_list.mts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ type FontFamilies = {
};
};

const fonts: FontFamilies = await fetch("https://raw.githubusercontent.com/silnrsi/fonts/main/families.json").then(
const fonts: FontFamilies = await fetch('https://raw.githubusercontent.com/silnrsi/fonts/main/families.json').then(
response => response.json()
);

Expand All @@ -31,7 +31,7 @@ const filesByFamily: {
for (const entry of Object.values(fonts)) {
if (entry.distributable !== true) continue;

const defaultFileName = entry.defaults?.["woff2"] ?? entry.defaults?.["woff"] ?? entry.defaults?.["ttf"];
const defaultFileName = entry.defaults?.['woff2'] ?? entry.defaults?.['woff'] ?? entry.defaults?.['ttf'];
if (defaultFileName == null) continue;

const defaultFileUrl = entry.files[defaultFileName]?.flourl;
Expand All @@ -56,7 +56,7 @@ for (const [family, files] of Object.entries(filesByFamily)) {
bestFileByFamily[family] = matchingFiles[0].fileUrl;
} else if (matchingFiles.length > 1) {
console.warn(
`Multiple Regular files found for ${family}: ${matchingFiles.map(file => file.fileName).join(", ")}`
`Multiple Regular files found for ${family}: ${matchingFiles.map(file => file.fileName).join(', ')}`
);
}
} else if (files.length === 0) {
Expand All @@ -67,7 +67,7 @@ for (const [family, files] of Object.entries(filesByFamily)) {
// Step 3: Get the Noto fonts

const notoFonts = await fetch(
"https://raw.githubusercontent.com/notofonts/notofonts.github.io/refs/heads/main/state.json"
'https://raw.githubusercontent.com/notofonts/notofonts.github.io/refs/heads/main/state.json'
).then(response => response.json());

const notoFontsByFamily: { [family: string]: string } = {};
Expand All @@ -89,7 +89,7 @@ for (const [_group, specification] of Object.entries(notoFonts)) {
matchingFiles.sort((a, b) => a.length - b.length);

if (matchingFiles.length > 1) {
console.warn(`Multiple Regular files found for ${family}: ${matchingFiles.join(", ")}`);
console.warn(`Multiple Regular files found for ${family}: ${matchingFiles.join(', ')}`);
console.warn(`Using ${matchingFiles[0]}`);
}

Expand All @@ -103,4 +103,4 @@ for (const [_group, specification] of Object.entries(notoFonts)) {

const filePath = `${import.meta.dirname}/../src/SIL.XForge.Scripture/fonts.json`;

Deno.writeTextFileSync(filePath, JSON.stringify(bestFileByFamily, null, 2) + "\n");
Deno.writeTextFileSync(filePath, JSON.stringify(bestFileByFamily, null, 2) + '\n');
54 changes: 27 additions & 27 deletions scripts/update_from_crowdin.mts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/usr/bin/env -S deno run --allow-net --allow-read --allow-write --allow-run=unzip --allow-env

const projectId = Deno.env.get("CROWDIN_PROJECT_ID");
const apiKey = Deno.env.get("CROWDIN_API_KEY");
const projectId = Deno.env.get('CROWDIN_PROJECT_ID');
const apiKey = Deno.env.get('CROWDIN_API_KEY');
const projectRoot = Deno.cwd();

function ensureSuccess(response: Response) {
Expand All @@ -11,8 +11,8 @@ function ensureSuccess(response: Response) {
async function saveLatestBuild() {
// Create a new build
const response = await fetch(`https://api.crowdin.com/api/v2/projects/${projectId}/translations/builds`, {
method: "POST",
headers: { Authorization: `Bearer ${apiKey}`, "Content-type": "application/json" },
method: 'POST',
headers: { Authorization: `Bearer ${apiKey}`, 'Content-type': 'application/json' },
body: JSON.stringify({
skipUntranslatedStrings: false,
skipUntranslatedFiles: false,
Expand All @@ -28,7 +28,7 @@ async function saveLatestBuild() {
// Poll the build status
let finished = false;
while (finished === false) {
console.log("Checking build status...");
console.log('Checking build status...');

const buildStatusResponse = await fetch(
`https://api.crowdin.com/api/v2/projects/${projectId}/translations/builds/${buildId}`,
Expand All @@ -38,17 +38,17 @@ async function saveLatestBuild() {
ensureSuccess(buildStatusResponse);
const buildStatusBody = await buildStatusResponse.json();

if (buildStatusBody.data.status === "finished") {
if (buildStatusBody.data.status === 'finished') {
finished = true;
} else if (buildStatusBody.data.status === "inProgress") {
} else if (buildStatusBody.data.status === 'inProgress') {
console.log(`Build status: ${buildStatusBody.data.status}. Waiting for 5 seconds...`);
await new Promise(resolve => setTimeout(resolve, 5000));
} else {
throw new Error(`Unexpected build status: ${buildStatusBody.data.status}`);
}
}

console.log("Build finished!");
console.log('Build finished!');

const buildDownloadResponse = await fetch(
`https://api.crowdin.com/api/v2/projects/${projectId}/translations/builds/${buildId}/download`,
Expand All @@ -60,7 +60,7 @@ async function saveLatestBuild() {
const buildUrl = buildDownloadBody.data.url;

// Download and save the file
console.log("Downloading the build:");
console.log('Downloading the build:');
console.log(buildUrl);
const downloadResponse = await fetch(buildUrl);
ensureSuccess(downloadResponse);
Expand All @@ -70,41 +70,41 @@ async function saveLatestBuild() {
const zipFilePath = `${projectRoot}/translations.zip`;
await Deno.writeFile(zipFilePath, buffer);

console.log("File downloaded and saved to", zipFilePath);
console.log('File downloaded and saved to', zipFilePath);

// Extract the zip file
const command = new Deno.Command("unzip", {
args: ["-o", zipFilePath, "-d", `${projectRoot}/translations`],
stdout: "piped",
stderr: "piped"
const command = new Deno.Command('unzip', {
args: ['-o', zipFilePath, '-d', `${projectRoot}/translations`],
stdout: 'piped',
stderr: 'piped'
});

const { code, stderr } = await command.output();
if (code === 0) {
console.log("Extraction completed successfully.");
console.log('Extraction completed successfully.');
} else {
const errorString = new TextDecoder().decode(stderr);
console.error(errorString);
throw new Error("ZIP extraction failed");
throw new Error('ZIP extraction failed');
}
}

const dirMapping = [
{
source: `${projectRoot}/translations/ClientApp/src/assets/i18n`,
dest: `${projectRoot}/src/SIL.XForge.Scripture/ClientApp/src/assets/i18n`,
localeSeparator: "_"
localeSeparator: '_'
},
{
source: `${projectRoot}/translations/Resources`,
dest: `${projectRoot}/src/SIL.XForge.Scripture/Resources`,
localeSeparator: "-"
localeSeparator: '-'
}
];

const localeRegex = /[\._]((?:[a-z]{2}[_-][A-Z]{2})|(?:[a-z]{3}))\.(?:resx|json)$/;

import locales from "../src/SIL.XForge.Scripture/locales.json" with { type: "json" };
import locales from '../src/SIL.XForge.Scripture/locales.json' with { type: 'json' };

async function copyFiles() {
for (const { source, dest, localeSeparator } of Object.values(dirMapping)) {
Expand All @@ -117,13 +117,13 @@ async function copyFiles() {
if (localeMatch == null) {
throw new Error(`Could not extract locale from file name: ${crowdinFileName}`);
}

const localeCodeInCrowdinFile = localeMatch[1];
const localeCodeWithHyphens = localeCodeInCrowdinFile.replaceAll("_", "-");
const localeCodeWithHyphens = localeCodeInCrowdinFile.replaceAll('_', '-');
const localeObject = locales.find(l => l.tags.includes(localeCodeWithHyphens));
if (localeObject == null) continue;

const localeInOutputFileName = localeObject.tags[0].replaceAll("-", localeSeparator);
const localeInOutputFileName = localeObject.tags[0].replaceAll('-', localeSeparator);
const newFileName = crowdinFileName.replace(localeCodeInCrowdinFile, localeInOutputFileName);

const oldFile = `${source}/${crowdinFileName}`;
Expand All @@ -140,14 +140,14 @@ async function cleanup() {
}

try {
console.log("--- Fetching latest build ---");
console.log('--- Fetching latest build ---');
await saveLatestBuild();
console.log("--- Copying files ---");
console.log('--- Copying files ---');
await copyFiles();
} catch(e) {
} catch (e) {
console.error(e);
} finally {
console.log("--- Cleaning up ---");
console.log('--- Cleaning up ---');
await cleanup();
console.log("--- Done ---");
console.log('--- Done ---');
}
16 changes: 8 additions & 8 deletions scripts/update_language_code_map.mts
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,16 @@
* @returns An array of objects, where each object represents a row in the TSV file.
*/
function parseTsvToObjects(tsv: string, expectedHeaders: readonly string[]): { [key: string]: string }[] {
const lines = tsv.trim().split("\n");
const rows = lines.map(line => line.split("\t"));
const lines = tsv.trim().split('\n');
const rows = lines.map(line => line.split('\t'));

const header = rows[0];
const missingHeaders = expectedHeaders.filter(expectedHeader => !header.includes(expectedHeader));
if (missingHeaders.length > 0) {
throw new Error(
`The file includes the following headers: ${header.join(
", "
)} and is missing the following required headers: ${missingHeaders.join(", ")}`
', '
)} and is missing the following required headers: ${missingHeaders.join(', ')}`
);
}

Expand All @@ -39,10 +39,10 @@ function parseTsvToObjects(tsv: string, expectedHeaders: readonly string[]): { [
return entries;
}

const fileHeaders = ["Id", "Part2b", "Part2t", "Part1", "Scope", "Language_Type", "Ref_Name", "Comment"] as const;
const fileHeaders = ['Id', 'Part2b', 'Part2t', 'Part1', 'Scope', 'Language_Type', 'Ref_Name', 'Comment'] as const;
type FileHeader = (typeof fileHeaders)[number];

const isoFileUrl = "https://iso639-3.sil.org/sites/iso639-3/files/downloads/iso-639-3.tab";
const isoFileUrl = 'https://iso639-3.sil.org/sites/iso639-3/files/downloads/iso-639-3.tab';
const fileContent = await fetch(isoFileUrl).then(response => response.text());
const mainFileEntries = parseTsvToObjects(fileContent, fileHeaders) as { [key in FileHeader]?: string }[];

Expand All @@ -52,7 +52,7 @@ const iso639_1_to_iso639_3: { [code: string]: string } = {};

for (const entry of mainFileEntries) {
if (entry.Id == null || entry.Scope == null || entry.Language_Type == null) {
throw new Error("Entry is Missing required fields: " + JSON.stringify(entry));
throw new Error('Entry is Missing required fields: ' + JSON.stringify(entry));
}

if (entry.Part2b && entry.Part2t && entry.Part2b !== entry.Part2t) {
Expand All @@ -73,4 +73,4 @@ const json = JSON.stringify(results, null, 2);

const filePath = `${import.meta.dirname}/../src/SIL.XForge.Scripture/language_code_mapping.json`;

Deno.writeTextFileSync(filePath, json + "\n");
Deno.writeTextFileSync(filePath, json + '\n');
30 changes: 15 additions & 15 deletions scripts/watch-for-rts-spike.mts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
// 5. Results can be found in APP_OWNER_HOME/.local/share/sf-resource-reports

// @ts-ignore Deno provides this module resolution at runtime.
import { parseArgs } from "jsr:@std/cli/parse-args";
import { parseArgs } from 'jsr:@std/cli/parse-args';

// Help IDE.
declare const Deno: any;
Expand Down Expand Up @@ -92,7 +92,7 @@ class RtsMon {

private async sendSignal(pid: number): Promise<void> {
try {
await this.runCommand("kill", ["-SIGUSR2", String(pid)]);
await this.runCommand('kill', ['-SIGUSR2', String(pid)]);
Program.log(`Sent SIGUSR2 to pid ${pid}`);
} catch (e) {
Program.logError(`Failed to send SIGUSR2 to pid ${pid}: ${(e as Error).message}`);
Expand All @@ -101,7 +101,7 @@ class RtsMon {

private async readRssMib(pid: number): Promise<number | undefined> {
try {
const { code, stdout } = await this.runCommand("ps", ["--quick-pid", String(pid), "--no-headers", "-o", "rss"]);
const { code, stdout } = await this.runCommand('ps', ['--quick-pid', String(pid), '--no-headers', '-o', 'rss']);
if (code !== 0) return undefined;
const text: string = new TextDecoder().decode(stdout).trim();
const kib: number = Number.parseInt(text, 10);
Expand All @@ -114,7 +114,7 @@ class RtsMon {

private async findRealtimeServerPid(): Promise<number | undefined> {
try {
const { code, stdout } = await this.runCommand("pgrep", ["--full", "--", "node .* --port 5002"]);
const { code, stdout } = await this.runCommand('pgrep', ['--full', '--', 'node .* --port 5002']);
if (code !== 0) return undefined;
const text: string = new TextDecoder().decode(stdout).trim();
const lines: string[] = text.split(/\n+/);
Expand All @@ -140,14 +140,14 @@ class RtsMon {

/** Handles running the program. */
class Program {
static programName: string = "rtsmon";
static programName: string = 'rtsmon';

async main(): Promise<void> {
try {
const options: CliOptions = this.parse(Deno.args);
const watcher: RtsMon = new RtsMon(options);
Deno.addSignalListener("SIGINT", () => {
Program.log("Received SIGINT. Exiting.");
Deno.addSignalListener('SIGINT', () => {
Program.log('Received SIGINT. Exiting.');
Deno.exit(0);
});
await watcher.monitor();
Expand All @@ -169,19 +169,19 @@ class Program {

private parse(args: string[]): CliOptions {
const parseOptions = {
boolean: ["help"],
default: { "threshold-mib": 1.5 * 1024, "interval-seconds": 10 }
boolean: ['help'],
default: { 'threshold-mib': 1.5 * 1024, 'interval-seconds': 10 }
};
const parsed = parseArgs(args, parseOptions);
const allowed: Set<string> = new Set(["threshold-mib", "interval-seconds", "help", "_"]);
const allowed: Set<string> = new Set(['threshold-mib', 'interval-seconds', 'help', '_']);
for (const key of Object.keys(parsed)) {
if (allowed.has(key) === false) {
Program.logError(`Unexpected argument: ${key}`);
Deno.exit(1);
}
}
if (parsed._.length > 0) {
Program.logError(`Unexpected arguments: ${parsed._.join(", ")}`);
Program.logError(`Unexpected arguments: ${parsed._.join(', ')}`);
Deno.exit(1);
}
if (parsed.help === true) {
Expand All @@ -190,17 +190,17 @@ class Program {
Deno.exit(0);
}
if (Array.isArray(parsed._) && parsed._.length > 0) {
Program.logError(`Unexpected positional arguments: ${parsed._.join(", ")}`);
Program.logError(`Unexpected positional arguments: ${parsed._.join(', ')}`);
Deno.exit(1);
}

const thresholdMib: number = this.toNumber(parsed["threshold-mib"], "threshold-mib");
const intervalSeconds: number = this.toNumber(parsed["interval-seconds"], "interval-seconds");
const thresholdMib: number = this.toNumber(parsed['threshold-mib'], 'threshold-mib');
const intervalSeconds: number = this.toNumber(parsed['interval-seconds'], 'interval-seconds');
return { thresholdMib, intervalSeconds };
}

private toNumber(value: unknown, name: string): number {
if (typeof value === "number") return value;
if (typeof value === 'number') return value;
throw new Error(`${name} must be a number`);
}
}
Expand Down
Loading
Loading