From d5e38c8f5d140d14dd1d8449f00899e6ab45eb01 Mon Sep 17 00:00:00 2001 From: DIYgod Date: Thu, 25 Jan 2024 13:29:00 +0800 Subject: [PATCH] feat: use npm directory-import --- lib/routes.ts | 3 +- .../directory-reader-async.ts | 41 ------ .../directory-import/directory-reader-sync.ts | 46 ------- lib/utils/directory-import/import-modules.ts | 105 --------------- lib/utils/directory-import/index.ts | 92 ------------- .../prepare-private-options.ts | 121 ------------------ lib/utils/directory-import/types.d.ts | 33 ----- package.json | 1 + pnpm-lock.yaml | 8 ++ 9 files changed, 10 insertions(+), 440 deletions(-) delete mode 100644 lib/utils/directory-import/directory-reader-async.ts delete mode 100644 lib/utils/directory-import/directory-reader-sync.ts delete mode 100644 lib/utils/directory-import/import-modules.ts delete mode 100644 lib/utils/directory-import/index.ts delete mode 100644 lib/utils/directory-import/prepare-private-options.ts delete mode 100644 lib/utils/directory-import/types.d.ts diff --git a/lib/routes.ts b/lib/routes.ts index ade7d80f30..b32091a0ab 100644 --- a/lib/routes.ts +++ b/lib/routes.ts @@ -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$/, }); diff --git a/lib/utils/directory-import/directory-reader-async.ts b/lib/utils/directory-import/directory-reader-async.ts deleted file mode 100644 index 0037ddc1e2..0000000000 --- a/lib/utils/directory-import/directory-reader-async.ts +++ /dev/null @@ -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} An object containing all imported modules. - */ -export default async function readDirectoryAsync( - options: ImportedModulesPrivateOptions, - targetDirectoryPath: string, -): Promise { - 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(); -} diff --git a/lib/utils/directory-import/directory-reader-sync.ts b/lib/utils/directory-import/directory-reader-sync.ts deleted file mode 100644 index f3a0ef6dd4..0000000000 --- a/lib/utils/directory-import/directory-reader-sync.ts +++ /dev/null @@ -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; -} diff --git a/lib/utils/directory-import/import-modules.ts b/lib/utils/directory-import/import-modules.ts deleted file mode 100644 index 00982169bd..0000000000 --- a/lib/utils/directory-import/import-modules.ts +++ /dev/null @@ -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} An object containing all imported modules. - */ -async function asyncHandler(options: ImportedModulesPrivateOptions): Promise { - 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} An object containing all imported modules. - */ -export default function importModules( - options: ImportedModulesPrivateOptions, -): ImportedModules | Promise { - if (!handlers[options.importMode]) { - throw new Error(`Expected sync or async import method, but got: ${options.importMode}`); - } - - return handlers[options.importMode](options); -} diff --git a/lib/utils/directory-import/index.ts b/lib/utils/directory-import/index.ts deleted file mode 100644 index 6660574bc6..0000000000 --- a/lib/utils/directory-import/index.ts +++ /dev/null @@ -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} An object containing all imported modules. - */ -function directoryImport(...arguments_: ImportModulesInputArguments): ImportedModules | Promise { - const options = preparePrivateOptions(...arguments_); - - try { - return importModules(options); - } catch (error) { - Object.assign(error as Error, options); - - throw error; - } -} - -export { directoryImport }; diff --git a/lib/utils/directory-import/prepare-private-options.ts b/lib/utils/directory-import/prepare-private-options.ts deleted file mode 100644 index cfa51a2458..0000000000 --- a/lib/utils/directory-import/prepare-private-options.ts +++ /dev/null @@ -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; -} diff --git a/lib/utils/directory-import/types.d.ts b/lib/utils/directory-import/types.d.ts deleted file mode 100644 index 19f169bc23..0000000000 --- a/lib/utils/directory-import/types.d.ts +++ /dev/null @@ -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 { - callback?: ImportModulesCallback; - - callerFilePath: string; - callerDirectoryPath: string; -} diff --git a/package.json b/package.json index 2c316374ff..51cf9566cd 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ca3eeca4fd..bc79bcd6b4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -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'}