UNPKG

ctrlshiftleft

Version:

AI-powered toolkit for embedding QA and security testing into development workflows

428 lines (427 loc) 14.9 kB
"use strict"; /** * Platform Utilities * * Utilities for cross-platform compatibility to ensure consistent behavior * across different operating systems (Windows, macOS, Linux). */ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.ShellUtils = exports.TestOutputUtils = exports.FileUtils = exports.PathUtils = exports.LINE_ENDINGS = void 0; exports.getPlatformInfo = getPlatformInfo; const path = __importStar(require("path")); const os = __importStar(require("os")); const fs = __importStar(require("fs")); /** * Line ending characters by type */ exports.LINE_ENDINGS = { lf: '\n', crlf: '\r\n', auto: os.platform() === 'win32' ? '\r\n' : '\n' }; /** * Cross-platform file path separator functions */ class PathUtils { /** * Normalize path separators for the current platform * @param filePath File path to normalize * @returns Normalized path */ static normalizePath(filePath) { return filePath.replace(/[/\\]+/g, path.sep); } /** * Ensure a path uses forward slashes (useful for URL or config paths) * @param filePath File path to normalize * @returns Path with forward slashes */ static forwardSlashes(filePath) { return filePath.replace(/\\/g, '/'); } /** * Convert a path to an absolute path, handling both relative and absolute inputs * @param filePath File path to resolve * @param basePath Base path for resolving relative paths (defaults to current directory) * @returns Absolute path */ static toAbsolutePath(filePath, basePath = process.cwd()) { if (path.isAbsolute(filePath)) { return this.normalizePath(filePath); } return this.normalizePath(path.resolve(basePath, filePath)); } /** * Get a relative path from one location to another, handling cross-platform differences * @param from Source path * @param to Destination path * @returns Relative path */ static getRelativePath(from, to) { const relativePath = path.relative(from, to); return this.normalizePath(relativePath); } /** * Ensure a directory exists, creating it if necessary * @param dirPath Directory path to ensure * @returns True if directory exists or was created */ static ensureDir(dirPath) { try { if (!fs.existsSync(dirPath)) { fs.mkdirSync(dirPath, { recursive: true }); } return true; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); console.error(`Error creating directory ${dirPath}: ${errorMessage}`); return false; } } /** * Get a platform-appropriate temporary directory path * @param subdir Optional subdirectory name * @returns Path to temporary directory */ static getTempDir(subdir) { const tempBase = os.tmpdir(); const dirPath = subdir ? path.join(tempBase, subdir) : tempBase; this.ensureDir(dirPath); return dirPath; } /** * Determine if a path exists and is a directory * @param dirPath Path to check * @returns True if path is a directory */ static isDirectory(dirPath) { try { return fs.existsSync(dirPath) && fs.statSync(dirPath).isDirectory(); } catch (error) { return false; } } /** * Determine if a path exists and is a file * @param filePath Path to check * @returns True if path is a file */ static isFile(filePath) { try { return fs.existsSync(filePath) && fs.statSync(filePath).isFile(); } catch (error) { return false; } } } exports.PathUtils = PathUtils; /** * Cross-platform file content utilities */ class FileUtils { /** * Ensure file content has consistent line endings * @param content File content to normalize * @param lineEnding Line ending type to use * @returns Normalized content */ static normalizeLineEndings(content, lineEnding = 'auto') { // First convert all line endings to LF const normalized = content.replace(/\r\n|\r/g, '\n'); // Then convert to the desired line ending if (lineEnding === 'crlf') { return normalized.replace(/\n/g, '\r\n'); } if (lineEnding === 'auto' && os.platform() === 'win32') { return normalized.replace(/\n/g, '\r\n'); } return normalized; } /** * Write file with consistent line endings * @param filePath Path to write to * @param content Content to write * @param lineEnding Line ending type to use * @returns True if successful */ static writeFile(filePath, content, lineEnding = 'auto') { try { const normalized = this.normalizeLineEndings(content, lineEnding); const dir = path.dirname(filePath); // Ensure directory exists if (!PathUtils.ensureDir(dir)) { return false; } fs.writeFileSync(filePath, normalized); return true; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); console.error(`Error writing file ${filePath}: ${errorMessage}`); return false; } } /** * Read file and normalize line endings * @param filePath Path to read from * @param lineEnding Line ending type to use * @returns File content or null if error */ static readFile(filePath, lineEnding = 'auto') { try { if (!PathUtils.isFile(filePath)) { return null; } const content = fs.readFileSync(filePath, 'utf8'); return this.normalizeLineEndings(content, lineEnding); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); console.error(`Error reading file ${filePath}: ${errorMessage}`); return null; } } /** * Append to file with consistent line endings * @param filePath Path to append to * @param content Content to append * @param lineEnding Line ending type to use * @returns True if successful */ static appendFile(filePath, content, lineEnding = 'auto') { try { let normalized = this.normalizeLineEndings(content, lineEnding); // Add a line break at the beginning if file exists and doesn't end with one if (PathUtils.isFile(filePath)) { const existing = fs.readFileSync(filePath, 'utf8'); const ending = lineEnding === 'crlf' ? '\r\n' : (lineEnding === 'auto' && os.platform() === 'win32') ? '\r\n' : '\n'; if (!existing.endsWith(ending)) { normalized = ending + normalized; } } const dir = path.dirname(filePath); // Ensure directory exists if (!PathUtils.ensureDir(dir)) { return false; } fs.appendFileSync(filePath, normalized); return true; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); console.error(`Error appending to file ${filePath}: ${errorMessage}`); return false; } } } exports.FileUtils = FileUtils; /** * Cross-platform test output generator */ class TestOutputUtils { /** * Generate a platform-appropriate path for test outputs * @param componentPath Path to component being tested * @param outputBase Base directory for output * @param fileExtension File extension for output file * @returns Path to test output file */ static getTestOutputPath(componentPath, outputBase = 'tests', fileExtension = '.spec.ts') { // Extract component name from path const componentName = path.basename(componentPath, path.extname(componentPath)); // Handle different naming conventions: // foo-bar.jsx -> FooBar.spec.ts or foo-bar.spec.ts const camelCaseName = componentName .split(/[-_]/) .map(part => part.charAt(0).toUpperCase() + part.slice(1)) .join(''); // Ensure output directory exists const outputDir = path.join(process.cwd(), outputBase); PathUtils.ensureDir(outputDir); // Create component subdirectory for better organization const componentDir = path.join(outputDir, camelCaseName); PathUtils.ensureDir(componentDir); // Return path with appropriate extension return path.join(componentDir, `${camelCaseName}${fileExtension}`); } /** * Generate configuration file with platform-appropriate paths * @param configType Type of configuration (playwright, jest) * @param outputDir Output directory * @param lineEnding Line ending type to use * @returns Generated configuration content */ static generateTestConfig(configType, outputDir, lineEnding = 'auto') { // Normalize path for the config file const normalizedOutputDir = PathUtils.forwardSlashes(outputDir); let content = ''; if (configType === 'playwright') { content = `import { defineConfig, devices } from '@playwright/test'; export default defineConfig({ testDir: './', outputDir: '../test-results', timeout: 30000, fullyParallel: true, forbidOnly: !!process.env.CI, retries: process.env.CI ? 2 : 0, workers: process.env.CI ? 1 : undefined, reporter: [['html', { outputFolder: '../test-report' }]], use: { baseURL: process.env.BASE_URL || 'http://localhost:3000', trace: 'on-first-retry', screenshot: 'only-on-failure', }, projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'] }, }, { name: 'firefox', use: { ...devices['Desktop Firefox'] }, }, { name: 'webkit', use: { ...devices['Desktop Safari'] }, }, ], });`; } else { content = `module.exports = { preset: 'ts-jest', testEnvironment: 'jsdom', testMatch: ['**/?(*.)+(spec|test).[jt]s?(x)'], moduleNameMapper: { '\\\\.(css|less|scss|sass)$': 'identity-obj-proxy', '^@/(.*)$': '<rootDir>/src/$1', }, setupFilesAfterEnv: ['./jest.setup.js'], collectCoverage: true, collectCoverageFrom: [ 'src/**/*.{js,jsx,ts,tsx}', '!src/**/*.d.ts', '!src/mocks/**', ], coverageDirectory: '../coverage', };`; } return FileUtils.normalizeLineEndings(content, lineEnding); } } exports.TestOutputUtils = TestOutputUtils; /** * Cross-platform shell command utilities */ class ShellUtils { /** * Get platform-appropriate shell command * @param command Base command * @param args Command arguments * @returns Platform-specific command string */ static getCommand(command, args = []) { const isWindows = os.platform() === 'win32'; // Handle commands that need special treatment on Windows if (isWindows) { // Common commands that need to be mapped on Windows const windowsCommandMap = { 'rm': 'del', 'cp': 'copy', 'mv': 'move', 'ls': 'dir', 'cat': 'type', 'mkdir': 'md', 'rmdir': 'rd', 'touch': 'echo.>', 'grep': 'findstr' }; // Map command if needed const mappedCommand = windowsCommandMap[command] || command; // Build the command string return [mappedCommand, ...args].join(' '); } // For non-Windows platforms, just join the command and args return [command, ...args].join(' '); } /** * Get platform-appropriate file path for a command * @param filePath File path to use in command * @returns Formatted file path string */ static getCommandPath(filePath) { const isWindows = os.platform() === 'win32'; // On Windows, wrap paths with spaces in quotes if (isWindows && filePath.includes(' ')) { return `"${filePath}"`; } // On Unix-like systems, escape spaces return filePath.replace(/ /g, '\\ '); } /** * Get platform-appropriate npm script command * @param scriptName NPM script name * @param args Script arguments * @returns Platform-specific npm command string */ static getNpmCommand(scriptName, args = []) { const isWindows = os.platform() === 'win32'; const npmCmd = isWindows ? 'npm.cmd' : 'npm'; // Build the command string return [npmCmd, 'run', scriptName, '--', ...args].join(' '); } } exports.ShellUtils = ShellUtils; /** * Detect the operating system and provide platform information */ function getPlatformInfo() { const platform = os.platform(); return { platform, isWindows: platform === 'win32', isMac: platform === 'darwin', isLinux: platform === 'linux', arch: os.arch(), pathSeparator: path.sep, lineEnding: platform === 'win32' ? '\r\n' : '\n' }; } //# sourceMappingURL=platformUtils.js.map