@@ -200,6 +200,24 @@ function inject() {
200200
201201 // Extract images
202202 if ( config . extractImages !== false ) extractImages ( files , outputDir ) ;
203+
204+ // All links
205+ if ( config . extractAllLinks !== false ) extractAllLinks ( files , outputDir ) ;
206+
207+ // Missing alt
208+ if ( config . missingAlt !== false ) missingAlt ( files , outputDir ) ;
209+
210+ // View transitions
211+ if ( config . viewTransitions !== false ) viewTransitions ( outputDir ) ;
212+
213+ // Keywords
214+ if ( config . extractKeywords !== false ) extractKeywords ( files , outputDir ) ;
215+
216+ // Depth score
217+ if ( config . depthScore !== false ) depthScore ( files , outputDir ) ;
218+
219+ // Validate URLs
220+ if ( config . validateUrls !== false ) validateUrls ( files , outputDir , config . site ?. url ) ;
203221 }
204222 }
205223
@@ -1235,3 +1253,106 @@ function extractImages(files, outputDir) {
12351253 fs . writeFileSync ( path . join ( outputDir , 'images.json' ) , JSON . stringify ( images , null , 2 ) ) ;
12361254 console . log ( 'images.json generated (' + images . length + ')' ) ;
12371255}
1256+
1257+ /**
1258+ * Extract all links (internal + external)
1259+ * href= → all-links.json
1260+ */
1261+ function extractAllLinks ( files , outputDir ) {
1262+ const links = { internal : [ ] , external : [ ] } ;
1263+
1264+ for ( const file of files ) {
1265+ const html = fs . readFileSync ( file , 'utf8' ) ;
1266+ const hrefs = html . match ( / h r e f = " ( [ ^ " ] + ) " / g) || [ ] ;
1267+ for ( const h of hrefs ) {
1268+ const url = h . replace ( 'href="' , '' ) . replace ( '"' , '' ) ;
1269+ if ( url . startsWith ( 'http' ) ) links . external . push ( url ) ;
1270+ else if ( ! url . startsWith ( '#' ) ) links . internal . push ( url ) ;
1271+ }
1272+ }
1273+
1274+ fs . writeFileSync ( path . join ( outputDir , 'all-links.json' ) , JSON . stringify ( links , null , 2 ) ) ;
1275+ console . log ( 'all-links.json generated' ) ;
1276+ }
1277+
1278+ /**
1279+ * Find missing alt text
1280+ * <img> without alt → missing-alt.json
1281+ */
1282+ function missingAlt ( files , outputDir ) {
1283+ const missing = [ ] ;
1284+
1285+ for ( const file of files ) {
1286+ const html = fs . readFileSync ( file , 'utf8' ) ;
1287+ const imgs = html . match ( / < i m g (? ! [ ^ > ] * a l t = ) [ ^ > ] * > / g) ;
1288+ if ( imgs ) missing . push ( ...imgs . map ( i => ( { file : path . basename ( file ) , tag : i . slice ( 0 , 100 ) } ) ) ) ;
1289+ }
1290+
1291+ fs . writeFileSync ( path . join ( outputDir , 'missing-alt.json' ) , JSON . stringify ( missing , null , 2 ) ) ;
1292+ console . log ( 'missing-alt.json generated (' + missing . length + ')' ) ;
1293+ }
1294+
1295+ /**
1296+ * Generate view transitions
1297+ * For SPA-like navigation → view-transitions.json
1298+ */
1299+ function viewTransitions ( outputDir ) {
1300+ const config = { enabled : true , types : [ 'slide' , 'fade' ] } ;
1301+ fs . writeFileSync ( path . join ( outputDir , 'view-transitions.json' ) , JSON . stringify ( config , null , 2 ) ) ;
1302+ console . log ( 'view-transitions.json generated' ) ;
1303+ }
1304+
1305+ /**
1306+ * Extract keywords from content
1307+ * TF-IDF-ish → keywords.json
1308+ */
1309+ function extractKeywords ( files , outputDir ) {
1310+ const wordCount = { } ;
1311+
1312+ for ( const file of files ) {
1313+ const html = fs . readFileSync ( file , 'utf8' ) ;
1314+ const words = html . toLowerCase ( ) . match ( / \b [ a - z ] { 4 , } \b / g) || [ ] ;
1315+ words . forEach ( w => wordCount [ w ] = ( wordCount [ w ] || 0 ) + 1 ) ;
1316+ }
1317+
1318+ const top = Object . entries ( wordCount ) . sort ( ( a , b ) => b [ 1 ] - a [ 1 ] ) . slice ( 0 , 50 ) . map ( ( [ w , c ] ) => ( { word : w , count : c } ) ) ;
1319+ fs . writeFileSync ( path . join ( outputDir , 'keywords.json' ) , JSON . stringify ( top , null , 2 ) ) ;
1320+ console . log ( 'keywords.json generated' ) ;
1321+ }
1322+
1323+ /**
1324+ * Calculate doc depth score
1325+ * URL structure → depth-score.json
1326+ */
1327+ function depthScore ( files , outputDir ) {
1328+ const scores = [ ] ;
1329+
1330+ for ( const file of files ) {
1331+ const rel = path . relative ( outputDir , file ) . replace ( / ^ \/ / , '' ) ;
1332+ const depth = rel . split ( '/' ) . length - 1 ;
1333+ scores . push ( { file : path . basename ( file ) , depth, score : Math . max ( 0 , 100 - depth * 20 ) } ) ;
1334+ }
1335+
1336+ fs . writeFileSync ( path . join ( outputDir , 'depth-score.json' ) , JSON . stringify ( scores , null , 2 ) ) ;
1337+ console . log ( 'depth-score.json generated' ) ;
1338+ }
1339+
1340+ /**
1341+ * Validate URLs in href/src
1342+ * Checks for http codes → url-valid.json
1343+ */
1344+ function validateUrls ( files , outputDir , siteUrl ) {
1345+ const results = [ ] ;
1346+
1347+ for ( const file of files ) {
1348+ const html = fs . readFileSync ( file , 'utf8' ) ;
1349+ const urls = html . match ( / (?: h r e f | s r c ) = " ( h t t p s ? : \/ \/ [ ^ " ] + ) " / g) || [ ] ;
1350+ for ( const u of urls ) {
1351+ const url = u . replace ( / (?: h r e f | s r c ) = " / , '' ) . replace ( / " / , '' ) ;
1352+ results . push ( { url, file : path . basename ( file ) , checked : false } ) ;
1353+ }
1354+ }
1355+
1356+ fs . writeFileSync ( path . join ( outputDir , 'url-valid.json' ) , JSON . stringify ( results . slice ( 0 , 100 ) , null , 2 ) ) ;
1357+ console . log ( 'url-valid.json generated (' + results . length + ')' ) ;
1358+ }
0 commit comments