UNPKG

yargs-file-commands

Version:

A yargs helper function that lets you define your commands structure via directory and file naming conventions.

97 lines 4.6 kB
import fs from 'node:fs'; import path from 'node:path'; import { pathToFileURL } from 'node:url'; /** * Imports a command module from a file * @async * @param {string} filePath - Path to the command file * @param {string} name - Command name * @param {ImportCommandOptions} options - Import options * @returns {Promise<CommandModule>} Imported command module * * @description * Dynamically imports a command file and constructs a Yargs command module. * Supports two styles of command declaration: * 1. Single export of CommandModule named 'command' * 2. Individual exports of command parts (command, describe, alias, etc.) * If no handler is provided, creates a null implementation. */ export const importCommandFromFile = async (filePath, name, options) => { // Resolve to absolute path first const resolvedPath = path.resolve(filePath); // Ensure file exists using fs node library if (!fs.existsSync(resolvedPath)) { const originalPath = filePath !== resolvedPath ? ` (original: ${filePath})` : ''; throw new Error(`Cannot import command from non-existent file path: ${resolvedPath}${originalPath}. ` + `Ensure the file exists and the path is correct. If using a relative path, consider using an absolute path or path.resolve().`); } // Get the real (canonical) path to handle symlinks consistently // This ensures the path matches what pathToFileURL will resolve to const realPath = fs.realpathSync.native(resolvedPath); // Construct file URL using Node.js's pathToFileURL which properly handles // path normalization, special characters, and cross-platform compatibility // Use the real path to avoid symlink resolution mismatches const url = pathToFileURL(realPath).href; const { logLevel = 'info' } = options; // Import the module let imported; try { imported = await import(url); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); throw new Error(`Failed to import command module from ${realPath}: ${errorMessage}. ` + `Ensure the file is a valid JavaScript/TypeScript module and the path is correct.`); } // Check if this is the default command const isDefault = name === '$default'; // First try to use the CommandModule export if it exists const importedRecord = imported; if ('command' in importedRecord && typeof importedRecord.command === 'object' && importedRecord.command !== null) { const commandModule = importedRecord.command; // Ensure the command property exists or use the filename if (!commandModule.command && !isDefault) { commandModule.command = name; } else if (isDefault && !commandModule.command) { commandModule.command = '$0'; } if (logLevel === 'debug') { console.debug('Importing CommandModule from', realPath, 'as', name, 'with description', commandModule.describe); } // Return the command module directly without wrapping return { command: commandModule.command, describe: commandModule.describe, builder: commandModule.builder, handler: commandModule.handler, deprecated: commandModule.deprecated, aliases: commandModule.aliases, }; } // Fall back to individual exports const handlerModule = importedRecord; const command = { command: handlerModule.command ?? (isDefault ? '$0' : name), describe: handlerModule.describe, aliases: handlerModule.aliases, builder: handlerModule.builder, deprecated: handlerModule.deprecated, handler: handlerModule.handler ?? (async (_args) => { // null implementation }), }; // Validate exports const supportedNames = ['command', 'describe', 'alias', 'builder', 'deprecated', 'handler']; const module = importedRecord; const unsupportedExports = Object.keys(module).filter((key) => !supportedNames.includes(key)); if (unsupportedExports.length > 0) { throw new Error(`Command module ${name} in ${realPath} has some unsupported exports, probably a misspelling: ${unsupportedExports.join(', ')}. Supported exports are: ${supportedNames.join(', ')}.`); } if (logLevel === 'debug') { console.debug('Importing individual exports from', realPath, 'as', name, 'with description', command.describe); } return command; }; //# sourceMappingURL=importCommand.js.map