ThatAIguyA world of ideas

THE CONSCIOUSNESS ATLAS
Read. Experiment. Follow the evidence.

ConsciousnessLearn · Explore · Joseph Anady

Janady13/llms-txt-hub / scripts/validation/check-as-casts.js

Source identity, revision, and recovered contents.

GitHub sourceSource text recovered

Source identity

FieldRecorded value
Source typeGitHub source
Original recordJanady13/llms-txt-hub / scripts/validation/check-as-casts.js
DateNot specified in source
RepositoryJanady13/llms-txt-hub
Revisione0f4bf978d4df168369c7601d9c5f5ea89e84377
Branchmain
Path or archive memberscripts/validation/check-as-casts.js
Original SHA-2566d1a1643a3d86a42e8dcc62c1e66594a8a5110cc48602a7146e1854beee474dd
Git blob SHA2d6659da36b09b891583545b61595a00f1250642
Reading-copy SHA-2566d1a1643a3d86a42e8dcc62c1e66594a8a5110cc48602a7146e1854beee474dd

Original source ↗

Recovered source transcript

#!/usr/bin/env node /* eslint-disable no-console */ /** * Validates that TypeScript files don't use 'as' type assertions * Enforces proper type safety without type casting */ // ============================================================================ // CONFIGURATION - Customize these settings for your project // ============================================================================ // Patterns to detect unsafe 'as' type assertions const AS_CAST_PATTERNS = [ // Match 'as Type' but not 'as const' /\bas\s+(?!const\b)[A-Z]\w*/g, // Match 'as any' /\bas\s+any\b/g, // Match 'as unknown' /\bas\s+unknown\b/g, // Match generic as casts like 'as Array<T>' /\bas\s+[A-Z]\w*\s*</g, // Match parenthesized as casts /\)\s*as\s+[A-Z]\w*/g ] // Exceptions where 'as' might be acceptable const ALLOWED_PATTERNS = [ // Allow 'as const' for const assertions /\bas\s+const\b/g, // Allow in test files for mocking /\.test\.(ts|tsx)$/, /\.spec\.(ts|tsx)$/, /__tests__/, // Allow in migration scripts (temporary) /scripts\/migrate.*\.ts$/ ] // Directories to exclude from checking const EXCLUDE_PATHS = ['node_modules', 'dist', 'build', '.next', 'coverage', '.git', 'public'] // ============================================================================ // IMPLEMENTATION - No need to modify below this line // ============================================================================ const fs = require('node:fs') const path = require('node:path') const { execSync } = require('node:child_process') // Colors for terminal output const colors = { red: '\x1b[31m', yellow: '\x1b[33m', green: '\x1b[32m', blue: '\x1b[34m', reset: '\x1b[0m', bold: '\x1b[1m' } /** * Check if a file path should be excluded */ function shouldExcludeFile(filePath) { return EXCLUDE_PATHS.some(exclude => filePath.includes(exclude)) } /** * Check if a file is allowed to have as casts */ function isAllowedFile(filePath) { return ALLOWED_PATTERNS.some(pattern => { if (pattern instanceof RegExp) { return pattern.test(filePath) } return false }) } /** * Find all as casts in a file */ function findAsCasts(filePath, content) { const violations = [] const lines = content.split('\n') lines.forEach((line, index) => { // Skip comments if (line.trim().startsWith('//') || line.trim().startsWith('*')) { return } // Skip import statements (import aliases are not type casts) if (line.includes('import ') && line.includes(' from ')) { return } // Check each pattern AS_CAST_PATTERNS.forEach(pattern => { const matches = line.matchAll(pattern) for (const match of matches) { // Check if it's not an allowed pattern const isAllowed = ALLOWED_PATTERNS.some(allowed => { if (allowed instanceof RegExp && !allowed.test(filePath)) { return allowed.test(match[0]) } return false }) if (!isAllowed) { violations.push({ line: index + 1, column: match.index + 1, match: match[0], text: line.trim() }) } } }) }) return violations } /** * Check a specific file for as casts */ function checkFileForAsCasts(filePath) { if (!fs.existsSync(filePath)) { return [] } if (shouldExcludeFile(filePath) || isAllowedFile(filePath)) { return [] } const content = fs.readFileSync(filePath, 'utf8') return findAsCasts(filePath, content) } /** * Get list of staged TypeScript files */ function getStagedFiles() { try { const result = execSync('git diff --cached --name-only --diff-filter=ACM', { encoding: 'utf8' }) return result .split('\n') .filter(file => file && (file.endsWith('.ts') || file.endsWith('.tsx'))) .filter(file => !shouldExcludeFile(file)) } catch (_error) { return [] } } /** * Get all TypeScript files (for full check) */ function getAllTypeScriptFiles() { const files = [] /** * Recursively walk directory to find TypeScript files * * @param {string} dir - Directory to walk */ function walkDir(dir) { const entries = fs.readdirSync(dir, { withFileTypes: true }) for (const entry of entries) { const fullPath = path.join(dir, entry.name) if (entry.isDirectory()) { if (!shouldExcludeFile(fullPath)) { walkDir(fullPath) } } else if (entry.isFile()) { if ( (entry.name.endsWith('.ts') || entry.name.endsWith('.tsx')) && !shouldExcludeFile(fullPath) ) { files.push(fullPath) } } } } walkDir('.') return files } /** * Main validation function */ function validateFiles(files, _isStaged = true) { console.log(`${colors.blue}🔍 Checking for 'as' type casts...${colors.reset}`) let totalViolations = 0 const fileViolations = {} for (const file of files) { // Skip allowed files if (isAllowedFile(file)) { continue } try { const content = fs.readFileSync(file, 'utf8') const violations = findAsCasts(file, content) if (violations.length > 0) { fileViolations[file] = violations totalViolations += violations.length } } catch (_error) { console.error(`${colors.yellow}⚠️ Warning: Could not read ${file}${colors.reset}`) } } // Report results if (totalViolations > 0) { console.log(`${colors.red}❌ Found 'as' type cast violations:${colors.reset}\n`) Object.entries(fileViolations).forEach(([file, violations]) => { console.log(`${colors.bold}📁 ${file}:${colors.reset}`) violations.forEach(violation => { console.log(` Line ${violation.line}: ${colors.red}${violation.match}${colors.reset}`) console.log(` ${colors.yellow}${violation.text}${colors.reset}`) }) console.log('') }) console.log( `${colors.red}❌ COMMIT BLOCKED: Found ${totalViolations} 'as' type cast${totalViolations > 1 ? 's' : ''}!${colors.reset}` ) console.log( `${colors.yellow}💡 Replace 'as' casts with proper type guards or type predicates${colors.reset}` ) console.log(`${colors.blue}📋 Examples of better alternatives:${colors.reset}`) console.log(' - Use type guards: if (typeof x === "string") { ... }') console.log(' - Use type predicates: function isString(x: unknown): x is string { ... }') console.log(' - Fix the source type instead of casting') console.log(' - Use generics for flexible types') console.log( ` - If absolutely necessary, use type assertion functions instead${colors.reset}\n` ) process.exit(1) } else { console.log(`${colors.green}✅ No 'as' type casts found${colors.reset}`) } } // Parse command line arguments const args = process.argv.slice(2) const isFullCheck = args.includes('--all') const isFix = args.includes('--fix') if (isFix) { console.log( `${colors.yellow}⚠️ --fix is not supported for as casts (requires manual refactoring)${colors.reset}` ) process.exit(0) } // Get files to check let files const specificFiles = args.filter( arg => !arg.startsWith('--') && (arg.endsWith('.ts') || arg.endsWith('.tsx')) ) if (specificFiles.length > 0) { // Use specific files provided as arguments files = specificFiles.filter(file => fs.existsSync(file)) } else if (isFullCheck) { // Check all TypeScript files files = getAllTypeScriptFiles() } else { // Check only staged files (for git hooks) files = getStagedFiles() } if (files.length === 0) { console.log(`${colors.blue}ℹ️ No TypeScript files to check${colors.reset}`) process.exit(0) } // Run validation (only if called directly) if (require.main === module) { validateFiles(files, !isFullCheck) } // Export for use in ESLint plugin module.exports = { AS_CAST_PATTERNS, ALLOWED_PATTERNS, EXCLUDE_PATHS, checkFileForAsCasts, findAsCasts, shouldExcludeFile, isAllowedFile }

Search equations, builds, manuscript text, and recovered sources.