refactor(rewrite): Reformat using prettier

This commit is contained in:
tecc 2023-09-09 22:38:15 +02:00
parent 9b7579aaae
commit b469865fd7
No known key found for this signature in database
GPG Key ID: 622EEC5BAE5EBD3A
13 changed files with 174 additions and 84 deletions

View File

@ -9,7 +9,7 @@ This is the working directory for the Pronouns.page rewrite.
Run `setup.mjs` for a quick setup. It'll symlink some directories and will work as the equivalent of `make switch` et al.
The reason for making this a script is that make was causing problems for Windows users.
This script does not yet depend on a package manager, so you're free to use whatever you like, provided it'll play nice
This script does not yet depend on a package manager, so you're free to use whatever you like, provided it'll play nice
with the remaining setup.
### Package managers

View File

@ -1,7 +1,3 @@
{
"watch": [
"dist/**",
"package.json",
"tsconfig.json"
]
}
"watch": ["dist/**", "package.json", "tsconfig.json"]
}

View File

@ -1,5 +1,10 @@
import "dotenv/config";
import { identity, parseBool, parseIntOrThrow, toLowerCase } from "@pronounspage/common/util";
import {
identity,
parseBool,
parseIntOrThrow,
toLowerCase,
} from "@pronounspage/common/util";
import * as path from "node:path";
import * as fs from "node:fs";
import type { log } from "#self/log";
@ -11,7 +16,7 @@ export enum Environment {
export interface Config {
environment: Environment;
logLevel?: typeof log["level"];
logLevel?: (typeof log)["level"];
http: {
baseUrl: string;
host: string;
@ -27,17 +32,17 @@ export interface Config {
function envVarOrDefault<T>(
key: string,
parse: (value: string) => T,
defaultValue: T,
defaultValue: T
): T;
function envVarOrDefault<T>(
key: string,
parse: (value: string) => T,
defaultValue?: undefined,
defaultValue?: undefined
): T | undefined;
function envVarOrDefault<T>(
key: string,
parse: (value: string) => T,
defaultValue: T | undefined = undefined,
defaultValue: T | undefined = undefined
): T | undefined {
const value = process.env[key];
return value == null ? defaultValue : parse(String(value));
@ -73,7 +78,7 @@ export function loadConfigFromEnv(): Config {
environment: envVarOrDefault(
"ENVIRONMENT",
parseEnvironment,
Environment.DEVELOPMENT,
Environment.DEVELOPMENT
),
logLevel: envVarOrDefault("LOG_LEVEL", toLowerCase, undefined),
http: {
@ -84,8 +89,16 @@ export function loadConfigFromEnv(): Config {
security: {
secret: envVarNotNull("SECRET", identity),
},
localeDataPath: envVarOrDefault("LOCALE_DATA_PATH", parsePath, "../locales"),
allowUnpublishedLocales: envVarOrDefault("ALLOW_UNPUBLISHED_LOCALES", parseBool, false),
localeDataPath: envVarOrDefault(
"LOCALE_DATA_PATH",
parsePath,
"../locales"
),
allowUnpublishedLocales: envVarOrDefault(
"ALLOW_UNPUBLISHED_LOCALES",
parseBool,
false
),
};
}
@ -99,8 +112,11 @@ export function validateConfig(config: Config): string | undefined {
if (secretLower === "changeme") {
return `You didn't change the secret? The secret is quite literally "${secretLower}"`;
}
if (secretLower === "changemeonprod!" && config.environment === Environment.PRODUCTION) {
return `Hey now, this is production! You have to change the secret on prod! Look, it says so: ${config.security.secret}`
if (
secretLower === "changemeonprod!" &&
config.environment === Environment.PRODUCTION
) {
return `Hey now, this is production! You have to change the secret on prod! Look, it says so: ${config.security.secret}`;
}
return undefined;

View File

@ -19,12 +19,15 @@ type Log = typeof log;
type TypeProvider = TypeBoxTypeProvider;
declare global {
type AppInstance = FastifyInstance<RawServer,
type AppInstance = FastifyInstance<
RawServer,
RequestExpr,
ReplyExpr,
typeof log,
TypeProvider
>;
type AppPluginAsync<Options extends FastifyPluginOptions = Record<never, never>> = FastifyPluginAsync<Options, RawServer, TypeProvider, Log>;
type AppReply = FastifyReply<RawServer, RequestExpr, ReplyExpr>
}
type AppPluginAsync<
Options extends FastifyPluginOptions = Record<never, never>,
> = FastifyPluginAsync<Options, RawServer, TypeProvider, Log>;
type AppReply = FastifyReply<RawServer, RequestExpr, ReplyExpr>;
}

View File

@ -2,7 +2,11 @@ import { startServer } from "#self/server";
import { getConfig, validateConfig } from "#self/config";
import log from "#self/log";
import { exit, pid, ppid } from "node:process";
import { getActiveLocaleDescriptions, loadAllLocales, loadLocaleDescriptions } from "./locales.js";
import {
getActiveLocaleDescriptions,
loadAllLocales,
loadLocaleDescriptions,
} from "./locales.js";
log.info(`Pronouns.page Backend Server - PID: ${pid}, PPID: ${ppid}`);
@ -21,13 +25,17 @@ try {
const localeDescriptions = await loadLocaleDescriptions();
const active = getActiveLocaleDescriptions();
await loadAllLocales();
log.info(`${localeDescriptions.length} locale(s) are registered, of which ${active.length} are active`)
log.info(
`${localeDescriptions.length} locale(s) are registered, of which ${active.length} are active`
);
} catch (e) {
log.error(`Could not load locales: ${e}`);
exit(1);
}
const memUsage = process.memoryUsage();
const memUsagePercent = memUsage.heapUsed / memUsage.heapTotal * 100;
log.info(`Finished preparations - system stats: MEM ${memUsagePercent.toFixed(2)}%`);
const memUsagePercent = (memUsage.heapUsed / memUsage.heapTotal) * 100;
log.info(
`Finished preparations - system stats: MEM ${memUsagePercent.toFixed(2)}%`
);
await startServer();

View File

@ -8,7 +8,12 @@ import * as suml from "@pronounspage/common/suml";
import fsp from "node:fs/promises";
import log from "#self/log";
import * as csv from "csv-parse/sync";
import { capitalizeFirstLetter, compileTemplate, isNotBlank, parseBool } from "@pronounspage/common/util";
import {
capitalizeFirstLetter,
compileTemplate,
isNotBlank,
parseBool,
} from "@pronounspage/common/util";
export interface LocaleDescription {
code: string;
@ -25,10 +30,19 @@ let loadedDescriptions: Array<LocaleDescription>;
let indexedDescriptions: Record<string, LocaleDescription>;
let activeLocaleDescriptions: Array<LocaleDescription> | undefined;
export async function loadLocaleDescriptions(): Promise<Array<LocaleDescription>> {
export async function loadLocaleDescriptions(): Promise<
Array<LocaleDescription>
> {
if (loadedDescriptions == undefined) {
loadedDescriptions = (await import("file://" + path.resolve(getConfig().localeDataPath, "locales.js"))).default;
indexedDescriptions = Object.fromEntries(loadedDescriptions.map((v) => [v.code, v]));
loadedDescriptions = (
await import(
"file://" +
path.resolve(getConfig().localeDataPath, "locales.js")
)
).default;
indexedDescriptions = Object.fromEntries(
loadedDescriptions.map((v) => [v.code, v])
);
activeLocaleDescriptions = undefined;
}
return loadedDescriptions;
@ -70,7 +84,7 @@ export interface Pronoun {
thirdForm?: string;
smallForm?: string;
forms: Record<string, { written: string, pronounced: string }>;
forms: Record<string, { written: string; pronounced: string }>;
}
export interface Example {
@ -93,8 +107,14 @@ export class Locale {
}
private async readFile(parts: Array<string>): Promise<string>;
private async readFile<T>(parts: Array<string>, parser: (value: string) => T): Promise<T>;
private async readFile<T>(pathParts: Array<string>, parser?: (value: string) => T) {
private async readFile<T>(
parts: Array<string>,
parser: (value: string) => T
): Promise<T>;
private async readFile<T>(
pathParts: Array<string>,
parser?: (value: string) => T
) {
// Some may complain about this
const filePath = this.path(pathParts);
try {
@ -106,7 +126,9 @@ export class Locale {
return data;
}
} catch (e) {
const err = new Error(`[${this.code}] Could not load file ${filePath}: ${e}`);
const err = new Error(
`[${this.code}] Could not load file ${filePath}: ${e}`
);
err.cause = e;
throw err;
}
@ -118,11 +140,12 @@ export class Locale {
log.trace(`[${this.code}] Importing file ${filePath} for locale`);
return await import(`file://${filePath}`);
} catch (e) {
const err = new Error(`[${this.code}] Could not import file ${filePath}: ${e}`);
const err = new Error(
`[${this.code}] Could not import file ${filePath}: ${e}`
);
err.cause = e;
throw err;
}
}
private _config: unknown | null = null;
@ -134,22 +157,29 @@ export class Locale {
// private _morphemes: Array<string>;
public async load() {
const tsvParse = (data: string) => csv.parse(data, {
columns: true,
cast: false,
relaxColumnCount: true,
relaxQuotes: true,
delimiter: "\t",
});
const tsvParse = (data: string) =>
csv.parse(data, {
columns: true,
cast: false,
relaxColumnCount: true,
relaxQuotes: true,
delimiter: "\t",
});
this._config = await this.readFile(["config.suml"], suml.parse);
this._translations = await this.readFile(["translations.suml"], suml.parse);
this._translations = await this.readFile(
["translations.suml"],
suml.parse
);
// NOTE(tecc): This currently doesn't work because the morphemes.js files (as well as many others) rely on ESM syntax.
// This is both inconsistent with the locales.js and expectedTranslations.js files,
// and causes Node to throw an error.
// this._morphemes = (await this.importFile("pronouns/morphemes.js")).default;
this._pronouns = [];
this._pronounsByAlias = {};
const pronouns = await this.readFile(["pronouns/pronouns.tsv"], tsvParse);
const pronouns = await this.readFile(
["pronouns/pronouns.tsv"],
tsvParse
);
for (const pronoun of pronouns) {
const partial: Partial<Pronoun> = { forms: {} };
for (const [key, value] of Object.entries<string>(pronoun)) {
@ -191,7 +221,10 @@ export class Locale {
this._pronounsByAlias[key] = i;
}
}
this._examples = await this.readFile(["pronouns/examples.tsv"], tsvParse);
this._examples = await this.readFile(
["pronouns/examples.tsv"],
tsvParse
);
}
public get code() {
@ -220,7 +253,10 @@ export class Locale {
return this._examples;
}
}
export function examplesFor(pronoun: Pronoun, examples: Array<Example>): Array<string> {
export function examplesFor(
pronoun: Pronoun,
examples: Array<Example>
): Array<string> {
const finished = [];
const templating: Record<string, string> = {};
@ -257,7 +293,10 @@ export function getLocale(localeCode: string): Locale | null {
}
let locale = loadedLocales[localeCode];
if (locale == null) {
locale = new Locale(localeCode, path.resolve(getConfig().localeDataPath, localeCode));
locale = new Locale(
localeCode,
path.resolve(getConfig().localeDataPath, localeCode)
);
loadedLocales[localeCode] = locale;
}
return locale;
@ -269,9 +308,15 @@ export async function loadAllLocales() {
for (const description of descriptions) {
const locale = getLocale(description.code);
if (locale) {
promises.push(locale.load().then(() => log.debug(`Successfully loaded locale ${locale.code}`)));
promises.push(
locale
.load()
.then(() =>
log.debug(`Successfully loaded locale ${locale.code}`)
)
);
}
}
await Promise.all(promises);
log.info("All active locales have been loaded into memory");
}
}

View File

@ -17,11 +17,10 @@ const loggerConfigurations = {
},
} satisfies Record<Environment, LoggerOptions>;
let environment, logLevel;
try {
const config = getConfig();
environment = config.environment
environment = config.environment;
logLevel = config.logLevel;
} catch (e) {
environment = Environment.DEVELOPMENT;

View File

@ -9,9 +9,9 @@ export async function startServer() {
logger: log,
ajv: {
customOptions: {
coerceTypes: "array"
}
}
coerceTypes: "array",
},
},
});
let start: Date;

View File

@ -18,7 +18,7 @@ export interface ApiErrorOptions {
export function error<T extends ApiErrorOptions>(options: T) {
const response: Record<string, unknown> = {
code: options.code,
message: options.message
message: options.message,
};
if (options.detail) {
response.detail = options.detail;
@ -26,7 +26,10 @@ export function error<T extends ApiErrorOptions>(options: T) {
return response;
}
export function replyError<T extends ApiErrorOptions>(reply: AppReply, options: T): AppReply {
export function replyError<T extends ApiErrorOptions>(
reply: AppReply,
options: T
): AppReply {
const response = error(options);
return reply.status(options.code).send(response);
}
@ -34,12 +37,12 @@ export function replyError<T extends ApiErrorOptions>(reply: AppReply, options:
const ApiError = {
INVALID_LOCALE: {
code: 404,
message: "Invalid locale"
message: "Invalid locale",
},
UNKNOWN_PRONOUN: {
code: 404,
message: "We aren't aware of any such pronoun. Perhaps there's a typo?"
}
message: "We aren't aware of any such pronoun. Perhaps there's a typo?",
},
} satisfies Record<string, ApiErrorOptions>;
/*
@ -50,12 +53,12 @@ const ApiError = {
export const routes = async function (app: AppInstance) {
const localeSpecific = Type.Object({
locale: Type.String()
locale: Type.String(),
});
function transformPronoun(pronoun: Pronoun, examples: Array<Example>) {
const morphemes: Record<string, string> = {};
const pronunciations: Record<string, string> = {};
const pronunciations: Record<string, string> = {};
for (const [name, form] of Object.entries(pronoun.forms)) {
morphemes[name] = form.written;
pronunciations[name] = form.pronounced;
@ -74,7 +77,7 @@ export const routes = async function (app: AppInstance) {
thirdForm: pronoun.thirdForm ?? null,
smallForm: pronoun.smallForm ?? null,
sourcesInfo: pronoun.source ?? null,
examples: examplesFor(pronoun, examples)
examples: examplesFor(pronoun, examples),
};
}
@ -82,7 +85,7 @@ export const routes = async function (app: AppInstance) {
"/:locale/pronouns",
{
schema: {
params: localeSpecific
params: localeSpecific,
},
},
(request, reply) => {
@ -93,7 +96,10 @@ export const routes = async function (app: AppInstance) {
const obj: Record<string, unknown> = {};
for (const pronoun of locale.pronouns) {
obj[pronoun.keys[0]] = transformPronoun(pronoun, locale.examples);
obj[pronoun.keys[0]] = transformPronoun(
pronoun,
locale.examples
);
}
return obj;
@ -104,10 +110,13 @@ export const routes = async function (app: AppInstance) {
"/:locale/pronouns/*",
{
schema: {
params: Type.Intersect([localeSpecific, Type.Object({ "*": Type.String() })]),
params: Type.Intersect([
localeSpecific,
Type.Object({ "*": Type.String() }),
]),
querystring: Type.Object({
'examples[]': Type.Array(Type.String(), { default: [] })
})
"examples[]": Type.Array(Type.String(), { default: [] }),
}),
},
},
(req, reply) => {
@ -124,7 +133,6 @@ export const routes = async function (app: AppInstance) {
const examples = req.query["examples[]"];
return transformPronoun(found, locale.examples);
}
);

View File

@ -1 +1 @@
declare module "suml";
declare module "suml";

View File

@ -2,7 +2,7 @@ import * as SumlImpl from "suml";
declare module "suml";
console.log(SumlImpl)
console.log(SumlImpl);
const instance = new SumlImpl.default();
/**
@ -18,4 +18,4 @@ export function parse(value: string): unknown {
*/
export function dump(value: unknown): string {
return instance.dump(value);
}
}

View File

@ -4,7 +4,9 @@ export function identity<T>(value: T): T {
// Generally, don't use this function to define commonly used functions.
// Prefer to write a manual implementation.
export function not<Args extends Array<unknown>>(f: (...args: Args) => boolean): (...args: Args) => boolean {
export function not<Args extends Array<unknown>>(
f: (...args: Args) => boolean
): (...args: Args) => boolean {
return (...args: Args) => !f(...args);
}
export function isNotNull<T>(value: T | null | undefined): value is T {
@ -24,12 +26,14 @@ export const isBlank = not(isNotBlank);
// This is unnecessarily typed but I don't really care, it works doesn't it?
export function toLowerCase<S extends string>(value: S): Lowercase<S> {
return value.toLowerCase() as Lowercase<S>
return value.toLowerCase() as Lowercase<S>;
}
export function parseIntOrThrow(value: string, radix?: number): number {
if (radix != null && (radix < 2 || radix > 36)) {
throw new Error(`invalid radix ${radix} - must be between 2 and 36 (inclusive)`);
throw new Error(
`invalid radix ${radix} - must be between 2 and 36 (inclusive)`
);
}
const parsed = parseInt(value, radix);
if (isNaN(parsed)) {
@ -62,7 +66,10 @@ export function capitalizeFirstLetter(value: string) {
return value.charAt(0).toUpperCase() + value.substring(1);
}
export function compileTemplate(template: string, values: Record<string, string>): string {
export function compileTemplate(
template: string,
values: Record<string, string>
): string {
let string = "";
let key = null;
for (let i = 0; i < template.length; i++) {
@ -93,4 +100,4 @@ export function compileTemplate(template: string, values: Record<string, string>
string = string.replace(`{${key}}`, value);
}*/
return string;
}
}

View File

@ -2,32 +2,40 @@ import fsp from "node:fs/promises";
import fs from "node:fs";
import path from "node:path";
console.log("Setting up project")
console.log("Setting up project");
const url = new URL(import.meta.url);
const projectDir = path.dirname(path.resolve(url.pathname.split('/').filter((v) => v && v.length > 0).join(path.sep)));
const projectDir = path.dirname(
path.resolve(
url.pathname
.split("/")
.filter((v) => v && v.length > 0)
.join(path.sep)
)
);
const legacyDir = path.resolve(projectDir, "..");
console.log(`
Project directory: ${projectDir}
Legacy directory: ${legacyDir}
`)
`);
async function symlink(to, from) {
if (!fs.existsSync(from)) {
console.log(` Creating symlink from ${from} to ${to}`)
await fsp.symlink(to, from, "dir")
console.log(` Creating symlink from ${from} to ${to}`);
await fsp.symlink(to, from, "dir");
} else {
console.log(` File ${from} already exists - not symlinking to ${to}`)
console.log(` File ${from} already exists - not symlinking to ${to}`);
}
}
// Create temporary symlinks
// This step may very well be removed in the future.
{
const to = path.resolve(legacyDir, "locale"), from = path.resolve(projectDir, "locales");
const to = path.resolve(legacyDir, "locale"),
from = path.resolve(projectDir, "locales");
await symlink(to, from);
}
console.log(`
Done!`)
Done!`);