Node: Migrate to ES Modules

This commit is contained in:
Cohee
2024-10-10 22:37:22 +03:00
parent 5a52196331
commit d52b4fbbde
74 changed files with 1291 additions and 1140 deletions

View File

@ -1,13 +1,16 @@
const path = require('path');
const fs = require('fs');
const commandExistsSync = require('command-exists').sync;
const writeFileAtomicSync = require('write-file-atomic').sync;
const _ = require('lodash');
const yauzl = require('yauzl');
const mime = require('mime-types');
const yaml = require('yaml');
const { default: simpleGit } = require('simple-git');
const { Readable } = require('stream');
import * as path from 'node:path';
import * as fs from 'node:fs';
import * as http2 from 'node:http2';
import { Readable } from 'node:stream';
import { createRequire } from 'node:module';
import yaml from 'yaml';
import { sync as commandExistsSync } from 'command-exists';
import { sync as writeFileAtomicSync } from 'write-file-atomic';
import _ from 'lodash';
import yauzl from 'yauzl';
import mime from 'mime-types';
import { default as simpleGit } from 'simple-git';
/**
* Parsed config object.
@ -18,7 +21,7 @@ let CACHED_CONFIG = null;
* Returns the config object from the config.yaml file.
* @returns {object} Config object
*/
function getConfig() {
export function getConfig() {
if (CACHED_CONFIG) {
return CACHED_CONFIG;
}
@ -46,7 +49,7 @@ function getConfig() {
* @param {any} defaultValue - Default value to return if the key is not found
* @returns {any} Value for the given key
*/
function getConfigValue(key, defaultValue = null) {
export function getConfigValue(key, defaultValue = null) {
const config = getConfig();
return _.get(config, key, defaultValue);
}
@ -56,7 +59,7 @@ function getConfigValue(key, defaultValue = null) {
* @param {string} key Key to set
* @param {any} value Value to set
*/
function setConfigValue(key, value) {
export function setConfigValue(key, value) {
// Reset cache so that the next getConfig call will read the updated config file
CACHED_CONFIG = null;
const config = getConfig();
@ -69,7 +72,7 @@ function setConfigValue(key, value) {
* @param {string} auth username:password
* @returns {string} Basic Auth header value
*/
function getBasicAuthHeader(auth) {
export function getBasicAuthHeader(auth) {
const encoded = Buffer.from(`${auth}`).toString('base64');
return `Basic ${encoded}`;
}
@ -79,7 +82,7 @@ function getBasicAuthHeader(auth) {
* Also returns the agent string for the Horde API.
* @returns {Promise<{agent: string, pkgVersion: string, gitRevision: string | null, gitBranch: string | null, commitDate: string | null, isLatest: boolean}>} Version info object
*/
async function getVersion() {
export async function getVersion() {
let pkgVersion = 'UNKNOWN';
let gitRevision = null;
let gitBranch = null;
@ -87,9 +90,10 @@ async function getVersion() {
let isLatest = true;
try {
const require = createRequire(import.meta.url);
const pkgJson = require(path.join(process.cwd(), './package.json'));
pkgVersion = pkgJson.version;
if (!process['pkg'] && commandExistsSync('git')) {
if (commandExistsSync('git')) {
const git = simpleGit();
const cwd = process.cwd();
gitRevision = await git.cwd(cwd).revparse(['--short', 'HEAD']);
@ -117,7 +121,7 @@ async function getVersion() {
* @param {number} ms Milliseconds to wait
* @returns {Promise<void>} Promise that resolves after the given amount of milliseconds
*/
function delay(ms) {
export function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
@ -127,7 +131,7 @@ function delay(ms) {
* @returns {string} Random hex string
* @example getHexString(8) // 'a1b2c3d4'
*/
function getHexString(length) {
export function getHexString(length) {
const chars = '0123456789abcdef';
let result = '';
for (let i = 0; i < length; i++) {
@ -142,7 +146,7 @@ function getHexString(length) {
* @param {string} fileExtension File extension to look for
* @returns {Promise<Buffer|null>} Buffer containing the extracted file. Null if the file was not found.
*/
async function extractFileFromZipBuffer(archiveBuffer, fileExtension) {
export async function extractFileFromZipBuffer(archiveBuffer, fileExtension) {
return await new Promise((resolve, reject) => yauzl.fromBuffer(Buffer.from(archiveBuffer), { lazyEntries: true }, (err, zipfile) => {
if (err) {
reject(err);
@ -181,7 +185,7 @@ async function extractFileFromZipBuffer(archiveBuffer, fileExtension) {
* @param {string} zipFilePath Path to the ZIP archive
* @returns {Promise<[string, Buffer][]>} Array of image buffers
*/
async function getImageBuffers(zipFilePath) {
export async function getImageBuffers(zipFilePath) {
return new Promise((resolve, reject) => {
// Check if the zip file exists
if (!fs.existsSync(zipFilePath)) {
@ -237,7 +241,7 @@ async function getImageBuffers(zipFilePath) {
* @param {any} readableStream Readable stream to read from
* @returns {Promise<Buffer[]>} Array of chunks
*/
async function readAllChunks(readableStream) {
export async function readAllChunks(readableStream) {
return new Promise((resolve, reject) => {
// Consume the readable stream
const chunks = [];
@ -261,7 +265,7 @@ function isObject(item) {
return (item && typeof item === 'object' && !Array.isArray(item));
}
function deepMerge(target, source) {
export function deepMerge(target, source) {
let output = Object.assign({}, target);
if (isObject(target) && isObject(source)) {
Object.keys(source).forEach(key => {
@ -278,7 +282,7 @@ function deepMerge(target, source) {
return output;
}
const color = {
export const color = {
byNum: (mess, fgNum) => {
mess = mess || '';
fgNum = fgNum === undefined ? 31 : fgNum;
@ -298,7 +302,7 @@ const color = {
* Gets a random UUIDv4 string.
* @returns {string} A UUIDv4 string
*/
function uuidv4() {
export function uuidv4() {
if ('crypto' in global && 'randomUUID' in global.crypto) {
return global.crypto.randomUUID();
}
@ -309,7 +313,7 @@ function uuidv4() {
});
}
function humanizedISO8601DateTime(date) {
export function humanizedISO8601DateTime(date) {
let baseDate = typeof date === 'number' ? new Date(date) : new Date();
let humanYear = baseDate.getFullYear();
let humanMonth = (baseDate.getMonth() + 1);
@ -322,7 +326,7 @@ function humanizedISO8601DateTime(date) {
return HumanizedDateTime;
}
function tryParse(str) {
export function tryParse(str) {
try {
return JSON.parse(str);
} catch {
@ -337,7 +341,7 @@ function tryParse(str) {
* @param {string} inputPath The path to be converted.
* @returns The relative URL path from which the client can access the file.
*/
function clientRelativePath(root, inputPath) {
export function clientRelativePath(root, inputPath) {
if (!inputPath.startsWith(root)) {
throw new Error('Input path does not start with the root directory');
}
@ -350,11 +354,11 @@ function clientRelativePath(root, inputPath) {
* @param {string} filename The file name to remove the extension from.
* @returns The file name, sans extension
*/
function removeFileExtension(filename) {
export function removeFileExtension(filename) {
return filename.replace(/\.[^.]+$/, '');
}
function generateTimestamp() {
export function generateTimestamp() {
const now = new Date();
const year = now.getFullYear();
const month = String(now.getMonth() + 1).padStart(2, '0');
@ -371,7 +375,7 @@ function generateTimestamp() {
* @param {string} directory The root directory to remove backups from.
* @param {string} prefix File prefix to filter backups by.
*/
function removeOldBackups(directory, prefix) {
export function removeOldBackups(directory, prefix) {
const MAX_BACKUPS = Number(getConfigValue('numberOfBackups', 50));
let files = fs.readdirSync(directory).filter(f => f.startsWith(prefix));
@ -389,7 +393,7 @@ function removeOldBackups(directory, prefix) {
* @param {'name' | 'date'} sortBy Sort images by name or date
* @returns {string[]} List of image file names
*/
function getImages(directoryPath, sortBy = 'name') {
export function getImages(directoryPath, sortBy = 'name') {
function getSortFunction() {
switch (sortBy) {
case 'name':
@ -415,7 +419,7 @@ function getImages(directoryPath, sortBy = 'name') {
* @param {import('node-fetch').Response} from The Fetch API response to pipe from.
* @param {import('express').Response} to The Express response to pipe to.
*/
function forwardFetchResponse(from, to) {
export function forwardFetchResponse(from, to) {
let statusCode = from.status;
let statusText = from.statusText;
@ -434,6 +438,7 @@ function forwardFetchResponse(from, to) {
to.statusCode = statusCode;
to.statusMessage = statusText;
from.body.pipe(to);
to.socket.on('close', function () {
@ -457,10 +462,9 @@ function forwardFetchResponse(from, to) {
* @param {object} headers Request headers
* @returns {Promise<string>} Response body
*/
function makeHttp2Request(endpoint, method, body, headers) {
export function makeHttp2Request(endpoint, method, body, headers) {
return new Promise((resolve, reject) => {
try {
const http2 = require('http2');
const url = new URL(endpoint);
const client = http2.connect(url.origin);
@ -511,7 +515,7 @@ function makeHttp2Request(endpoint, method, body, headers) {
* @param {string} yamlString YAML-serialized object
* @returns
*/
function mergeObjectWithYaml(obj, yamlString) {
export function mergeObjectWithYaml(obj, yamlString) {
if (!yamlString) {
return;
}
@ -540,7 +544,7 @@ function mergeObjectWithYaml(obj, yamlString) {
* @param {string} yamlString YAML-serialized array
* @returns {void} Nothing
*/
function excludeKeysByYaml(obj, yamlString) {
export function excludeKeysByYaml(obj, yamlString) {
if (!yamlString) {
return;
}
@ -569,14 +573,14 @@ function excludeKeysByYaml(obj, yamlString) {
* @param {string} str Input string
* @returns {string} Trimmed string
*/
function trimV1(str) {
export function trimV1(str) {
return String(str ?? '').replace(/\/$/, '').replace(/\/v1$/, '');
}
/**
* Simple TTL memory cache.
*/
class Cache {
export class Cache {
/**
* @param {number} ttl Time to live in milliseconds
*/
@ -633,7 +637,7 @@ class Cache {
* @param {string} text Text with color formatting
* @returns {string} Text without color formatting
*/
function removeColorFormatting(text) {
export function removeColorFormatting(text) {
// ANSI escape codes for colors are usually in the format \x1b[<codes>m
return text.replace(/\x1b\[\d{1,2}(;\d{1,2})*m/g, '');
}
@ -643,7 +647,7 @@ function removeColorFormatting(text) {
* @param {number} n Number of times to repeat the separator
* @returns {string} Separator string
*/
function getSeparator(n) {
export function getSeparator(n) {
return '='.repeat(n);
}
@ -652,7 +656,7 @@ function getSeparator(n) {
* @param {string} url String to check
* @returns {boolean} If the URL is valid
*/
function isValidUrl(url) {
export function isValidUrl(url) {
try {
new URL(url);
return true;
@ -660,35 +664,3 @@ function isValidUrl(url) {
return false;
}
}
module.exports = {
getConfig,
getConfigValue,
setConfigValue,
getVersion,
getBasicAuthHeader,
extractFileFromZipBuffer,
getImageBuffers,
readAllChunks,
delay,
deepMerge,
color,
uuidv4,
humanizedISO8601DateTime,
tryParse,
clientRelativePath,
removeFileExtension,
generateTimestamp,
removeOldBackups,
getImages,
forwardFetchResponse,
getHexString,
mergeObjectWithYaml,
excludeKeysByYaml,
trimV1,
Cache,
makeHttp2Request,
removeColorFormatting,
getSeparator,
isValidUrl,
};