Source identity
| Field | Recorded value |
|---|---|
| Source type | GitHub source |
| Original record | Janady13/llms-txt-hub / scripts/validation/check-relative-imports.js |
| Date | Not specified in source |
| Repository | Janady13/llms-txt-hub |
| Revision | e0f4bf978d4df168369c7601d9c5f5ea89e84377 |
| Branch | main |
| Path or archive member | scripts/validation/check-relative-imports.js |
| Original SHA-256 | 59978f86c6fef900053f8066326ad5268734de9909615b112dd6325b5954e164 |
| Git blob SHA | 09ba0ea503550ef765bfe55603e911b9e780da94 |
| Reading-copy SHA-256 | 59978f86c6fef900053f8066326ad5268734de9909615b112dd6325b5954e164 |
Recovered source transcript
Download complete source text ↓164 lines · 4.7 KB
#!/usr/bin/env node
/**
* Validates import patterns to enforce consistent path aliases
* Prevents deep relative imports that break on refactoring
*/
// ============================================================================
// CONFIGURATION - Customize these settings for your project
// ============================================================================
// Maximum depth allowed for relative imports (e.g., ../../.. = depth 3)
const MAX_RELATIVE_DEPTH = 2
// Allowed relative import patterns (same directory or one level)
const ALLOWED_RELATIVE_PATTERNS = [
/^\.\/[^/]+$/, // ./file or ./directory
/^\.\/types\//, // ./types/something (local types)
/^\.\/components\//, // ./components/something (local components)
/^\.\/utils\//, // ./utils/something (local utilities)
/^\.\/hooks\// // ./hooks/something (local hooks)
]
// Preferred path aliases (project should use these instead of deep relative)
const PREFERRED_ALIASES = {
components: '@/components', // Use @/components instead of ../../../components
utils: '@/utils', // Use @/utils instead of ../../utils
hooks: '@/hooks', // Use @/hooks instead of ../hooks
types: '@/types', // Use @/types instead of ../../types
lib: '@/lib', // Use @/lib instead of ../lib
styles: '@/styles' // Use @/styles instead of ../../styles
}
// Files to exclude from relative import checking
const _EXCLUDE_PATTERNS = [
/node_modules/,
/\.test\./,
/\.spec\./,
/\.d\.ts$/,
/build\//,
/dist\//,
/\.next\//
]
// ============================================================================
// IMPLEMENTATION - No need to modify below this line
// ============================================================================
const fs = require('node:fs')
/**
* Counts the depth of a relative import by counting '..' segments
*/
function getRelativeDepth(importPath) {
const parts = importPath.split('/')
return parts.filter(part => part === '..').length
}
/**
* Suggests a preferred path alias for a relative import based on configured aliases
*/
function getSuggestedAlias(importPath) {
for (const [key, alias] of Object.entries(PREFERRED_ALIASES)) {
if (importPath.includes(key)) {
return alias
}
}
return null
}
/**
* Checks a file for relative import violations and returns an array of issues
*/
function checkFile(filePath) {
const content = fs.readFileSync(filePath, 'utf-8')
const lines = content.split('\n')
const violations = []
lines.forEach((line, index) => {
// Check for relative imports
const importMatch = line.match(/from\s+['"](\.\.[^'"`]+)['"]/)
if (importMatch) {
const importPath = importMatch[1]
const depth = getRelativeDepth(importPath)
// Check if depth exceeds maximum allowed
if (depth > MAX_RELATIVE_DEPTH) {
violations.push({
line: index + 1,
import: importPath,
text: line.trim(),
reason: `Relative import depth ${depth} exceeds maximum ${MAX_RELATIVE_DEPTH}`
})
return
}
// Check if it's an allowed relative import pattern
const isAllowed = ALLOWED_RELATIVE_PATTERNS.some(pattern => pattern.test(importPath))
if (!isAllowed && !importPath.startsWith('./')) {
// Suggest preferred alias if available
const suggestedAlias = getSuggestedAlias(importPath)
violations.push({
line: index + 1,
import: importPath,
text: line.trim(),
reason: 'Should use path alias instead of deep relative import',
suggestion: suggestedAlias
})
}
}
})
return violations
}
/**
* Entry point that validates relative imports in files passed as CLI arguments
*/
function main() {
const files = process.argv.slice(2)
if (files.length === 0) {
console.log('No files to check')
process.exit(0)
}
let hasViolations = false
const allViolations = []
files.forEach(file => {
if (!fs.existsSync(file)) {
return
}
const violations = checkFile(file)
if (violations.length > 0) {
hasViolations = true
allViolations.push({ file, violations })
}
})
if (hasViolations) {
console.log('❌ Found relative import violations:')
allViolations.forEach(({ file, violations }) => {
console.log(`\n📁 ${file}:`)
violations.forEach(({ line, import: importPath, reason, suggestion }) => {
console.log(` Line ${line}: "${importPath}"`)
console.log(` ${reason}`)
if (suggestion) {
console.log(` 💡 Suggestion: Use "${suggestion}" instead`)
}
})
})
console.log('\n💡 Use "@/" aliases instead of deep relative imports')
process.exit(1)
}
console.log('✅ All imports use proper aliases')
process.exit(0)
}
main()