ctrlshiftleft
Version:
AI-powered toolkit for embedding QA and security testing into development workflows
283 lines (247 loc) • 7.57 kB
JavaScript
/**
* Platform Utilities (JavaScript wrapper)
*
* This is a JavaScript wrapper for the TypeScript platformUtils module.
* It simulates the core functionality for use in JavaScript-only environments.
*/
const fs = require('fs');
const path = require('path');
const os = require('os');
// Line ending types
const LINE_ENDINGS = {
lf: '\n',
crlf: '\r\n',
auto: os.platform() === 'win32' ? '\r\n' : '\n'
};
class PathUtils {
// Normalize path separators for the current platform
static normalizePath(filePath) {
return filePath.replace(/[/\\]+/g, path.sep);
}
// Ensure a path uses forward slashes
static forwardSlashes(filePath) {
return filePath.replace(/\\/g, '/');
}
// Convert a path to an 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
static getRelativePath(from, to) {
const relativePath = path.relative(from, to);
return this.normalizePath(relativePath);
}
// Ensure a directory exists, creating it if necessary
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
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
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
static isFile(filePath) {
try {
return fs.existsSync(filePath) && fs.statSync(filePath).isFile();
} catch (error) {
return false;
}
}
}
class FileUtils {
// Ensure file content has consistent line endings
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') {
return os.platform() === 'win32' ?
normalized.replace(/\n/g, '\r\n') :
normalized;
}
return normalized;
}
// Write file with consistent line endings
static writeFile(filePath, content, lineEnding = 'auto') {
try {
// Ensure parent directory exists
const dirPath = path.dirname(filePath);
if (!fs.existsSync(dirPath)) {
fs.mkdirSync(dirPath, { recursive: true });
}
// Normalize line endings
const normalized = this.normalizeLineEndings(content, lineEnding);
// Write to file
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
static readFile(filePath, lineEnding = 'auto') {
try {
if (!fs.existsSync(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;
}
}
}
class TestOutputUtils {
// Generate a platform-appropriate path for test outputs
static getTestOutputPath(componentPath) {
const filename = path.basename(componentPath, path.extname(componentPath));
return path.join('tests', filename, `${filename}.spec.ts`);
}
// Generate test configuration content with appropriate line endings
static generateTestConfig(configType, outputDir, lineEnding = 'auto') {
let content = '';
if (configType === 'playwright') {
content = `
import { defineConfig } 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: { browserName: 'chromium' },
},
{
name: 'firefox',
use: { browserName: 'firefox' },
},
{
name: 'webkit',
use: { browserName: 'webkit' },
},
],
});
`.trim();
} 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',
},
collectCoverage: true,
collectCoverageFrom: [
'src/**/*.{js,jsx,ts,tsx}',
'!src/**/*.d.ts',
'!src/mocks/**',
],
coverageDirectory: '../coverage',
};
`.trim();
}
return FileUtils.normalizeLineEndings(content, lineEnding);
}
}
// Cross-platform shell utilities
class ShellUtils {
// Get platform-appropriate shell command
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
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 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'
};
}
module.exports = {
LINE_ENDINGS,
PathUtils,
FileUtils,
TestOutputUtils,
ShellUtils,
getPlatformInfo
};