11import fs from "fs" ;
22import path from "path" ;
3- import { glob } from "glob" ;
3+ import { globSync } from "glob" ;
44
55const CONFIG_FILE = path . join ( process . cwd ( ) , "test" , "dirty-file-paths.json" ) ;
66const BUILD_DIR = path . join ( process . cwd ( ) , "build" ) ;
77
8- // Load and parse the configuration file
8+ /**
9+ * Loads and parses the configuration file for dirty path rules.
10+ * @returns {object[] } The parsed configuration.
11+ */
912function loadConfig ( ) {
13+ if ( ! fs . existsSync ( CONFIG_FILE ) ) {
14+ console . error ( `❌ Error: Configuration file not found at ${ CONFIG_FILE } ` ) ;
15+ process . exit ( 1 ) ;
16+ }
1017 try {
1118 const configContent = fs . readFileSync ( CONFIG_FILE , "utf-8" ) ;
1219 return JSON . parse ( configContent ) ;
1320 } catch ( error ) {
14- console . error ( "Error loading configuration:" , error . message ) ;
21+ console . error ( "❌ Error loading or parsing configuration:" , error . message ) ;
1522 process . exit ( 1 ) ;
1623 }
1724}
1825
19- // Find all files in the build directory
20- function findTargetFiles ( ) {
21- return glob
22- . sync ( "**/*" , {
23- cwd : BUILD_DIR ,
24- nocase : false , // Case sensitive as per requirements
25- dot : false ,
26- } )
27- . filter ( ( file ) => {
28- const fullPath = path . join ( BUILD_DIR , file ) ;
29- return fs . lstatSync ( fullPath ) . isFile ( ) ;
26+ /**
27+ * Gathers target files from command-line arguments or the default build directory.
28+ * @returns {string[] } An array of absolute file paths to check.
29+ */
30+ function getTargetFiles ( ) {
31+ const args = process . argv . slice ( 2 ) ;
32+
33+ // If no arguments, default to all files in the build directory.
34+ if ( args . length === 0 ) {
35+ return globSync ( path . join ( BUILD_DIR , "**" , "*" ) , {
36+ nodir : true ,
37+ absolute : true ,
3038 } ) ;
39+ }
40+
41+ // Process arguments as file paths, directory paths, or glob patterns.
42+ const patterns = args . map ( ( arg ) => {
43+ try {
44+ if ( fs . statSync ( arg ) . isDirectory ( ) ) {
45+ // If it's a directory, create a glob to find all files within it.
46+ return path . join ( arg , "**" , "*" ) ;
47+ }
48+ } catch ( e ) {
49+ // Not a directory or doesn't exist, treat as a file/glob.
50+ }
51+ return arg ;
52+ } ) ;
53+
54+ console . log ( `ℹ️ Searching for files matching: ${ patterns . join ( ", " ) } ` ) ;
55+ return globSync ( patterns , {
56+ nodir : true ,
57+ absolute : true ,
58+ } ) ;
3159}
3260
33- // Convert relative file path to the format expected by rules (starting with /)
61+ /**
62+ * Converts a file path to the format expected by rules (e.g., /path/to/file.html).
63+ * @param {string } filePath - The file path relative to the build directory.
64+ * @returns {string } The normalized path.
65+ */
3466function normalizePathForMatching ( filePath ) {
3567 // Convert backslashes to forward slashes and ensure it starts with /
3668 const normalized = filePath . replace ( / \\ / g, "/" ) ;
3769 return normalized . startsWith ( "/" ) ? normalized : "/" + normalized ;
3870}
3971
40- // Check a single file's path against the dirty path rules
41- function checkFile ( filePath , config ) {
42- const normalizedPath = normalizePathForMatching ( filePath ) ;
43-
44- // Process rules in order, later rules take precedence
72+ /**
73+ * Checks a single file's path against the dirty path rules.
74+ * @param {string } relativeFilePath - The path of the file relative to the build directory.
75+ * @param {object[] } config - The array of rules.
76+ * @returns {object | null } A violation object or null if the path is clean.
77+ */
78+ function checkFile ( relativeFilePath , config ) {
79+ const normalizedPath = normalizePathForMatching ( relativeFilePath ) ;
4580 let finalRule = null ;
4681
4782 for ( const rule of config ) {
@@ -51,12 +86,12 @@ function checkFile(filePath, config) {
5186 finalRule = rule ;
5287 }
5388 } catch ( error ) {
54- console . error ( `Invalid regex pattern in rule: ${ rule . path } ` , error . message ) ;
89+ console . error ( `❌ Invalid regex in rule: ${ rule . path } ` , error . message ) ;
5590 process . exit ( 1 ) ;
5691 }
5792 }
5893
59- // Return violation only if the final matching rule is dirty
94+ // Return a violation only if the final matching rule is ' dirty: true'.
6095 if ( finalRule && finalRule . dirty ) {
6196 return { path : normalizedPath , advice : finalRule . advice } ;
6297 }
@@ -67,32 +102,51 @@ function checkFile(filePath, config) {
67102console . log ( "🧪 Testing files for dirty file paths" ) ;
68103
69104const config = loadConfig ( ) ;
70- const files = findTargetFiles ( ) ;
105+ const absoluteFilePaths = getTargetFiles ( ) ;
71106let hasErrors = false ;
72- const violationsByPath = new Map ( ) ;
107+ const violations = [ ] ;
108+ const cleanFiles = [ ] ;
109+
110+ if ( absoluteFilePaths . length === 0 ) {
111+ console . log ( "⚠️ No files found to check." ) ;
112+ process . exit ( 0 ) ;
113+ }
114+
115+ // Collect all violations and clean files
116+ for ( const absolutePath of absoluteFilePaths ) {
117+ // Rules are based on the path relative to the site root (the build dir).
118+ const relativeToBuildDir = path . relative ( BUILD_DIR , absolutePath ) ;
119+ const violation = checkFile ( relativeToBuildDir , config ) ;
120+
121+ // Use a path relative to the current working directory for user-friendly logging.
122+ const relativeToCwd = path . relative ( process . cwd ( ) , absolutePath ) ;
73123
74- // Collect violations
75- files . forEach ( ( file ) => {
76- const violation = checkFile ( file , config ) ;
77124 if ( violation ) {
78125 hasErrors = true ;
79- console . log ( `❌ ${ file } : ${ violation . advice } ` ) ;
80- violationsByPath . set ( violation . path , violation . advice ) ;
126+ violations . push ( { path : relativeToCwd , advice : violation . advice } ) ;
127+ } else {
128+ cleanFiles . push ( relativeToCwd ) ;
81129 }
82- } ) ;
130+ }
83131
84- // Summary of clean files
85- const cleanFiles = files . filter ( ( file ) => ! checkFile ( file , config ) ) ;
86- console . log ( `✅ Site includes ${ cleanFiles . length } clean files` ) ;
132+ // --- Report results ---
87133
88- // Summary of dirty paths
134+ // First, log all the individual failures.
89135if ( hasErrors ) {
90- console . log ( "Following are dirty file paths: " ) ;
91- violationsByPath . forEach ( ( advice , path ) => {
92- console . log ( `${ path } ${ advice } ` ) ;
136+ console . log ( "\n--- " ) ;
137+ violations . forEach ( ( { path, advice } ) => {
138+ console . log ( `❌ ${ path } : ${ advice } ` ) ;
93139 } ) ;
94- console . error ( "\n❌ Dirty paths check failed" ) ;
140+ console . log ( "---\n" ) ;
141+ }
142+
143+ console . log ( `📊 Results Summary:` ) ;
144+ console . log ( `✅ Found ${ cleanFiles . length } clean file paths.` ) ;
145+
146+ if ( hasErrors ) {
147+ console . log ( `❌ Found ${ violations . length } dirty file paths.` ) ;
148+ console . error ( "\n❌ Dirty paths check failed." ) ;
95149 process . exit ( 1 ) ;
96150} else {
97- console . log ( "✨ All files passed dirty paths check!\n" ) ;
151+ console . log ( "✨ All file paths passed the check!\n" ) ;
98152}
0 commit comments