mirror of
https://github.com/DIYgod/RSSHub.git
synced 2026-09-24 23:12:34 +08:00
feat: use npm directory-import
This commit is contained in:
+1
-2
@@ -1,4 +1,4 @@
|
||||
import { directoryImport } from '@/utils/directory-import';
|
||||
import { directoryImport } from 'directory-import';
|
||||
import type { Handler } from 'hono'
|
||||
|
||||
type Root = {
|
||||
@@ -6,7 +6,6 @@ type Root = {
|
||||
}
|
||||
|
||||
const imports = directoryImport({
|
||||
callerDirectoryPath: __dirname,
|
||||
targetDirectoryPath: './v3',
|
||||
importPattern: /router\.js$/,
|
||||
});
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
import { promises as fs } from 'node:fs';
|
||||
|
||||
import { ImportedModulesPrivateOptions } from './types.d';
|
||||
|
||||
/**
|
||||
* Async version of the directory reader.
|
||||
*
|
||||
* I use *for loop* instead of forEach because it is faster.
|
||||
* I define the *counter* variable outside the loop because it is faster. Because it is not redefined every iteration.
|
||||
* @param {ImportedModulesPrivateOptions} options - The private options generated by the preparePrivateOptions function.
|
||||
* @param {string} targetDirectoryPath - The path to the directory to import modules from. Required for recursive calls.
|
||||
* @returns {Promise<string[]>} An object containing all imported modules.
|
||||
*/
|
||||
export default async function readDirectoryAsync(
|
||||
options: ImportedModulesPrivateOptions,
|
||||
targetDirectoryPath: string,
|
||||
): Promise<string[]> {
|
||||
const receivedItemsPaths = await fs.readdir(targetDirectoryPath);
|
||||
|
||||
const result = await Promise.all(
|
||||
receivedItemsPaths.map(async (itemPath) => {
|
||||
const receivedFilesPaths = [];
|
||||
|
||||
const relativeItemPath = `${targetDirectoryPath}/${itemPath}`;
|
||||
|
||||
const stat = await fs.stat(relativeItemPath);
|
||||
|
||||
if (stat.isDirectory() && options.includeSubdirectories) {
|
||||
const files = await readDirectoryAsync(options, relativeItemPath);
|
||||
|
||||
receivedFilesPaths.push(...files);
|
||||
} else if (stat.isFile()) {
|
||||
receivedFilesPaths.push('/' + relativeItemPath);
|
||||
}
|
||||
|
||||
return receivedFilesPaths;
|
||||
}),
|
||||
);
|
||||
|
||||
return result.flat();
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
import * as fs from 'node:fs';
|
||||
|
||||
import { ImportedModulesPrivateOptions } from './types.d';
|
||||
|
||||
/**
|
||||
* Sync version of the directory reader.
|
||||
*
|
||||
* !important this function will block the event loop until it finishes.
|
||||
* I use *for loop* instead of forEach because it is faster.
|
||||
* I define the *counter* variable outside the loop because it is faster. Because it is not redefined every iteration.
|
||||
* @param {ImportedModulesPrivateOptions} options - The private options generated by the preparePrivateOptions function.
|
||||
* @param {string} targetDirectoryPath - The path to the directory to import modules from. Required for recursive calls.
|
||||
* @returns {string[]} An array of file paths.
|
||||
*/
|
||||
export default function readDirectorySync(
|
||||
options: ImportedModulesPrivateOptions,
|
||||
targetDirectoryPath: string,
|
||||
): string[] {
|
||||
const receivedItemsPaths = fs.readdirSync(targetDirectoryPath);
|
||||
const receivedDirectoriesPaths = [];
|
||||
const receivedFilesPaths = [];
|
||||
|
||||
// Get the files and directories paths from the target directory.
|
||||
let itemsCounter = 0;
|
||||
for (itemsCounter; itemsCounter < receivedItemsPaths.length; itemsCounter += 1) {
|
||||
const itemPath = `${targetDirectoryPath}/${receivedItemsPaths[itemsCounter]}`;
|
||||
const stat = fs.statSync(itemPath);
|
||||
|
||||
if (stat.isDirectory() && options.includeSubdirectories) {
|
||||
receivedDirectoriesPaths.push(itemPath);
|
||||
} else if (stat.isFile()) {
|
||||
receivedFilesPaths.push('/' + itemPath);
|
||||
}
|
||||
}
|
||||
|
||||
// Recursively get the files paths from the subdirectories.
|
||||
let directoriesCounter = 0;
|
||||
for (directoriesCounter; directoriesCounter < receivedDirectoriesPaths.length; directoriesCounter += 1) {
|
||||
const files = readDirectorySync(options, receivedDirectoriesPaths[directoriesCounter] as string);
|
||||
|
||||
receivedFilesPaths.push(...files);
|
||||
}
|
||||
|
||||
// Return the files paths.
|
||||
return receivedFilesPaths;
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
import * as path from 'node:path';
|
||||
|
||||
import readDirectoryAsync from './directory-reader-async';
|
||||
import readDirectorySync from './directory-reader-sync';
|
||||
import { ImportedModules, ImportedModulesPrivateOptions } from './types.d';
|
||||
|
||||
const VALID_IMPORT_EXTENSIONS = new Set(['.js', '.mjs', '.ts', '.json']);
|
||||
|
||||
const handlers = { async: asyncHandler, sync: syncHandler };
|
||||
|
||||
/**
|
||||
* Synchronously import modules from the specified directory.
|
||||
* @param {ImportedModulesPrivateOptions} options - The private options generated by the preparePrivateOptions function.
|
||||
* @returns {ImportedModules} An object containing all imported modules.
|
||||
*/
|
||||
function syncHandler(options: ImportedModulesPrivateOptions): ImportedModules {
|
||||
const modules = {};
|
||||
|
||||
const filesPaths = readDirectorySync(options, options.targetDirectoryPath);
|
||||
|
||||
let index = 0;
|
||||
|
||||
for (const filePath of filesPaths) {
|
||||
const isModuleImported = importModule(filePath, index, options, modules);
|
||||
|
||||
if (isModuleImported) index += 1;
|
||||
if (index === options.limit) break;
|
||||
}
|
||||
|
||||
return modules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asynchronously import modules from the specified directory.
|
||||
* @param {ImportedModulesPrivateOptions} options - The private options generated by the preparePrivateOptions function.
|
||||
* @returns {Promise<ImportedModules>} An object containing all imported modules.
|
||||
*/
|
||||
async function asyncHandler(options: ImportedModulesPrivateOptions): Promise<ImportedModules> {
|
||||
const modules = {};
|
||||
|
||||
const filesPaths = await readDirectoryAsync(options, options.targetDirectoryPath);
|
||||
|
||||
let index = 0;
|
||||
|
||||
for (const filePath of filesPaths) {
|
||||
const isModuleImported = importModule(filePath, index, options, modules);
|
||||
|
||||
if (isModuleImported) index += 1;
|
||||
if (index === options.limit) break;
|
||||
}
|
||||
|
||||
return modules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Import a module and add it to the modules object.
|
||||
* @param {string} filePath - The path to the file to import.
|
||||
* @param {number} index - The index of the module.
|
||||
* @param {ImportedModulesPrivateOptions} options - The private options generated by the preparePrivateOptions function.
|
||||
* @param {ImportedModules} modules - The object containing all imported modules.
|
||||
* @returns {boolean} Whether the module was imported or not.
|
||||
*/
|
||||
function importModule(
|
||||
filePath: string,
|
||||
index: number,
|
||||
options: ImportedModulesPrivateOptions,
|
||||
modules: ImportedModules,
|
||||
) {
|
||||
const { name: fileName, ext: fileExtension } = path.parse(filePath);
|
||||
const isValidModuleExtension = VALID_IMPORT_EXTENSIONS.has(fileExtension);
|
||||
const isDeclarationFile = filePath.endsWith('.d.ts');
|
||||
const isValidFilePath = options.importPattern ? options.importPattern.test(filePath) : true;
|
||||
|
||||
if (!isValidModuleExtension) return false;
|
||||
if (!isValidFilePath) return false;
|
||||
if (isDeclarationFile) return false;
|
||||
|
||||
const relativeModulePath = filePath.slice(options.targetDirectoryPath.length + 1);
|
||||
|
||||
// eslint-disable-next-line security/detect-non-literal-require, @typescript-eslint/no-var-requires, unicorn/prefer-module
|
||||
const importedModule = require(filePath) as unknown;
|
||||
|
||||
modules[relativeModulePath] = importedModule;
|
||||
|
||||
if (options.callback) {
|
||||
options.callback(fileName, relativeModulePath, importedModule, index);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Import all modules from the specified directory synchronously or asynchronously.
|
||||
* @param {ImportedModulesPrivateOptions} options - The private options generated by the preparePrivateOptions function.
|
||||
* @returns {ImportedModules | Promise<ImportedModules>} An object containing all imported modules.
|
||||
*/
|
||||
export default function importModules(
|
||||
options: ImportedModulesPrivateOptions,
|
||||
): ImportedModules | Promise<ImportedModules> {
|
||||
if (!handlers[options.importMode]) {
|
||||
throw new Error(`Expected sync or async import method, but got: ${options.importMode}`);
|
||||
}
|
||||
|
||||
return handlers[options.importMode](options);
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
import importModules from './import-modules';
|
||||
import preparePrivateOptions from './prepare-private-options';
|
||||
import {
|
||||
ImportedModules,
|
||||
ImportedModulesPublicOptions,
|
||||
ImportModulesCallback,
|
||||
ImportModulesInputArguments,
|
||||
ImportModulesMode,
|
||||
} from './types.d';
|
||||
|
||||
/**
|
||||
* Import modules from the current directory synchronously
|
||||
* @returns {ImportedModules} An object containing all imported modules.
|
||||
*/
|
||||
function directoryImport(): ImportedModules;
|
||||
|
||||
/**
|
||||
* Import modules from the current directory synchronously and call the provided callback for each imported module.
|
||||
* @param {ImportModulesCallback} callback - The callback function to call for each imported module.
|
||||
* @returns {ImportedModules} An object containing all imported modules.
|
||||
*/
|
||||
function directoryImport(callback: ImportModulesCallback): ImportedModules;
|
||||
|
||||
/**
|
||||
* Import modules from the specified directory synchronously.
|
||||
* @param {string} targetDirectoryPath - The path to the directory to import modules from.
|
||||
* @returns {ImportedModules} An object containing all imported modules.
|
||||
*/
|
||||
function directoryImport(targetDirectoryPath: string): ImportedModules;
|
||||
|
||||
/**
|
||||
* Import all modules from the specified directory synchronously and call the provided callback for each imported module.
|
||||
* @param {string} targetDirectoryPath - The path to the directory to import modules from.
|
||||
* @param {ImportModulesCallback} callback - The callback function to call for each imported module.
|
||||
* @returns {ImportedModules} An object containing all imported modules.
|
||||
*/
|
||||
function directoryImport(targetDirectoryPath: string, callback: ImportModulesCallback): ImportedModules;
|
||||
|
||||
/**
|
||||
* Import all modules from the specified directory synchronously or asynchronously.
|
||||
* @param {string} targetDirectoryPath - The path to the directory to import modules from.
|
||||
* @param {ImportModulesMode} mode - The import mode. Can be 'sync' or 'async'.
|
||||
* @returns {ImportedModules} An object containing all imported modules.
|
||||
*/
|
||||
function directoryImport(targetDirectoryPath: string, mode: ImportModulesMode): ImportedModules;
|
||||
|
||||
/**
|
||||
* Import all modules from the specified directory synchronously or asynchronously and call the provided callback for each imported module.
|
||||
* @param {string} targetDirectoryPath - The path to the directory to import modules from.
|
||||
* @param {ImportModulesMode} importMode - The import mode. Can be 'sync' or 'async'.
|
||||
* @param {ImportModulesCallback} callback - The callback function to call for each imported module.
|
||||
* @returns {ImportedModules} An object containing all imported modules.
|
||||
*/
|
||||
function directoryImport(
|
||||
targetDirectoryPath: string,
|
||||
importMode: ImportModulesMode,
|
||||
callback: ImportModulesCallback,
|
||||
): ImportedModules;
|
||||
|
||||
/**
|
||||
* Import all modules from the specified directory
|
||||
* @param {ImportedModulesPublicOptions} options - The options object.
|
||||
* @returns {ImportedModules} An object containing all imported modules.
|
||||
*/
|
||||
function directoryImport(options: ImportedModulesPublicOptions): ImportedModules;
|
||||
|
||||
/**
|
||||
* Import all modules from the specified directory and call the provided callback for each imported module.
|
||||
* @param {ImportedModulesPublicOptions} options - The options object.
|
||||
* @param {ImportModulesCallback} callback - The callback function to call for each imported module.
|
||||
* @returns {ImportedModules} An object containing all imported modules.
|
||||
*/
|
||||
function directoryImport(options: ImportedModulesPublicOptions, callback: ImportModulesCallback): ImportedModules;
|
||||
|
||||
/**
|
||||
* Import all modules from the specified directory with the given options and call the provided callback for each imported module.
|
||||
* @param {ImportModulesInputArguments} arguments_ - The arguments.
|
||||
* @returns {ImportedModules|Promise<ImportedModules>} An object containing all imported modules.
|
||||
*/
|
||||
function directoryImport(...arguments_: ImportModulesInputArguments): ImportedModules | Promise<ImportedModules> {
|
||||
const options = preparePrivateOptions(...arguments_);
|
||||
|
||||
try {
|
||||
return importModules(options);
|
||||
} catch (error) {
|
||||
Object.assign(error as Error, options);
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export { directoryImport };
|
||||
@@ -1,121 +0,0 @@
|
||||
import * as path from 'node:path';
|
||||
|
||||
import {
|
||||
ImportedModulesPrivateOptions,
|
||||
ImportedModulesPublicOptions,
|
||||
ImportModulesInputArguments,
|
||||
ImportModulesMode,
|
||||
} from './types.d';
|
||||
|
||||
const getDefaultOptions = (): ImportedModulesPrivateOptions => {
|
||||
const options = {
|
||||
includeSubdirectories: true,
|
||||
importMode: 'sync' as ImportModulesMode,
|
||||
importPattern: /.*/,
|
||||
limit: Number.POSITIVE_INFINITY,
|
||||
callerFilePath: '/',
|
||||
callerDirectoryPath: '/',
|
||||
targetDirectoryPath: '/',
|
||||
};
|
||||
|
||||
options.callerFilePath = (new Error('functional-error').stack as string)
|
||||
.split('\n')[4]
|
||||
?.match(/at file:\/\/(.+):\d+|at .+ \((.+):\d+:\d+\)/)?.slice(1).find(Boolean) as string;
|
||||
|
||||
options.callerDirectoryPath = options.callerFilePath?.split('/').slice(0, -1).join('/');
|
||||
options.targetDirectoryPath = options.callerDirectoryPath;
|
||||
|
||||
return options;
|
||||
};
|
||||
|
||||
/**
|
||||
* Prepare the options object from the provided arguments.
|
||||
* @param {...any} arguments_ - The arguments.
|
||||
* @returns {ImportedModulesPrivateOptions} The options object.
|
||||
*/
|
||||
export default function preparePrivateOptions(
|
||||
...arguments_: ImportModulesInputArguments
|
||||
): ImportedModulesPrivateOptions {
|
||||
const options = { ...getDefaultOptions() };
|
||||
|
||||
// * Check first argument
|
||||
|
||||
// ** If user provided nothing as first argument,
|
||||
// ** it means that he wants to use the default options
|
||||
// ** return the default options
|
||||
if (arguments_[0] === undefined) {
|
||||
return options;
|
||||
}
|
||||
|
||||
// ** If user provided an object as first argument,
|
||||
// ** it means that he wants to set custom options
|
||||
// ** use it to oweverwrite the default options
|
||||
else if (typeof arguments_[0] === 'object') {
|
||||
const result = {
|
||||
...getDefaultOptions(),
|
||||
...(arguments_[0] as ImportedModulesPublicOptions),
|
||||
};
|
||||
|
||||
result.targetDirectoryPath = path.resolve(result.callerDirectoryPath, result.targetDirectoryPath);
|
||||
result.callback = typeof arguments_[1] === 'function' ? arguments_[1] : undefined;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// ** If user provided a string
|
||||
// ** it means that he wants to set the target directory path as first argument
|
||||
// ** use it to oweverwrite the default options
|
||||
else if (typeof arguments_[0] === 'string') {
|
||||
options.targetDirectoryPath = path.resolve(options.callerDirectoryPath, arguments_[0]);
|
||||
}
|
||||
|
||||
// ** If user provided a function
|
||||
// ** it means that he wants to set the callback as first argument
|
||||
// ** use it to oweverwrite the default options
|
||||
else if (typeof arguments_[0] === 'function') {
|
||||
options.callback = arguments_[0];
|
||||
}
|
||||
|
||||
// ** If user provided something else
|
||||
// ** it means that he made a mistake
|
||||
// ** throw an error
|
||||
else {
|
||||
throw new TypeError(
|
||||
`Expected undefined, object, string or function as first argument, but got: ${typeof arguments_[0]}`,
|
||||
);
|
||||
}
|
||||
|
||||
// * Check second argument
|
||||
|
||||
// ** If user provided string as second argument
|
||||
// ** it means that he wants to set the import mode (sync or async) as second argument
|
||||
// ** use it to oweverwrite the default options
|
||||
if (typeof arguments_[1] === 'string') {
|
||||
// ** But if the import mode is not sync or async
|
||||
// ** throw an error
|
||||
if (arguments_[1] !== 'sync' && arguments_[1] !== 'async') {
|
||||
throw new TypeError(`Expected sync or async as second argument, but got: ${arguments_[1]}`);
|
||||
}
|
||||
|
||||
options.importMode = arguments_[1];
|
||||
}
|
||||
|
||||
// ** If user provided a function as second argument
|
||||
// ** it means that he wants to set the callback as second argument
|
||||
// ** use it to oweverwrite the default options
|
||||
else if (typeof arguments_[1] === 'function') {
|
||||
options.callback = arguments_[1];
|
||||
}
|
||||
|
||||
// * Check third argument
|
||||
|
||||
// ** If user provided a function as third argument
|
||||
// ** it means that he wants to set the callback as third argument
|
||||
// ** use it to oweverwrite the default options
|
||||
if (typeof arguments_[2] === 'function') {
|
||||
options.callback = arguments_[2];
|
||||
}
|
||||
|
||||
// * Return the options object
|
||||
return options;
|
||||
}
|
||||
-33
@@ -1,33 +0,0 @@
|
||||
export type ModuleName = string;
|
||||
export type ModulePath = string;
|
||||
export type ModuleData = unknown;
|
||||
export type ModuleIndex = number;
|
||||
|
||||
export type ImportModulesMode = 'sync' | 'async';
|
||||
export type ImportModulesCallback = (name: ModuleName, path: ModulePath, data: ModuleData, index: ModuleIndex) => void;
|
||||
|
||||
export type ImportModulesInputArguments = [
|
||||
targetDirectoryPathOrOptionsOrCallback?: string | ImportedModulesPublicOptions | ImportModulesCallback,
|
||||
modeOrCallback?: ImportModulesMode | ImportModulesCallback,
|
||||
callback?: ImportModulesCallback,
|
||||
];
|
||||
|
||||
export interface ImportedModules {
|
||||
[key: ModulePath]: ModuleData;
|
||||
}
|
||||
|
||||
export interface ImportedModulesPublicOptions {
|
||||
includeSubdirectories?: boolean;
|
||||
callerDirectoryPath?: string;
|
||||
targetDirectoryPath?: string;
|
||||
importPattern?: RegExp;
|
||||
importMode?: ImportModulesMode;
|
||||
limit?: number;
|
||||
}
|
||||
|
||||
export interface ImportedModulesPrivateOptions extends Required<ImportedModulesPublicOptions> {
|
||||
callback?: ImportModulesCallback;
|
||||
|
||||
callerFilePath: string;
|
||||
callerDirectoryPath: string;
|
||||
}
|
||||
@@ -91,6 +91,7 @@
|
||||
"crypto-js": "4.2.0",
|
||||
"currency-symbol-map": "5.1.0",
|
||||
"dayjs": "1.11.8",
|
||||
"directory-import": "3.2.1",
|
||||
"dotenv": "16.3.2",
|
||||
"entities": "4.5.0",
|
||||
"etag": "1.8.1",
|
||||
|
||||
Generated
+8
@@ -53,6 +53,9 @@ dependencies:
|
||||
dayjs:
|
||||
specifier: 1.11.8
|
||||
version: 1.11.8
|
||||
directory-import:
|
||||
specifier: 3.2.1
|
||||
version: 3.2.1
|
||||
dotenv:
|
||||
specifier: 16.3.2
|
||||
version: 16.3.2
|
||||
@@ -3421,6 +3424,11 @@ packages:
|
||||
engines: {node: '>=0.3.1'}
|
||||
dev: true
|
||||
|
||||
/directory-import@3.2.1:
|
||||
resolution: {integrity: sha512-hIMc9WJHs4HRZbBiNAvKGxkhjaJ2wOaFcFA0K+yj16GyBJDg28e6QgQ3hFuFB0ZRETNYZu3u85rmxIxoxWEwbA==}
|
||||
engines: {node: '>=18.17.0'}
|
||||
dev: false
|
||||
|
||||
/doctrine@3.0.0:
|
||||
resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==}
|
||||
engines: {node: '>=6.0.0'}
|
||||
|
||||
Reference in New Issue
Block a user