UNPKG

sbg-utility

Version:
1,549 lines (1,514 loc) 98.2 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var fs = require('fs-extra'); var path$1 = require('upath'); var url$1 = require('node:url'); var yaml = require('yaml'); var ansiColors = require('ansi-colors'); var path$2 = require('path'); var slugify$1 = require('slugify'); var url = require('url'); var Bluebird = require('bluebird'); var minimatch = require('minimatch'); var os = require('os'); var util = require('util'); var debuglib = require('debug'); var Axios = require('axios'); var cryptolib = require('crypto'); var glob = require('glob'); var fs$1 = require('fs'); var micromatch = require('micromatch'); var hutil = require('hexo-util'); var nunjucks = require('nunjucks'); var stream = require('stream'); var EventEmitter = require('events'); var through2 = require('through2'); var jsdom = require('jsdom'); var PluginError = require('plugin-error'); var async = require('async'); var cheerio = require('cheerio'); var ProgressBar = require('progress'); var request = require('request'); var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null; function _interopNamespaceDefault(e) { var n = Object.create(null); if (e) { Object.keys(e).forEach(function (k) { if (k !== 'default') { var d = Object.getOwnPropertyDescriptor(e, k); Object.defineProperty(n, k, d.get ? d : { enumerable: true, get: function () { return e[k]; } }); } }); } n.default = e; return Object.freeze(n); } var yaml__namespace = /*#__PURE__*/_interopNamespaceDefault(yaml); var cryptolib__namespace = /*#__PURE__*/_interopNamespaceDefault(cryptolib); var glob__namespace = /*#__PURE__*/_interopNamespaceDefault(glob); /** * pick random items from array * @param items * @returns */ function array_random(items) { return items[Math.floor(Math.random() * items.length)]; } /** * unique array * * array of string,number * * array of object by object key * @param arr * @param field key name (for array of object) * @returns * * @example * arrayOfObjUniq({p:'x',n:'x'},{p:'23',n:'x'},{p:'x',n:'5g'}, 'p'); // [{p:'x',n:'x'},{p:'23',n:'x'}] * * @link https://stackoverflow.com/a/67322087/6404439 */ function array_unique(arr, field) { if (Array.isArray(arr)) { if (typeof field !== 'string') { arr = arr.filter(function (x, i, a) { return a.indexOf(x) === i; }); } else { arr = arr.filter((a, i) => arr.findIndex((s) => a[field] === s[field]) === i); } return arr.filter((item) => { if (typeof item === 'string') return item.trim().length > 0; if (Array.isArray(item)) return item.length > 0; return true; }); } else { throw new Error('array param must be instance of ARRAY'); } } /** * Remove empties from array * @param arr * @returns */ function array_remove_empty(arr) { return arr.filter((target) => { if (typeof target === 'string') return target.trim().length > 0; if (Array.isArray(target)) return target.length > 0; if (typeof target === 'object') return Object.keys(target).length > 0; return true; }); } /** * unique array of object by object key * @param arr * @param field key name * @returns * @see {@link https://stackoverflow.com/a/67322087/6404439} * @example * const arrobj = [{p:'x',n:'x'},{p:'23',n:'x'},{p:'x',n:'5g'}], * arrayOfObjUniq(arrobj, 'p'); // [{p:'x',n:'x'},{p:'23',n:'x'}] */ function arrayOfObjUniq(arr, field) { //console.log(array); if (!Array.isArray(arr)) { throw new Error('array param must be instance of ARRAY'); } return arr.filter((a, i) => arr.findIndex((s) => a[field] === s[field]) === i); } /** * array shuffler * @param items * @returns */ function array_shuffle(items) { return items.sort(() => Math.random() - 0.5); } /** * generate random number * @see {@link https://stackoverflow.com/a/65638217/6404439} * @param n * @returns */ const rand = (n) => 0 | (Math.random() * n); /** * fast shuffle array (internal) * @see {@link https://stackoverflow.com/a/65638217/6404439} * @param t */ function swap(t, i, j) { const q = t[i]; t[i] = t[j]; t[j] = q; return t; } /** * fast shuffle array using swap method * @see {@link https://stackoverflow.com/a/65638217/6404439} * @param t */ function array_shuffle_swap(t) { let last = t.length; let n; while (last > 0) { n = rand(last); swap(t, n, --last); } } /** * flattern array * @param arr * @returns */ function array_flatten(arr, depth) { if (typeof depth === 'number') return arr.flat(depth); return arr.reduce((acc, cur) => acc.concat(Array.isArray(cur) ? array_flatten(cur) : cur), []); } function isStream(stream, {checkOpen = true} = {}) { return stream !== null && typeof stream === 'object' && (stream.writable || stream.readable || !checkOpen || (stream.writable === undefined && stream.readable === undefined)) && typeof stream.pipe === 'function'; } function isWritableStream(stream, {checkOpen = true} = {}) { return isStream(stream, {checkOpen}) && (stream.writable || !checkOpen) && typeof stream.write === 'function' && typeof stream.end === 'function' && typeof stream.writable === 'boolean' && typeof stream.writableObjectMode === 'boolean' && typeof stream.destroy === 'function' && typeof stream.destroyed === 'boolean'; } function isReadableStream(stream, {checkOpen = true} = {}) { return isStream(stream, {checkOpen}) && (stream.readable || !checkOpen) && typeof stream.read === 'function' && typeof stream.readable === 'boolean' && typeof stream.readableObjectMode === 'boolean' && typeof stream.destroy === 'function' && typeof stream.destroyed === 'boolean'; } /** * copy file/folder recursively * @param src * @param dest * @param options * @returns */ function copyPath(src, dest, options) { if (!fs.existsSync(path$2.dirname(dest))) { fs.mkdirSync(path$2.dirname(dest), { recursive: true }); } return fs.copy(src, dest, Object.assign({ overwrite: true, dereference: true, errorOnExist: false }, options || {})); } /** * delete folder/file async * @param path path of file/folder * @param throws enable throwable * @returns */ function del(path, throws) { return new Bluebird((resolve, reject) => { const rmOpt = { recursive: true, force: true }; if (fs.existsSync(path)) { if (!throws) { // always success fs.rm(path, rmOpt).then(resolve).catch(resolve); } else { fs.rm(path, rmOpt).then(resolve).catch(reject); } /*if (statSync(path).isDirectory()) { rmdir(path, { maxRetries: 10 }).then(resolve).catch(resolve); } else { rm(path, rmOpt).then(resolve).catch(resolve); }*/ } else { if (!throws) { // always success resolve(new Error(path + ' not found')); } else { reject(new Error(path + ' not found')); } } }); } /** * read directory recursive callback * @param dir * @returns */ const readDir = function (dir, done) { let results = []; fs.readdir(dir, function (err, list) { if (err) return done(err); let i = 0; (function next() { let file = list[i++]; if (!file) return done(undefined, results); file = path$1.resolve(dir, file); fs.stat(file, function (err, stat) { if (!err && stat && stat.isDirectory()) { readDir(file, function (err, res) { if (!err && Array.isArray(res)) results = results.concat(res); next(); }); } else { results.push(file); next(); } }); })(); }); }; /** * read directory recursive async * @param dir * @returns */ const readDirAsync = function (dir) { return new Bluebird((resolve, reject) => { readDir(dir, function (err, files) { if (err) reject(err); resolve(files); }); }); }; /** * empty dir with filters * @param dir * @param param1 */ function emptyDir(dir, { ignore }) { readDir(dir, function (err, fileList) { if (err) throw err; // const filterFn = (pattern: string) => minimatch.filter(pattern, { matchBase: true }); const filter = fileList?.filter((file) => { for (let i = 0; i < ignore.length; i++) { const pattern = ignore[i]; if (typeof pattern === 'string') { const match = minimatch.minimatch(file, pattern, { matchBase: true, dot: true }); // console.log(file.replace(dir, ''), pattern, match); // filter file if matched if (match) return false; } else { // filter file if matched if (pattern.test(file)) return false; } } return true; }) || []; return filter; }); } //const _ex = path.join(__dirname, '../..'); //emptyDir(_ex, { ignore: ['**/config/**'] }); const readdir = util.promisify(fs.readdir); const isWindows = os.platform() === 'win32'; const delimiter = isWindows ? '\\' : '/'; function trueCasePathNew(opt) { const defaults = { sync: false }; const trueCase = _trueCasePath(Object.assign(defaults, opt || {})); return (filePath, basePath, cbOpt) => { if (filePath.length > 3) { let result; let bPath = undefined; let callbackOpt = Object.assign({ unix: false }, cbOpt || {}); if (typeof basePath === 'string') { bPath = basePath; } else if (typeof basePath === 'object') { callbackOpt = Object.assign({ unix: false }, basePath || {}); } // Join basePath if provided let fPath = filePath; if (typeof bPath === 'string') fPath = path$2.join(bPath, filePath); // Normalize the path before any checks fPath = path$2.normalize(fPath); // Check if the file exists if (fs.existsSync(fPath)) { result = trueCase(filePath, bPath); } else { // Only capitalize the drive letter, don't touch the rest of the path result = fPath.replace(/^([a-zA-Z]):/, (match, driveLetter) => { return driveLetter.toUpperCase() + ':'; }); } // Handle Unix conversion if requested if (callbackOpt?.unix) { return path$1.toUnix(result); } else { return result; } } else { // Error handling for invalid paths if (typeof basePath === 'string') { if (opt?.debug) console.error('Failed to convert case-path of', { basePath, filePath }); return path$2.join(basePath, filePath); } else { if (opt?.debug) console.error('Failed to convert case-path of', { filePath }); return filePath; } } }; } const trueCasePathSync = trueCasePathNew({ sync: true }); const trueCasePath = trueCasePathNew({ sync: false }); function getRelevantFilePathSegments(filePath) { return filePath.split(delimiter).filter((s) => s !== ''); } function escapeString(str) { return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); } function matchCaseInsensitive(fileOrDirectory, directoryContents, filePath) { const caseInsensitiveRegex = new RegExp(`^${escapeString(fileOrDirectory)}$`, 'i'); for (const file of directoryContents) { if (caseInsensitiveRegex.test(file)) return file; } throw new Error(`[true-case-path]: Called with ${filePath}, but no matching file exists`); } function _trueCasePath({ sync }) { return (filePath, basePath) => { if (!fs.existsSync(filePath)) return basePath ? path$2.join(basePath, filePath) : filePath; if (basePath) { if (!path$2.isAbsolute(basePath)) { throw new Error(`[true-case-path]: basePath argument must be absolute. Received "${basePath}"`); } basePath = path$2.normalize(basePath); } filePath = path$2.normalize(filePath); const segments = getRelevantFilePathSegments(filePath); if (path$2.isAbsolute(filePath)) { if (basePath) { throw new Error('[true-case-path]: filePath must be relative when used with basePath'); } basePath = isWindows ? segments.shift()?.toUpperCase() // drive letter : ''; } else if (!basePath) { basePath = process.cwd(); } return sync ? iterateSync(basePath, filePath, segments) : iterateAsync(basePath, filePath, segments); }; } function iterateSync(basePath, filePath, segments) { return segments.reduce((realPath, fileOrDirectory) => realPath + delimiter + matchCaseInsensitive(fileOrDirectory, fs.readdirSync(realPath + delimiter), filePath), basePath); } async function iterateAsync(basePath, filePath, segments) { return await segments.reduce(async (realPathPromise, fileOrDirectory) => (await realPathPromise) + delimiter + matchCaseInsensitive(fileOrDirectory, await readdir((await realPathPromise) + delimiter), filePath), basePath); } const __filename$8 = url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href))); const __dirname$6 = path$1.dirname(__filename$8); function getAppRootDir() { let currentDir = __dirname$6; while (!fs.existsSync(path$1.join(currentDir, 'package.json'))) { currentDir = path$1.join(currentDir, '..'); } return path$1.toUnix(trueCasePathSync(currentDir)); } /** * function to encode file data to base64 encoded string * @param file path file * @returns */ function image_base64_encode(file) { // read binary data const bitmap = fs.readFileSync(file); // convert binary data to base64 encoded string return bitmap.toString('base64'); } function fixDriveLetter(filePath) { // Handle Windows-style paths with lowercase drive letters if (/^[a-z]:/.test(filePath)) { // Ensure first letter is uppercase return filePath.charAt(0).toUpperCase() + filePath.slice(1); } return filePath; } /** * UNIX join path with true-case-path * @description normalize path and make drive letter uppercase * @param str * @returns Unix Style Path */ function normalizePath(...str) { const join = path$2.join(...str); if (fs.existsSync(join)) { const casePath = trueCasePathSync(join); return fixDriveLetter(casePath); } else { return fixDriveLetter(join); } } /** * UNIX join path with true-case-path * @description normalize path and make drive letter uppercase * @param str * @returns Unix Style Path */ function normalizePathUnix(...str) { const join = path$1.join(...str); if (fs.existsSync(join)) { const casePath = trueCasePathSync(join); return fixDriveLetter(path$1.toUnix(casePath)); } else { return fixDriveLetter(join); } } /** * remove base path * @param target path to remove * @param toRemove cwd */ function removeCwd(target, toRemove) { return normalizePath(target).replace(toRemove, ''); } /** * UNIX join path with auto create dirname when not exists * @param path * @returns */ function joinSolve(...paths) { const merge = normalizePath(...paths); if (!fs.existsSync(path$1.dirname(merge))) { fs.mkdirSync(path$1.dirname(merge), { recursive: true }); } return merge; } /** * create writestream (auto create dirname) * @param dest * @param options * @returns */ function createWriteStream(dest, options) { if (!fs.existsSync(path$1.dirname(dest))) fs.mkdirSync(path$1.dirname(dest)); return fs.createWriteStream(dest, options); } /** * sync write to file recursively (auto create dirname) * @param file * @param content * @param opt */ function writefile(file, content, opt = {}) { // create dirname when not exist if (!fs.existsSync(path$1.dirname(file))) fs.mkdirSync(path$1.dirname(file), Object.assign({ recursive: true }, opt)); const result = { file, append: false }; // transform object if (typeof content === 'object') { content = JSON.stringify(content, null, 2); } if (opt.append) { result.append = true; fs.appendFileSync(file, content); } else { fs.writeFileSync(file, content); } if (opt.async) return Promise.resolve(result); return result; } // filemanager /** * is non-markdown file * @param path * @returns */ const isAsset = (path) => /.(js|css|scss|njk|ejs|png|jpe?g|gif|svg|webp|json|html|txt)$/.test(String(path)); /** * is markdown file * @param path * @returns */ const isMarkdown = (path) => /.(md)$/i.test(String(path)); // import { areWeTestingWithJest } from './jest'; // import * as configs from '../config'; const __filename$7 = url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href))); path$2.dirname(__filename$7); const FOLDER = path$1.join(process.cwd(), 'tmp/logs'); // let cwd = process.cwd(); // disable console.log on jest // if (areWeTestingWithJest()) { // const log = console.log; // console.log = function (...args: any[]) { // if (typeof configs.getConfig === 'function') { // const cfg = configs.getConfig(); // FOLDER = upath.join(cfg.cwd, 'tmp/logs/'); // cwd = cfg.cwd; // } // const stack = (new Error('').stack || '').split(/\r?\n/gm); // let msg = (stack || [])[3] || ''; // if (msg.includes(__filename)) { // msg = (stack || [])[2] || ''; // } // // log(stack[2], stack[4]); // const filename = slugify(upath.toUnix(msg).replace(upath.toUnix(cwd), ''), { // lower: true, // trim: true, // replacement: '-', // strict: true // }); // const header = `\n\n ${new Date()} \n\n`; // const write = writefile(upath.join(FOLDER, filename + '.log'), header + args.join('\n\n'), { append: true }); // log(write.file); // }; // } //const _log = typeof hexo === 'undefined' ? console : Object.assign({ log: console.log }, hexo.log); const _log$1 = console; /** * @example * const console = Logger * Logger.log('hello world'); // should be written in <temp folder>/logs/[trace-name].log */ class Logger { static log(...args) { _log$1.log(...args); this.tracer(...args); } static info(...args) { _log$1.info.apply(null, args); this.tracer(...args); } static error(...args) { _log$1.error.apply(null, args); this.tracer(...args); } static tracer(...args) { const error = new Error(); const split = error.stack?.split(/\r?\n/gm).map((str) => { const split2 = str.trim().split(' '); return { name: split2[1], path: split2[2]?.replace(/\\+/gm, '/').replace(/^\(/, '').replace(/\)$/, '') //trace: error.stack }; }); if (split) { split.splice(0, 3); let logfile; let templ; // anonymous caller if (typeof split[0].path === 'undefined' && split[1].path.includes('anonymous')) { const id = split[1].name; const filePath = split[0].name; const base = path$1.basename(filePath.split(':')[0].length === 1 ? filePath.split(':')[0] + ':' + filePath.split(':')[1] : filePath.split(':')[0]); logfile = path$1.join(FOLDER, slugify$1(id, { trim: true }) + '-' + slugify$1(base, { trim: true }) + '.log'); if (!fs.existsSync(logfile)) { writefile(logfile, ''); } templ = `${'='.repeat(20)}\nfile: ${filePath}\ndate: ${new Date()}\n${'='.repeat(20)}\n\n`; args.forEach((o) => { if (o === null) o = 'null'; if (typeof o === 'object') { try { o = JSON.stringify(o, null, 2); } catch { // } } templ += String(o) + '\n\n'; }); fs.appendFileSync(logfile, templ); } // Logger.log(split); } } } /** * Chainable function runner. * * @param schedule - An array of function objects, each containing a callback and optional `opt` properties for before and after functions. * @example * ```ts * chain([ * { * callback: () => 'stream process eg: gulp', * opt: { * before: () => 'run before callback called', * after: () => 'run after callback called' * } * }, * { * callback: () => 'promise process' * }, * { * callback: () => 'synchronous function' * } * ]); * ``` */ async function chain(schedule) { // NodeJS.ReadWriteStream | Promise<any> const run = function (instance) { return new Promise((resolve) => { const logname = ansiColors.blueBright('chain') + '.' + ansiColors.yellowBright('run'); if (instance.opt?.before) { instance.opt.before(); } const obj = instance.callback.call && instance.callback.call(null); // Logger.log( // `is readable stream ${isReadableStream(obj)}`, // `is writable stream ${isWritableStream(obj)}`, // `is promise ${isPromise(obj)}`, // `is stream ${isStream(obj)}`, // { keys: Object.keys(obj) } // ); if (isReadableStream(obj)) { Logger.log('readable stream'); return obj.once('end', async () => { if (instance.opt?.after) { await instance.opt.after(); return resolve(this); } else { return resolve(this); } }); } else if (isWritableStream(obj)) { Logger.log('writable stream'); return obj.once('finish', async () => { if (instance.opt?.after) { await instance.opt.after(); return resolve(this); } else { return resolve(this); } }); } else if (isStream(obj)) { // gulp instance / readwrite stream return obj.once('end', async () => { if (instance.opt?.after) { await instance.opt.after(); return resolve(this); } else { return resolve(this); } }); } else if (isPromise(obj)) { //Logger.log('promises'); return obj.then(async () => { if (instance.opt?.after) { await instance.opt.after(); return resolve(this); } else { return resolve(this); } }); } else { if (typeof instance.callback !== 'function') { Logger.log(logname, 'cannot determine method instances'); } } resolve.bind(this)(chain.bind(this)); }); }; while (schedule.length > 0) { const instance = schedule.shift(); if (typeof instance !== 'undefined') await run(instance); } } /** * check object is Promises * @param p * @returns */ function isPromise(p) { return ((p && String(Object.prototype.toString.call(p)).toLowerCase() === '[object promise]') || // check ES6 Promises (p && typeof p.constructor === 'function' && Function.prototype.toString.call(p.constructor).replace(/\(.*\)/, '()') === Function.prototype.toString .call(/*native object*/ Function) .replace('Function', 'Promise') // replacing Identifier .replace(/\(.*\)/, '()')) || // removing possible FormalParameterList // my own experiment (p && String(Function.prototype.toString.call(p.constructor)).startsWith('function Promise('))); } /** * convert hours,min,sec to milliseconds * @param hrs * @param min * @param sec * @returns */ const toMilliseconds = (hrs, min, sec) => (hrs * 60 * 60 + min * 60 + sec) * 1000; /** * Creates a debug logger with the specified name. * * @param name - The name of the debug logger. This will be used as a prefix for log messages. * @returns A debug logger function that can be used to log messages. */ function debug(name) { return debuglib(name); } /** * Creates a debug logger with the default name `sbg`. * * This function allows for an optional subname to further specify the debug logger. * * @param subname - An optional suffix to append to the default logger name. * @returns A debug logger function with the name `sbg` or `sbg-{subname}` if provided. */ function sbgDebug(subname) { if (subname) return debuglib(`sbg-${subname}`); return debuglib('sbg'); } const __filename$6 = url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href))); const __dirname$5 = path$1.dirname(__filename$6); /** * MD5 file synchronously * @param path */ function md5FileSync(path) { if (!path || path.length === 0) return undefined; let fileBuffer = Buffer.from(path); if (fs.existsSync(path)) { if (fs.statSync(path).isFile()) fileBuffer = fs.readFileSync(path); } const hashSum = cryptolib.createHash('md5'); // sha256 hashSum.update(fileBuffer); return hashSum.digest('hex'); } /** * PHP MD5 Equivalent * @param data */ function md5(data) { if (!data || data.length === 0) return undefined; return cryptolib.createHash('md5').update(data).digest('hex'); } /** * convert file to hash * @param alogarithm * @param path * @param encoding * @returns */ function file_to_hash(alogarithm, path, encoding = 'hex') { return new Promise((resolve, reject) => { const hash = cryptolib.createHash(alogarithm); const rs = fs.createReadStream(path); rs.on('error', reject); rs.on('data', (chunk) => hash.update(chunk)); rs.on('end', () => resolve(hash.digest(encoding))); }); } /** * convert data to hash (async) * @param alogarithm * @param data * @param encoding * @returns */ function data_to_hash(alogarithm = 'sha1', data, encoding = 'hex') { return new Promise((resolve, reject) => { try { resolve(data_to_hash_sync(alogarithm, data, encoding)); } catch (e) { reject(e); } }); } /** * convert data to hash (sync) * @param alogarithm * @param data * @param encoding * @returns */ function data_to_hash_sync(alogarithm = 'sha1', data, encoding = 'hex') { return cryptolib.createHash(alogarithm).update(data).digest(encoding); } /** * get hashes from folder * @param alogarithm * @param folder * @param options * @returns */ async function folder_to_hash(alogarithm, folder, options) { return new Promise((resolvePromise, rejectPromise) => { options = Object.assign({ encoding: 'hex', ignored: [], pattern: '' }, options || {}); if (folder.startsWith('file:')) folder = folder.replace('file:', ''); // fix non exist if (!fs.existsSync(folder)) folder = path$1.join(__dirname$5, folder); // run only if exist if (fs.existsSync(folder)) { glob__namespace .glob(options.pattern || '**/*', { cwd: folder, ignore: (options.ignored || [ '**/tmp/**', '**/build/**', '**/.cache/**', '**/dist/**', '**/.vscode/**', '**/coverage/**', '**/release/**', '**/bin/**', '**/*.json' ]).concat('**/.git*/**', '**/node_modules/**'), dot: true, noext: true }) .then((matches) => { const filesWithHash = {}; for (let i = 0; i < matches.length; i++) { const item = matches[i]; const fullPath = path$1.join(folder, item); const statInfo = fs.statSync(fullPath); if (statInfo.isFile()) { const fileInfo = `${fullPath}:${statInfo.size}:${statInfo.mtimeMs}`; const hash = data_to_hash_sync(alogarithm, fileInfo, options.encoding); filesWithHash[fullPath] = hash; } } resolvePromise({ filesWithHash, hash: data_to_hash_sync(alogarithm, Object.values(filesWithHash).join(''), options.encoding) }); }) .catch(rejectPromise); } else { console.log(folder + ' not found'); } }); } /** * convert data to hash * @param alogarithm * @param url * @param encoding * @returns */ async function url_to_hash(alogarithm = 'sha1', url, encoding = 'hex') { return new Promise((resolve, reject) => { let outputLocationPath = path$1.join(__dirname$5, 'node_modules/.cache/postinstall', path$1.basename(url)); // remove slashes when url ends with slash if (!path$1.basename(url).endsWith('/')) { outputLocationPath = outputLocationPath.replace(/\/$/, ''); } // add extension when dot not exist if (!path$1.basename(url).includes('.')) { outputLocationPath += '.tgz'; } if (!fs.existsSync(path$1.dirname(outputLocationPath))) { fs.mkdirSync(path$1.dirname(outputLocationPath), { recursive: true }); } const writer = fs.createWriteStream(outputLocationPath, { flags: 'w' }); Axios(url, { responseType: 'stream' }).then((response) => { response.data.pipe(writer); let error; writer.on('error', (err) => { error = err; writer.close(); reject(err); }); writer.on('close', async () => { if (!error) { // console.log('package downloaded', outputLocationPath.replace(__dirname, '')); file_to_hash(alogarithm, outputLocationPath, encoding).then((checksum) => { resolve(checksum); }); } }); }); }); } /** * check development NODE_ENV * @returns */ function isdev() { return /dev/i.test(process.env.NODE_ENV || ''); } /** * Check if current runner is JEST * * source: {@link https://stackoverflow.com/a/52231746} * @returns */ function areWeTestingWithJest() { return process.env.JEST_WORKER_ID !== undefined; } /* eslint no-fallthrough: ["error", { "commentPattern": "break[\\s\\w]*omitted" }] */ const __filename$5 = url$1.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href))); const { parse: $parse, stringify: $stringify } = JSON; const { keys } = Object; /** * Generates an SHA-1 hash from the provided data. * @param data - The input data to hash. * @returns The SHA-1 hash of the data. */ function sha1(data) { return cryptolib__namespace.createHash('sha1').update(data.toString(), 'binary').digest('hex'); } /** * Retrieves the current stack trace and creates an error log file. * @returns An object containing the stack trace and the file path. */ function getStack() { const stack = new Error('get caller').stack ?.split(/\r?\n/gim) .filter((str) => /(dist|src)/i.test(str) && !str.includes(__filename$5)) ?? []; const folder = path$2.join(process.cwd(), 'tmp/error/json-serializer'); // Create folder if it does not exist if (!fs.existsSync(folder)) fs.mkdirSync(folder, { recursive: true }); const file = path$2.join(folder, `${sha1(stack[1])}.log`); return { stack, file }; } const Primitive = String; // It could be Number const primitive = 'string'; // It could be 'number' const ignore = {}; const object = 'object'; const noop$2 = (_, value) => value; const primitives = (value) => (value instanceof Primitive ? Primitive(value) : value); const Primitives = (_, value) => (typeof value === primitive ? new Primitive(value) : value); /** * Recursively revives circular references in a parsed object. */ const revive = (input, parsed, output, $) => { const lazy = []; for (let ke = keys(output), { length } = ke, y = 0; y < length; y++) { const k = ke[y]; const value = output[k]; if (value instanceof Primitive) { const tmp = input[value]; if (typeof tmp === object && !parsed.has(tmp)) { parsed.add(tmp); output[k] = ignore; lazy.push({ k, a: [input, parsed, tmp, $] }); } else output[k] = $.call(output, k, tmp); } else if (output[k] !== ignore) output[k] = $.call(output, k, value); } for (let { length } = lazy, i = 0; i < length; i++) { const { k, a } = lazy[i]; // eslint-disable-next-line prefer-spread output[k] = $.call(output, k, revive.apply(null, a)); } return output; }; /** * Adds a value to a set of known values and returns its index. */ const set = (known, input, value) => { const index = Primitive(input.push(value) - 1); known.set(value, index); return index; }; /** * Parses a JSON string with support for circular references. * @param text - The JSON string to parse. * @param reviver - Optional function to transform the parsed values. * @returns The parsed object. */ const parse = (text, reviver) => { try { const input = $parse(text, Primitives).map(primitives); const value = input[0]; const $ = reviver || noop$2; const tmp = typeof value === object && value ? revive(input, new Set(), value, $) : value; return $.call({ '': tmp }, '', tmp); } catch (e) { const { stack, file } = getStack(); fs.writeFileSync(file, `${stack.join('\n')}\n\n${text}`); console.log('fail parse ' + file + ' ' + e.message); process.exit(1); } }; /** * Stringifies an object into JSON with support for circular references. * @param value - The object to stringify. * @param replacer - Optional function to transform the values before stringifying. * @param space - Optional number or string to use as a white space in the output. * @returns The JSON string representation of the object. */ const stringify = (value, replacer, space) => { const $ = replacer && typeof replacer === 'object' ? (k, v) => (k === '' || replacer.indexOf(k) !== -1 ? v : void 0) : replacer || noop$2; const known = new Map(); const input = []; const output = []; let i = +set(known, input, $.call({ '': value }, '', value)); let firstRun = !i; while (i < input.length) { firstRun = true; output[i] = $stringify(input[i++], replace, space); } return '[' + output.join(',') + ']'; function replace(key, value) { if (firstRun) { firstRun = false; return value; } const after = $.call(this, key, value); switch (typeof after) { case object: if (after === null) return after; // break omitted case primitive: return known.get(after) || set(known, input, after); } return after; } }; /** * Converts an object with circular references to JSON. * @param any - The object to convert. * @returns The JSON representation of the object. */ const toJSON = (any) => $parse(stringify(any)); /** * Parses a circular object from JSON. * @param any - The JSON string to parse. * @returns The parsed object. */ const fromJSON = (any) => parse($stringify(any)); JSON.stringifyWithCircularRefs = toJSON; /** * transform any object to json. Suppress `TypeError: Converting circular structure to JSON` * @param data * @returns */ function jsonStringifyWithCircularRefs(data) { return stringify(data); } /** * parse json stringified with circular refs */ function jsonParseWithCircularRefs(data) { return parse(data); } /** SCHEDULER JOB **/ /*** Postpone executing functions ***/ //const _log = typeof hexo !== 'undefined' ? hexo.log : console; const _log = console; const logname = ansiColors.magentaBright('[scheduler]'); const fns = {}; let triggered; /** * Bind functions to exit handler * @param key * @param fn */ function bindProcessExit(key, fn) { fns[key] = fn; // trigger once if (!triggered) { triggered = true; triggerProcess(); } } /** * Handler function on process exit * @param options * @param exitCode */ async function exitHandler(options, exitCode = 0) { const funcs = []; for (const key in fns) { if (Object.prototype.hasOwnProperty.call(fns, key)) { funcs.push({ callback: fns[key], opt: { before: () => { if (scheduler.verbose) _log.info(logname, `executing function key: ${key}`); } } }); } } if (options?.cleanup) chain(funcs); if (options?.cleanup && scheduler.verbose) _log.info(logname, `clean exit(${exitCode})`); if (options?.exit) process.exit(); } /** * Trigger Process Bindings */ function triggerProcess() { // before exit //process.on('beforeExit', exitHandler.bind(undefined, { exit: true })); //do something when app is closing process.on('exit', exitHandler.bind(undefined, { cleanup: true })); // process.on('disconnect', exitHandler.bind(undefined, { exit: true })); //catches ctrl+c event process.on('SIGINT', exitHandler.bind(undefined, { exit: true })); //process.on('SIGKILL', exitHandler.bind(undefined, { exit: true })); // catches "kill pid" (for example: nodemon restart) process.on('SIGUSR1', exitHandler.bind(undefined, { exit: true })); process.on('SIGUSR2', exitHandler.bind(undefined, { exit: true })); //catches uncaught exceptions process.on('uncaughtException', exitHandler.bind(undefined, { exit: true })); } ///// task queue manager const functions = {}; /** * @example * ```js * bindProcessExit("scheduler_on_exit", function () { * _log.info("executing scheduled functions"); * scheduler.executeAll(); * }); * ``` * or * ```js * scheduler.register(); * ``` */ class scheduler { static verbose = true; constructor() { if (!scheduler.registered) scheduler.register(); } static registered = false; /** * Register scheduler to process system */ static register() { if (scheduler.registered) return; scheduler.registered = true; bindProcessExit('scheduler_on_exit', function () { scheduler.executeAll(); }); } /** * Add function with key to list * @param key existing key (duplicate) will be overwritten * @param value */ static add(key, value) { functions[key] = value; const self = this; new Promise((resolve) => { setTimeout(() => { resolve(self.register()); }, 3000); }); } static postponeCounter = 0; /** * Add function to postpone, the functions will be executed every 5 items added */ static postpone(key, value) { functions['postpone-' + key] = value; scheduler.postponeCounter += 1; if (scheduler.postponeCounter == 5) { scheduler.executeAll(); scheduler.postponeCounter = 0; } } /** * Execute functon in key and delete * @param key */ static execute(key, deleteAfter = true) { if (typeof functions[key] == 'function') { functions[key](); if (deleteAfter) delete functions[key]; } else { if (scheduler.verbose) console.error(`function with key: ${key} is not function`); } } /** * Execute all function lists */ static async executeAll() { const keys = Object.keys(functions); for (let i = 0; i < keys.length; i++) { const key = keys[i]; if (scheduler.verbose) _log.info(logname, 'executing', key); await Bluebird.promisify(functions[key])(); } } } const locks = []; scheduler.add('clean locks', () => { locks.forEach((lock) => lock.release()); }); const __filename$4 = url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href))); const __dirname$4 = path$2.dirname(__filename$4); /** * search yarn root workspace folder * @param ctx option with property `base_dir` */ function findYarnRootWorkspace(ctx) { const baseDir = ctx.base_dir; /** * extract workspaces from package.json * @param manifest * @returns */ const extractWorkspaces = function (manifest) { const workspaces = (manifest || {}).workspaces; return (workspaces && workspaces.packages) || (Array.isArray(workspaces) ? workspaces : null); }; /** * read package.json from given folder * @param dir * @returns */ const readPackageJSON = function (dir) { const file = path$2.join(dir, 'package.json'); if (fs$1.existsSync(file)) { return JSON.parse(fs$1.readFileSync(file).toString()); } }; let previous = 'THIS INITIATOR VALUE WILL NEVER EXECUTED'; let current = path$2.normalize(baseDir); // loop searching do { const manifest = readPackageJSON(current); if (!manifest) continue; const workspaces = extractWorkspaces(manifest); if (workspaces) { const relativePath = path$2.relative(current, baseDir); if (relativePath === '' || micromatch([relativePath], workspaces).length > 0) { return current; } return null; } previous = current; current = path$2.dirname(current); } while (current !== previous); return null; } /** * Resolves the path of a command binary from `node_modules/.bin`. * * This function searches for the specified command in various directories, including * the current working directory, the module directory, and optionally, user-defined * search directories. If the command is not found, it returns the original command name. * * @param commandName - The name of the command to resolve. * @param options - Optional parameters for command resolution. * @param options.searchDir - A custom directory or an array of directories to search for * the command. If provided, these directories will be included in the search. * @returns The resolved command path if found; otherwise, returns the original command name. */ function resolveCommand(commandName, options) { const dirs = [__dirname$4, process.cwd()]; if (typeof process === 'object') { if ('mainModule' in process) dirs.push(process.mainModule?.paths[0].split('node_modules')[0].slice(0, -1)); if ('main' in process) dirs.push(process.main?.paths[0].split('node_modules')[0].slice(0, -1)); } if (options && options.searchDir) { if (!Array.isArray(options.searchDir)) { options.searchDir = [options.searchDir]; } dirs.push(...options.searchDir); } // try { // dirs.push(findYarnRootWorkspace({ base_dir: process.cwd() })); // } catch (_) { // // // } const cmdPath = dirs .filter((str) => typeof str === 'string' && str.length > 0) .map((cwd) => { const nm = path$2.join(cwd, 'node_modules/.bin'); return path$2.join(nm, commandName); }) .filter(fs$1.existsSync)[0]; if (!cmdPath) { console.error(`Command '${commandName}' not found in node_modules/.bin`); return commandName; // Return the original command name } return process.platform === 'win32' ? `${cmdPath}.cmd` : cmdPath; } const cmd = resolveCommand; /** * no operations * @param _args * @returns */ function noop$1(..._args) { return; } function envNunjucks(loader, opts) { const env = new nunjucks.Environment(loader, opts); env.addFilter('uriencode', (str) => { return hutil.encodeURL(str); }); env.addFilter('noControlChars', (str) => { return str.replace(/[\x00-\x1F\x7F]/g, ''); // eslint-disable-line no-control-regex }); // Extract date from datetime env.addFilter('formatDate', (input) => { return input.toISOString().substring(0, 10); }); return env; } /** * sort alphabetically object by key * @param obj * @returns */ function orderKeys(obj) { const keys = Object.keys(obj).sort(function keyOrder(k1, k2) { if (k1 < k2) return -1; else if (k1 > k2) return +1; else return 0; }); const after = {}; for (let i = 0; i < keys.length; i++) { after[keys[i]] = obj[keys[i]]; delete obj[keys[i]]; } for (let i = 0; i < keys.length; i++) { obj[keys[i]] = after[keys[i]]; } return obj; } /** * get object property by key, supress typescript error * @param item * @param key * @returns */ function getObjectProperty(item, key) { if (typeof item === 'undefined' || item === null) return; if (key in item) return item[key]; } class persistentCache { base; name; duration; memory; persist; memoryCache = {}; constructor(options = {}) { this.base = options.base || normalizePath((require.main ? path$1.dirname(require.main.filename) : undefined) || process.cwd(), 'tmp'); this.name = options.name || 'cache'; this.persist = typeof options.persist == 'boolean' ? options.persist : true; this.memory = typeof options.memory == 'boolean' ? options.memory : true; this.duration = options.duration || Infinity; } /** * add cache deferred callback * @param key * @param data * @param cb * @returns */ put(key, data, cb) { const put = this.putSync(key, data); if (put === true) { return safeCb(cb)(null); } else { safeCb(cb)(put); } } /** * add cache sync * @param key * @param data * @returns boolean=success, any=error */ putSync(key, data) { const entry = this.buildCacheEntry(data); if (this.persist) { // save in file try { writefile(this.buildFilePath(key), jsonStringifyWithCircularRefs(entry)); } catch (e) { return e; } } if (this.memory)