@intlayer/engine 9.5.9 → 9.5.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (303) hide show
  1. package/dist/cjs/cli.cjs +7 -0
  2. package/dist/cjs/docReview/alignBlocks.cjs +53 -36
  3. package/dist/cjs/docReview/alignBlocks.cjs.map +1 -1
  4. package/dist/cjs/docReview/rebuildDocument.cjs +3 -1
  5. package/dist/cjs/docReview/rebuildDocument.cjs.map +1 -1
  6. package/dist/cjs/docReview/reviewReport.cjs.map +1 -1
  7. package/dist/cjs/docReview/segmentDocument.cjs +8 -5
  8. package/dist/cjs/docReview/segmentDocument.cjs.map +1 -1
  9. package/dist/cjs/init/cms.cjs +35 -0
  10. package/dist/cjs/init/cms.cjs.map +1 -1
  11. package/dist/cjs/init/documentationRouter.cjs +141 -0
  12. package/dist/cjs/init/documentationRouter.cjs.map +1 -0
  13. package/dist/cjs/init/frameworkSetup/backend/index.cjs +138 -0
  14. package/dist/cjs/init/frameworkSetup/backend/index.cjs.map +1 -0
  15. package/dist/cjs/init/frameworkSetup/backend/transforms.cjs +223 -0
  16. package/dist/cjs/init/frameworkSetup/backend/transforms.cjs.map +1 -0
  17. package/dist/cjs/init/frameworkSetup/index.cjs +9 -3
  18. package/dist/cjs/init/frameworkSetup/index.cjs.map +1 -1
  19. package/dist/cjs/init/frameworkSetup/localeRouting.cjs +13 -0
  20. package/dist/cjs/init/frameworkSetup/localeRouting.cjs.map +1 -0
  21. package/dist/cjs/init/frameworkSetup/nextAppRouter/index.cjs +34 -7
  22. package/dist/cjs/init/frameworkSetup/nextAppRouter/index.cjs.map +1 -1
  23. package/dist/cjs/init/frameworkSetup/nextAppRouter/templates.cjs +56 -0
  24. package/dist/cjs/init/frameworkSetup/nextAppRouter/templates.cjs.map +1 -1
  25. package/dist/cjs/init/frameworkSetup/nextAppRouter/transforms.cjs +6 -3
  26. package/dist/cjs/init/frameworkSetup/nextAppRouter/transforms.cjs.map +1 -1
  27. package/dist/cjs/init/frameworkSetup/tanstackStart/index.cjs +32 -3
  28. package/dist/cjs/init/frameworkSetup/tanstackStart/index.cjs.map +1 -1
  29. package/dist/cjs/init/frameworkSetup/tanstackStart/templates.cjs +58 -0
  30. package/dist/cjs/init/frameworkSetup/tanstackStart/templates.cjs.map +1 -1
  31. package/dist/cjs/init/frameworkSetup/tanstackStart/transforms.cjs +94 -14
  32. package/dist/cjs/init/frameworkSetup/tanstackStart/transforms.cjs.map +1 -1
  33. package/dist/cjs/init/index.cjs +46 -159
  34. package/dist/cjs/init/index.cjs.map +1 -1
  35. package/dist/cjs/init/utils/configManipulation.cjs +39 -0
  36. package/dist/cjs/init/utils/configManipulation.cjs.map +1 -1
  37. package/dist/cjs/init/utils/index.cjs +10 -0
  38. package/dist/cjs/init/utils/lintConfig.cjs +52 -0
  39. package/dist/cjs/init/utils/lintConfig.cjs.map +1 -0
  40. package/dist/cjs/init/utils/packageManager.cjs +62 -13
  41. package/dist/cjs/init/utils/packageManager.cjs.map +1 -1
  42. package/dist/cjs/init/utils/urlRoutingFramework.cjs +41 -0
  43. package/dist/cjs/init/utils/urlRoutingFramework.cjs.map +1 -0
  44. package/dist/cjs/initConfig/index.cjs +4 -1
  45. package/dist/cjs/initConfig/index.cjs.map +1 -1
  46. package/dist/cjs/installMCP/installMCP.cjs +1 -1
  47. package/dist/cjs/installMCP/installMCP.cjs.map +1 -1
  48. package/dist/cjs/loadDictionaries/loadLocalDictionaries.cjs +3 -1
  49. package/dist/cjs/loadDictionaries/loadLocalDictionaries.cjs.map +1 -1
  50. package/dist/cjs/loadDictionaries/loadRemoteDictionaries.cjs +1 -1
  51. package/dist/cjs/loadDictionaries/loadRemoteDictionaries.cjs.map +1 -1
  52. package/dist/cjs/loadDictionaries/log.cjs +1 -1
  53. package/dist/cjs/loadDictionaries/log.cjs.map +1 -1
  54. package/dist/cjs/loadDictionaries/logTypeScriptErrors.cjs +111 -43
  55. package/dist/cjs/loadDictionaries/logTypeScriptErrors.cjs.map +1 -1
  56. package/dist/cjs/scan/analyzeBundleContent.cjs +26 -12
  57. package/dist/cjs/scan/analyzeBundleContent.cjs.map +1 -1
  58. package/dist/cjs/scan/calculateScore.cjs +4 -1
  59. package/dist/cjs/scan/calculateScore.cjs.map +1 -1
  60. package/dist/cjs/scan/checks.cjs +394 -221
  61. package/dist/cjs/scan/checks.cjs.map +1 -1
  62. package/dist/cjs/scan/detection/checkDetails.cjs +56 -0
  63. package/dist/cjs/scan/detection/checkDetails.cjs.map +1 -0
  64. package/dist/cjs/scan/detection/classifyInternalLinks.cjs +47 -0
  65. package/dist/cjs/scan/detection/classifyInternalLinks.cjs.map +1 -0
  66. package/dist/cjs/scan/detection/detectRoutingStrategy.cjs +143 -0
  67. package/dist/cjs/scan/detection/detectRoutingStrategy.cjs.map +1 -0
  68. package/dist/cjs/scan/detection/detectTechnologies.cjs +89 -0
  69. package/dist/cjs/scan/detection/detectTechnologies.cjs.map +1 -0
  70. package/dist/cjs/scan/detection/index.cjs +36 -0
  71. package/dist/cjs/scan/detection/localeCode.cjs +118 -0
  72. package/dist/cjs/scan/detection/localeCode.cjs.map +1 -0
  73. package/dist/cjs/scan/detection/localizedPages.cjs +48 -0
  74. package/dist/cjs/scan/detection/localizedPages.cjs.map +1 -0
  75. package/dist/cjs/scan/detection/technologySignatures.cjs +564 -0
  76. package/dist/cjs/scan/detection/technologySignatures.cjs.map +1 -0
  77. package/dist/cjs/scan/detection/url.cjs +53 -0
  78. package/dist/cjs/scan/detection/url.cjs.map +1 -0
  79. package/dist/cjs/scan/fetchText.cjs +56 -0
  80. package/dist/cjs/scan/fetchText.cjs.map +1 -0
  81. package/dist/cjs/scan/index.cjs +59 -3
  82. package/dist/cjs/scan/parseHtml.cjs +55 -10
  83. package/dist/cjs/scan/parseHtml.cjs.map +1 -1
  84. package/dist/cjs/scan/robots.cjs +65 -0
  85. package/dist/cjs/scan/robots.cjs.map +1 -0
  86. package/dist/cjs/scan/runScanChecks.cjs +89 -0
  87. package/dist/cjs/scan/runScanChecks.cjs.map +1 -0
  88. package/dist/cjs/scan/scanWebsite.cjs +58 -52
  89. package/dist/cjs/scan/scanWebsite.cjs.map +1 -1
  90. package/dist/cjs/scan/sitemap.cjs +109 -0
  91. package/dist/cjs/scan/sitemap.cjs.map +1 -0
  92. package/dist/cjs/utils/chunkJSON.cjs +24 -9
  93. package/dist/cjs/utils/chunkJSON.cjs.map +1 -1
  94. package/dist/cjs/utils/getChunk.cjs +2 -2
  95. package/dist/cjs/utils/getChunk.cjs.map +1 -1
  96. package/dist/cjs/utils/runParallel/index.cjs +1 -1
  97. package/dist/cjs/utils/runParallel/index.cjs.map +1 -1
  98. package/dist/cjs/utils/runParallel/pidTree.cjs +23 -11
  99. package/dist/cjs/utils/runParallel/pidTree.cjs.map +1 -1
  100. package/dist/cjs/utils/runParallel/ps.cjs +8 -3
  101. package/dist/cjs/utils/runParallel/ps.cjs.map +1 -1
  102. package/dist/cjs/utils/runParallel/runTask.cjs +2 -1
  103. package/dist/cjs/utils/runParallel/runTask.cjs.map +1 -1
  104. package/dist/cjs/utils/runParallel/wmic.cjs +8 -3
  105. package/dist/cjs/utils/runParallel/wmic.cjs.map +1 -1
  106. package/dist/cjs/writeContentDeclaration/detectExportedComponentName.cjs +7 -4
  107. package/dist/cjs/writeContentDeclaration/detectExportedComponentName.cjs.map +1 -1
  108. package/dist/cjs/writeContentDeclaration/writeMarkdownFile.cjs +2 -2
  109. package/dist/cjs/writeContentDeclaration/writeMarkdownFile.cjs.map +1 -1
  110. package/dist/esm/cli.mjs +5 -3
  111. package/dist/esm/docReview/alignBlocks.mjs +53 -36
  112. package/dist/esm/docReview/alignBlocks.mjs.map +1 -1
  113. package/dist/esm/docReview/rebuildDocument.mjs +3 -1
  114. package/dist/esm/docReview/rebuildDocument.mjs.map +1 -1
  115. package/dist/esm/docReview/reviewReport.mjs.map +1 -1
  116. package/dist/esm/docReview/segmentDocument.mjs +8 -5
  117. package/dist/esm/docReview/segmentDocument.mjs.map +1 -1
  118. package/dist/esm/init/cms.mjs +35 -2
  119. package/dist/esm/init/cms.mjs.map +1 -1
  120. package/dist/esm/init/documentationRouter.mjs +139 -0
  121. package/dist/esm/init/documentationRouter.mjs.map +1 -0
  122. package/dist/esm/init/frameworkSetup/backend/index.mjs +130 -0
  123. package/dist/esm/init/frameworkSetup/backend/index.mjs.map +1 -0
  124. package/dist/esm/init/frameworkSetup/backend/transforms.mjs +216 -0
  125. package/dist/esm/init/frameworkSetup/backend/transforms.mjs.map +1 -0
  126. package/dist/esm/init/frameworkSetup/index.mjs +9 -3
  127. package/dist/esm/init/frameworkSetup/index.mjs.map +1 -1
  128. package/dist/esm/init/frameworkSetup/localeRouting.mjs +12 -0
  129. package/dist/esm/init/frameworkSetup/localeRouting.mjs.map +1 -0
  130. package/dist/esm/init/frameworkSetup/nextAppRouter/index.mjs +36 -9
  131. package/dist/esm/init/frameworkSetup/nextAppRouter/index.mjs.map +1 -1
  132. package/dist/esm/init/frameworkSetup/nextAppRouter/templates.mjs +54 -1
  133. package/dist/esm/init/frameworkSetup/nextAppRouter/templates.mjs.map +1 -1
  134. package/dist/esm/init/frameworkSetup/nextAppRouter/transforms.mjs +6 -3
  135. package/dist/esm/init/frameworkSetup/nextAppRouter/transforms.mjs.map +1 -1
  136. package/dist/esm/init/frameworkSetup/tanstackStart/index.mjs +35 -6
  137. package/dist/esm/init/frameworkSetup/tanstackStart/index.mjs.map +1 -1
  138. package/dist/esm/init/frameworkSetup/tanstackStart/templates.mjs +56 -1
  139. package/dist/esm/init/frameworkSetup/tanstackStart/templates.mjs.map +1 -1
  140. package/dist/esm/init/frameworkSetup/tanstackStart/transforms.mjs +94 -15
  141. package/dist/esm/init/frameworkSetup/tanstackStart/transforms.mjs.map +1 -1
  142. package/dist/esm/init/index.mjs +42 -158
  143. package/dist/esm/init/index.mjs.map +1 -1
  144. package/dist/esm/init/utils/configManipulation.mjs +38 -1
  145. package/dist/esm/init/utils/configManipulation.mjs.map +1 -1
  146. package/dist/esm/init/utils/index.mjs +5 -3
  147. package/dist/esm/init/utils/lintConfig.mjs +50 -0
  148. package/dist/esm/init/utils/lintConfig.mjs.map +1 -0
  149. package/dist/esm/init/utils/packageManager.mjs +60 -14
  150. package/dist/esm/init/utils/packageManager.mjs.map +1 -1
  151. package/dist/esm/init/utils/urlRoutingFramework.mjs +40 -0
  152. package/dist/esm/init/utils/urlRoutingFramework.mjs.map +1 -0
  153. package/dist/esm/initConfig/index.mjs +4 -1
  154. package/dist/esm/initConfig/index.mjs.map +1 -1
  155. package/dist/esm/installMCP/installMCP.mjs +1 -1
  156. package/dist/esm/installMCP/installMCP.mjs.map +1 -1
  157. package/dist/esm/loadDictionaries/loadLocalDictionaries.mjs +3 -1
  158. package/dist/esm/loadDictionaries/loadLocalDictionaries.mjs.map +1 -1
  159. package/dist/esm/loadDictionaries/loadRemoteDictionaries.mjs +1 -1
  160. package/dist/esm/loadDictionaries/loadRemoteDictionaries.mjs.map +1 -1
  161. package/dist/esm/loadDictionaries/log.mjs +1 -1
  162. package/dist/esm/loadDictionaries/log.mjs.map +1 -1
  163. package/dist/esm/loadDictionaries/logTypeScriptErrors.mjs +112 -44
  164. package/dist/esm/loadDictionaries/logTypeScriptErrors.mjs.map +1 -1
  165. package/dist/esm/scan/analyzeBundleContent.mjs +26 -12
  166. package/dist/esm/scan/analyzeBundleContent.mjs.map +1 -1
  167. package/dist/esm/scan/calculateScore.mjs +4 -1
  168. package/dist/esm/scan/calculateScore.mjs.map +1 -1
  169. package/dist/esm/scan/checks.mjs +387 -220
  170. package/dist/esm/scan/checks.mjs.map +1 -1
  171. package/dist/esm/scan/detection/checkDetails.mjs +54 -0
  172. package/dist/esm/scan/detection/checkDetails.mjs.map +1 -0
  173. package/dist/esm/scan/detection/classifyInternalLinks.mjs +46 -0
  174. package/dist/esm/scan/detection/classifyInternalLinks.mjs.map +1 -0
  175. package/dist/esm/scan/detection/detectRoutingStrategy.mjs +141 -0
  176. package/dist/esm/scan/detection/detectRoutingStrategy.mjs.map +1 -0
  177. package/dist/esm/scan/detection/detectTechnologies.mjs +87 -0
  178. package/dist/esm/scan/detection/detectTechnologies.mjs.map +1 -0
  179. package/dist/esm/scan/detection/index.mjs +10 -0
  180. package/dist/esm/scan/detection/localeCode.mjs +108 -0
  181. package/dist/esm/scan/detection/localeCode.mjs.map +1 -0
  182. package/dist/esm/scan/detection/localizedPages.mjs +46 -0
  183. package/dist/esm/scan/detection/localizedPages.mjs.map +1 -0
  184. package/dist/esm/scan/detection/technologySignatures.mjs +563 -0
  185. package/dist/esm/scan/detection/technologySignatures.mjs.map +1 -0
  186. package/dist/esm/scan/detection/url.mjs +47 -0
  187. package/dist/esm/scan/detection/url.mjs.map +1 -0
  188. package/dist/esm/scan/fetchText.mjs +55 -0
  189. package/dist/esm/scan/fetchText.mjs.map +1 -0
  190. package/dist/esm/scan/index.mjs +15 -3
  191. package/dist/esm/scan/parseHtml.mjs +52 -11
  192. package/dist/esm/scan/parseHtml.mjs.map +1 -1
  193. package/dist/esm/scan/robots.mjs +63 -0
  194. package/dist/esm/scan/robots.mjs.map +1 -0
  195. package/dist/esm/scan/runScanChecks.mjs +88 -0
  196. package/dist/esm/scan/runScanChecks.mjs.map +1 -0
  197. package/dist/esm/scan/scanWebsite.mjs +59 -53
  198. package/dist/esm/scan/scanWebsite.mjs.map +1 -1
  199. package/dist/esm/scan/sitemap.mjs +105 -0
  200. package/dist/esm/scan/sitemap.mjs.map +1 -0
  201. package/dist/esm/utils/chunkJSON.mjs +24 -9
  202. package/dist/esm/utils/chunkJSON.mjs.map +1 -1
  203. package/dist/esm/utils/getChunk.mjs +2 -2
  204. package/dist/esm/utils/getChunk.mjs.map +1 -1
  205. package/dist/esm/utils/runParallel/index.mjs +1 -1
  206. package/dist/esm/utils/runParallel/index.mjs.map +1 -1
  207. package/dist/esm/utils/runParallel/pidTree.mjs +23 -11
  208. package/dist/esm/utils/runParallel/pidTree.mjs.map +1 -1
  209. package/dist/esm/utils/runParallel/ps.mjs +8 -3
  210. package/dist/esm/utils/runParallel/ps.mjs.map +1 -1
  211. package/dist/esm/utils/runParallel/runTask.mjs +2 -1
  212. package/dist/esm/utils/runParallel/runTask.mjs.map +1 -1
  213. package/dist/esm/utils/runParallel/wmic.mjs +8 -3
  214. package/dist/esm/utils/runParallel/wmic.mjs.map +1 -1
  215. package/dist/esm/writeContentDeclaration/detectExportedComponentName.mjs +7 -4
  216. package/dist/esm/writeContentDeclaration/detectExportedComponentName.mjs.map +1 -1
  217. package/dist/esm/writeContentDeclaration/writeMarkdownFile.mjs +2 -2
  218. package/dist/esm/writeContentDeclaration/writeMarkdownFile.mjs.map +1 -1
  219. package/dist/types/cli.d.ts +5 -3
  220. package/dist/types/docReview/rebuildDocument.d.ts.map +1 -1
  221. package/dist/types/docReview/reviewReport.d.ts.map +1 -1
  222. package/dist/types/docReview/segmentDocument.d.ts.map +1 -1
  223. package/dist/types/formatDictionary.d.ts +2 -2
  224. package/dist/types/init/cms.d.ts +11 -0
  225. package/dist/types/init/cms.d.ts.map +1 -1
  226. package/dist/types/init/documentationRouter.d.ts +64 -0
  227. package/dist/types/init/documentationRouter.d.ts.map +1 -0
  228. package/dist/types/init/frameworkSetup/backend/index.d.ts +16 -0
  229. package/dist/types/init/frameworkSetup/backend/index.d.ts.map +1 -0
  230. package/dist/types/init/frameworkSetup/backend/transforms.d.ts +37 -0
  231. package/dist/types/init/frameworkSetup/backend/transforms.d.ts.map +1 -0
  232. package/dist/types/init/frameworkSetup/index.d.ts.map +1 -1
  233. package/dist/types/init/frameworkSetup/localeRouting.d.ts +11 -0
  234. package/dist/types/init/frameworkSetup/localeRouting.d.ts.map +1 -0
  235. package/dist/types/init/frameworkSetup/nextAppRouter/index.d.ts +5 -5
  236. package/dist/types/init/frameworkSetup/nextAppRouter/index.d.ts.map +1 -1
  237. package/dist/types/init/frameworkSetup/nextAppRouter/templates.d.ts +12 -0
  238. package/dist/types/init/frameworkSetup/nextAppRouter/templates.d.ts.map +1 -1
  239. package/dist/types/init/frameworkSetup/nextAppRouter/transforms.d.ts +10 -1
  240. package/dist/types/init/frameworkSetup/nextAppRouter/transforms.d.ts.map +1 -1
  241. package/dist/types/init/frameworkSetup/tanstackStart/index.d.ts +3 -2
  242. package/dist/types/init/frameworkSetup/tanstackStart/index.d.ts.map +1 -1
  243. package/dist/types/init/frameworkSetup/tanstackStart/templates.d.ts +10 -0
  244. package/dist/types/init/frameworkSetup/tanstackStart/templates.d.ts.map +1 -1
  245. package/dist/types/init/frameworkSetup/tanstackStart/transforms.d.ts +12 -0
  246. package/dist/types/init/frameworkSetup/tanstackStart/transforms.d.ts.map +1 -1
  247. package/dist/types/init/frameworkSetup/types.d.ts +6 -0
  248. package/dist/types/init/frameworkSetup/types.d.ts.map +1 -1
  249. package/dist/types/init/index.d.ts +15 -1
  250. package/dist/types/init/index.d.ts.map +1 -1
  251. package/dist/types/init/utils/configManipulation.d.ts +14 -0
  252. package/dist/types/init/utils/configManipulation.d.ts.map +1 -1
  253. package/dist/types/init/utils/index.d.ts +5 -3
  254. package/dist/types/init/utils/lintConfig.d.ts +28 -0
  255. package/dist/types/init/utils/lintConfig.d.ts.map +1 -0
  256. package/dist/types/init/utils/packageManager.d.ts +29 -6
  257. package/dist/types/init/utils/packageManager.d.ts.map +1 -1
  258. package/dist/types/init/utils/urlRoutingFramework.d.ts +9 -0
  259. package/dist/types/init/utils/urlRoutingFramework.d.ts.map +1 -0
  260. package/dist/types/initConfig/index.d.ts +3 -1
  261. package/dist/types/initConfig/index.d.ts.map +1 -1
  262. package/dist/types/loadDictionaries/logTypeScriptErrors.d.ts +4 -0
  263. package/dist/types/loadDictionaries/logTypeScriptErrors.d.ts.map +1 -1
  264. package/dist/types/scan/analyzeBundleContent.d.ts.map +1 -1
  265. package/dist/types/scan/calculateScore.d.ts +4 -1
  266. package/dist/types/scan/calculateScore.d.ts.map +1 -1
  267. package/dist/types/scan/checks.d.ts +114 -21
  268. package/dist/types/scan/checks.d.ts.map +1 -1
  269. package/dist/types/scan/detection/checkDetails.d.ts +29 -0
  270. package/dist/types/scan/detection/checkDetails.d.ts.map +1 -0
  271. package/dist/types/scan/detection/classifyInternalLinks.d.ts +44 -0
  272. package/dist/types/scan/detection/classifyInternalLinks.d.ts.map +1 -0
  273. package/dist/types/scan/detection/detectRoutingStrategy.d.ts +60 -0
  274. package/dist/types/scan/detection/detectRoutingStrategy.d.ts.map +1 -0
  275. package/dist/types/scan/detection/detectTechnologies.d.ts +60 -0
  276. package/dist/types/scan/detection/detectTechnologies.d.ts.map +1 -0
  277. package/dist/types/scan/detection/index.d.ts +9 -0
  278. package/dist/types/scan/detection/localeCode.d.ts +49 -0
  279. package/dist/types/scan/detection/localeCode.d.ts.map +1 -0
  280. package/dist/types/scan/detection/localizedPages.d.ts +40 -0
  281. package/dist/types/scan/detection/localizedPages.d.ts.map +1 -0
  282. package/dist/types/scan/detection/technologySignatures.d.ts +48 -0
  283. package/dist/types/scan/detection/technologySignatures.d.ts.map +1 -0
  284. package/dist/types/scan/detection/url.d.ts +27 -0
  285. package/dist/types/scan/detection/url.d.ts.map +1 -0
  286. package/dist/types/scan/fetchText.d.ts +30 -0
  287. package/dist/types/scan/fetchText.d.ts.map +1 -0
  288. package/dist/types/scan/index.d.ts +16 -4
  289. package/dist/types/scan/parseHtml.d.ts +17 -0
  290. package/dist/types/scan/parseHtml.d.ts.map +1 -1
  291. package/dist/types/scan/robots.d.ts +23 -0
  292. package/dist/types/scan/robots.d.ts.map +1 -0
  293. package/dist/types/scan/runScanChecks.d.ts +43 -0
  294. package/dist/types/scan/runScanChecks.d.ts.map +1 -0
  295. package/dist/types/scan/scanWebsite.d.ts.map +1 -1
  296. package/dist/types/scan/sitemap.d.ts +49 -0
  297. package/dist/types/scan/sitemap.d.ts.map +1 -0
  298. package/dist/types/scan/types.d.ts +15 -0
  299. package/dist/types/scan/types.d.ts.map +1 -1
  300. package/dist/types/utils/chunkJSON.d.ts.map +1 -1
  301. package/dist/types/utils/runParallel/pidTree.d.ts.map +1 -1
  302. package/dist/types/writeContentDeclaration/writeMarkdownFile.d.ts.map +1 -1
  303. package/package.json +16 -8
@@ -1,273 +1,446 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+ const require_scan_fetchText = require('./fetchText.cjs');
3
+ const require_scan_detection_url = require('./detection/url.cjs');
2
4
  const require_scan_parseHtml = require('./parseHtml.cjs');
3
5
  const require_scan_analyzeBundleContent = require('./analyzeBundleContent.cjs');
4
- let _intlayer_types_allLocales = require("@intlayer/types/allLocales");
6
+ const require_scan_detection_localeCode = require('./detection/localeCode.cjs');
7
+ const require_scan_detection_detectRoutingStrategy = require('./detection/detectRoutingStrategy.cjs');
8
+ const require_scan_detection_classifyInternalLinks = require('./detection/classifyInternalLinks.cjs');
9
+ const require_scan_robots = require('./robots.cjs');
10
+ const require_scan_sitemap = require('./sitemap.cjs');
5
11
 
6
12
  //#region src/scan/checks.ts
13
+ /** Maximum number of links listed in a check's details. */
14
+ const MAX_LISTED_LINKS = 30;
15
+ /** Maximum number of hreflang alternates fetched for the reciprocity check. */
16
+ const MAX_FETCHED_ALTERNATES = 8;
17
+ /** Statuses meaning the site refuses automated requests, not a broken page. */
18
+ const BOT_BLOCKING_STATUSES = /* @__PURE__ */ new Set([
19
+ 401,
20
+ 403,
21
+ 429
22
+ ]);
7
23
  /** Format a byte count as a human-readable size. */
8
24
  const formatSize = (bytes) => {
9
25
  if (bytes >= 1048576) return `${(bytes / 1048576).toFixed(2)} MB`;
10
26
  if (bytes >= 1024) return `${(bytes / 1024).toFixed(2)} KB`;
11
27
  return `${bytes} B`;
12
28
  };
29
+ /** Build a URL-scoped check type (`url_htmlLang\https://…`). */
30
+ const urlCheckType = (checkName, targetUrl) => `${checkName}\\${targetUrl}`;
31
+ /** Emit an event whose details are attached to its own status. */
32
+ const emitCheck = (emit, type, status, details) => emit({
33
+ type,
34
+ status,
35
+ details: details === void 0 ? void 0 : { [status]: details }
36
+ });
37
+ /** Render an anchor as an HTML line for the details popover. */
38
+ const formatAnchor = ({ href, text }) => `<a href="${href}">${text.slice(0, 60) || "Link"}</a>`;
39
+ /** `url_htmlLang`: `<html lang>` is present and a valid BCP 47 tag. */
40
+ const checkHtmlLang = ({ targetUrl, langTag }, emit) => {
41
+ const type = urlCheckType("url_htmlLang", targetUrl);
42
+ if (!langTag) emitCheck(emit, type, "error", "Missing html lang attribute");
43
+ else if (!require_scan_detection_localeCode.isValidLocaleCode(langTag)) emitCheck(emit, type, "error", `"${langTag}" is not a valid BCP 47 language tag (expected e.g. "en" or "en-US")`);
44
+ else emitCheck(emit, type, "success", langTag);
45
+ };
13
46
  /**
14
- * Check the `<html>` element attributes (`lang`, `dir`) and the resulting
15
- * current-locale signal. Returns the detected language tag.
47
+ * `url_htmlDir`: the text direction matches the language. `dir` is only
48
+ * required for right-to-left languages, `ltr` being the browser default.
16
49
  */
17
- const checkHtmlAttributes = (html, targetUrl, events) => {
18
- const langTag = require_scan_parseHtml.extractHtmlLang(html);
19
- const dirTag = require_scan_parseHtml.extractHtmlDir(html);
20
- events.push({
21
- type: `url_htmlLang\\${targetUrl}`,
22
- status: langTag ? "success" : "error",
23
- details: {
24
- success: langTag,
25
- error: langTag ? void 0 : "Missing html lang attribute"
26
- }
27
- });
28
- events.push({
29
- type: `url_currentLocale\\${targetUrl}`,
30
- status: langTag ? "success" : "warning",
31
- details: {
32
- success: langTag,
33
- warning: langTag ? void 0 : "No locale detected"
34
- }
35
- });
36
- events.push({
37
- type: `url_htmlDir\\${targetUrl}`,
38
- status: dirTag ? "success" : "warning",
39
- details: {
40
- success: dirTag,
41
- warning: dirTag ? void 0 : "Missing html dir attribute"
42
- }
43
- });
44
- return { langTag };
50
+ const checkHtmlDir = ({ targetUrl, langTag, dirTag }, emit) => {
51
+ const type = urlCheckType("url_htmlDir", targetUrl);
52
+ const direction = dirTag?.toLowerCase();
53
+ if (!langTag) {
54
+ emitCheck(emit, type, "warning", "Cannot verify the text direction: html lang is missing");
55
+ return;
56
+ }
57
+ const expectedDirection = require_scan_detection_localeCode.isRightToLeftLocale(langTag) ? "rtl" : "ltr";
58
+ if (direction === expectedDirection || !direction && expectedDirection === "ltr") {
59
+ emitCheck(emit, type, "success", direction ?? "ltr (browser default)");
60
+ return;
61
+ }
62
+ emitCheck(emit, type, "error", direction ? `dir="${direction}" does not match the ${expectedDirection.toUpperCase()} language "${langTag}"` : `Missing dir="rtl" for the right-to-left language "${langTag}"`);
45
63
  };
46
- /** Check the presence of a canonical link. */
47
- const checkCanonical = (html, targetUrl, events) => {
48
- const present = require_scan_parseHtml.hasCanonical(html);
49
- events.push({
50
- type: `url_hasCanonical\\${targetUrl}`,
51
- status: present ? "success" : "warning",
52
- details: { warning: present ? void 0 : "Missing canonical link" }
64
+ /**
65
+ * `url_currentLocale`: the locale announced by `<html lang>`, the URL and the
66
+ * self hreflang entry agree.
67
+ */
68
+ const checkLocaleConsistency = ({ targetUrl, langTag, hreflangs, routing }, emit) => {
69
+ const type = urlCheckType("url_currentLocale", targetUrl);
70
+ const normalizedTargetUrl = require_scan_detection_url.normalizeUrl(targetUrl);
71
+ const selfHreflang = hreflangs.find(({ hreflang, href }) => hreflang.toLowerCase() !== "x-default" && require_scan_detection_url.normalizeUrl(href, targetUrl) === normalizedTargetUrl)?.hreflang;
72
+ const signals = {
73
+ htmlLang: langTag,
74
+ urlLocale: routing.urlLocale,
75
+ selfHreflang
76
+ };
77
+ const definedLocales = Object.values(signals).filter((locale) => Boolean(locale));
78
+ if (definedLocales.length === 0) {
79
+ emitCheck(emit, type, "warning", "No locale detected on the page");
80
+ return;
81
+ }
82
+ const firstLocale = definedLocales[0];
83
+ const isConsistent = definedLocales.every((locale) => require_scan_detection_localeCode.isSameLanguage(locale, firstLocale));
84
+ emitCheck(emit, type, isConsistent ? "success" : "error", isConsistent ? signals : {
85
+ message: "The page announces different locales in html lang, its URL and its hreflang entry",
86
+ ...signals
53
87
  });
54
88
  };
89
+ /** Describe an invalid Open Graph locale, with the value to use instead. */
90
+ const formatInvalidOpenGraphLocale = (localeCode, langTag, property) => {
91
+ const suggestion = require_scan_detection_localeCode.toOpenGraphLocale(localeCode, langTag);
92
+ return `${property} "${localeCode}" is not a valid Open Graph locale (expected language_TERRITORY${suggestion ? `, e.g. "${suggestion}"` : ""}): crawlers ignore it and fall back to en_US`;
93
+ };
55
94
  /**
56
- * Check the page's hreflang structure and collect discovered locales into
57
- * `localesSet`.
95
+ * `url_ogLocale`: `og:locale` is set, uses the Open Graph `language_TERRITORY`
96
+ * format (`en_GB`, not `en` nor `en-GB`) and matches `<html lang>`.
97
+ * `og:locale:alternate` values must use the same format.
58
98
  */
59
- const checkLinguisticStructure = (html, targetUrl, localesSet, events) => {
60
- const langTag = require_scan_parseHtml.extractHtmlLang(html);
61
- if (langTag) localesSet.add(langTag);
62
- const hreflangs = require_scan_parseHtml.extractHreflangs(html);
63
- for (const { hreflang } of hreflangs) if (hreflang !== "x-default") localesSet.add(hreflang);
64
- const hasXDefault = hreflangs.some((h) => h.hreflang === "x-default");
65
- events.push({
66
- type: `url_hreflang\\${targetUrl}`,
67
- status: hreflangs.length > 0 ? "success" : "warning",
68
- details: {
69
- success: hreflangs.length > 0 ? hreflangs : void 0,
70
- warning: hreflangs.length === 0 ? "No hreflang tags found" : void 0
71
- }
72
- });
73
- events.push({
74
- type: `url_hasXDefault\\${targetUrl}`,
75
- status: hasXDefault ? "success" : "error",
76
- details: { error: hasXDefault ? void 0 : "Missing x-default hreflang link" }
99
+ const checkOgLocale = ({ targetUrl, ogLocale, ogLocaleAlternates = [], langTag }, emit) => {
100
+ const type = urlCheckType("url_ogLocale", targetUrl);
101
+ if (!ogLocale) {
102
+ emitCheck(emit, type, "warning", "Missing <meta property=\"og:locale\">: social previews default to en_US");
103
+ return;
104
+ }
105
+ if (!require_scan_detection_localeCode.isValidOpenGraphLocale(ogLocale)) {
106
+ emitCheck(emit, type, "error", formatInvalidOpenGraphLocale(ogLocale, langTag, "og:locale"));
107
+ return;
108
+ }
109
+ if (langTag && !require_scan_detection_localeCode.isSameLanguage(ogLocale, langTag)) {
110
+ emitCheck(emit, type, "warning", `og:locale "${ogLocale}" does not match html lang "${langTag}"`);
111
+ return;
112
+ }
113
+ const invalidAlternates = ogLocaleAlternates.filter((alternate) => !require_scan_detection_localeCode.isValidOpenGraphLocale(alternate));
114
+ if (invalidAlternates.length > 0) {
115
+ emitCheck(emit, type, "warning", invalidAlternates.map((alternate) => formatInvalidOpenGraphLocale(alternate, void 0, "og:locale:alternate")).join("\n"));
116
+ return;
117
+ }
118
+ emitCheck(emit, type, "success", ogLocale);
119
+ };
120
+ /**
121
+ * `url_hasCanonical`: a canonical link exists and does not point to another
122
+ * locale version (which would de-index the current one).
123
+ */
124
+ const checkCanonical = ({ targetUrl, canonicalHref, hreflangs, routing, pageLocale }, emit) => {
125
+ const type = urlCheckType("url_hasCanonical", targetUrl);
126
+ if (canonicalHref === void 0 || canonicalHref === "") {
127
+ emitCheck(emit, type, "warning", "Missing canonical link");
128
+ return;
129
+ }
130
+ const canonicalUrl = require_scan_detection_url.normalizeUrl(canonicalHref, targetUrl);
131
+ const otherLocaleAlternate = hreflangs.find(({ hreflang, href }) => hreflang.toLowerCase() !== "x-default" && require_scan_detection_url.normalizeUrl(href, targetUrl) === canonicalUrl && !require_scan_detection_localeCode.isSameLanguage(hreflang, pageLocale));
132
+ const canonicalLocale = require_scan_detection_detectRoutingStrategy.getUrlLocale(canonicalUrl, routing);
133
+ if (otherLocaleAlternate || canonicalLocale && pageLocale && !require_scan_detection_localeCode.isSameLanguage(canonicalLocale, pageLocale)) {
134
+ emitCheck(emit, type, "error", {
135
+ message: `The canonical URL points to the "${otherLocaleAlternate?.hreflang ?? canonicalLocale}" version instead of this "${pageLocale}" page: search engines will drop this page from their index`,
136
+ canonical: canonicalUrl
137
+ });
138
+ return;
139
+ }
140
+ emitCheck(emit, type, "success", canonicalUrl === require_scan_detection_url.normalizeUrl(targetUrl) ? canonicalUrl : {
141
+ message: "Canonical points to another URL of the same locale",
142
+ canonical: canonicalUrl
77
143
  });
78
144
  };
79
- const normalizeHost = (host) => host.startsWith("www.") ? host.slice(4) : host;
80
- const localeValues = Object.values(_intlayer_types_allLocales.ALL_LOCALES);
81
145
  /**
82
- * Check whether internal links carry a locale segment, mirroring the hosted
83
- * audit's URL-structure analysis but operating on parsed anchors instead of a
84
- * live DOM.
146
+ * `url_hreflang`: hreflang tags exist, use valid codes and absolute URLs, have
147
+ * no duplicates, and include a self reference.
85
148
  */
86
- const checkUrlStructure = (anchors, origin, targetUrl, events) => {
87
- const targetHostname = normalizeHost(new URL(origin).hostname);
88
- let localizedCount = 0;
89
- let totalInternalCount = 0;
90
- const nonLocalizedLinks = [];
91
- for (const { href } of anchors) {
92
- if (!href || href.startsWith("#") || href.startsWith("javascript:")) continue;
93
- try {
94
- const url = new URL(href, origin);
95
- if (normalizeHost(url.hostname) !== targetHostname) continue;
96
- totalInternalCount++;
97
- const path = url.pathname.toLowerCase();
98
- const hostname = url.hostname.toLowerCase();
99
- const hasLocaleInPath = localeValues.some((locale) => {
100
- const l = locale.toLowerCase();
101
- return path === `/${l}` || path.includes(`/${l}/`);
102
- });
103
- const hasLocaleInSubdomain = localeValues.some((locale) => {
104
- const l = locale.toLowerCase();
105
- return hostname.startsWith(`${l}.`) || hostname.includes(`.${l}.`);
106
- });
107
- if (hasLocaleInPath || hasLocaleInSubdomain) localizedCount++;
108
- else nonLocalizedLinks.push(href);
109
- } catch {}
149
+ const checkHreflang = ({ targetUrl, hreflangs, canonicalHref }, emit) => {
150
+ const type = urlCheckType("url_hreflang", targetUrl);
151
+ if (hreflangs.length === 0) {
152
+ emitCheck(emit, type, "warning", "No hreflang tags found");
153
+ return;
110
154
  }
111
- const hasLocalizedLinks = localizedCount > 0;
112
- const allAnchorsLocalized = totalInternalCount === 0 || localizedCount === totalInternalCount;
113
- events.push({
114
- type: `url_hasLocalizedLinks\\${targetUrl}`,
115
- status: hasLocalizedLinks ? "success" : "warning",
116
- details: {
117
- success: hasLocalizedLinks ? `${localizedCount} localized links found out of ${totalInternalCount} internal links` : void 0,
118
- warning: hasLocalizedLinks ? void 0 : "No localized links found"
119
- }
155
+ const issues = [];
156
+ const invalidCodes = hreflangs.map(({ hreflang }) => hreflang).filter((hreflang) => !require_scan_detection_localeCode.isValidLocaleCode(hreflang));
157
+ if (invalidCodes.length > 0) issues.push(`Invalid hreflang codes: ${invalidCodes.join(", ")}`);
158
+ const ukCodes = hreflangs.map(({ hreflang }) => hreflang).filter((hreflang) => /-uk$/i.test(hreflang));
159
+ if (ukCodes.length > 0) issues.push(`"${ukCodes.join(", ")}": the United Kingdom region code is "GB" (e.g. en-GB)`);
160
+ const relativeHrefs = hreflangs.filter(({ href }) => !require_scan_detection_url.isAbsoluteUrl(href));
161
+ if (relativeHrefs.length > 0) issues.push(`hreflang URLs must be absolute (https://…), search engines ignore: ${relativeHrefs.map(({ hreflang, href }) => `${hreflang} → ${href}`).join(", ")}`);
162
+ const urlsByCode = /* @__PURE__ */ new Map();
163
+ for (const { hreflang, href } of hreflangs) {
164
+ const code = require_scan_detection_localeCode.normalizeLocaleCode(hreflang);
165
+ const urls = urlsByCode.get(code) ?? /* @__PURE__ */ new Set();
166
+ urls.add(require_scan_detection_url.normalizeUrl(href, targetUrl));
167
+ urlsByCode.set(code, urls);
168
+ }
169
+ const conflictingCodes = [...urlsByCode.entries()].filter(([, urls]) => urls.size > 1).map(([code]) => code);
170
+ if (conflictingCodes.length > 0) issues.push(`Same hreflang declared with different URLs: ${conflictingCodes.join(", ")}`);
171
+ const selfUrls = /* @__PURE__ */ new Set([require_scan_detection_url.normalizeUrl(targetUrl)]);
172
+ if (canonicalHref) selfUrls.add(require_scan_detection_url.normalizeUrl(canonicalHref, targetUrl));
173
+ if (!hreflangs.some(({ hreflang, href }) => hreflang.toLowerCase() !== "x-default" && selfUrls.has(require_scan_detection_url.normalizeUrl(href, targetUrl)))) issues.push("No hreflang entry points to the page itself (self reference)");
174
+ const status = relativeHrefs.length > 0 ? "error" : issues.length > 0 ? "warning" : "success";
175
+ emitCheck(emit, type, status, issues.length > 0 ? {
176
+ issues,
177
+ hreflangs
178
+ } : hreflangs);
179
+ };
180
+ /** `url_hasXDefault`: an `x-default` hreflang is declared. */
181
+ const checkXDefault = ({ targetUrl, hreflangs }, emit) => {
182
+ const type = urlCheckType("url_hasXDefault", targetUrl);
183
+ const xDefault = hreflangs.find(({ hreflang }) => hreflang.toLowerCase() === "x-default");
184
+ if (xDefault) emitCheck(emit, type, "success", xDefault.href);
185
+ else if (hreflangs.length === 0) emitCheck(emit, type, "warning", "No hreflang tags, so no x-default either");
186
+ else emitCheck(emit, type, "error", "Missing x-default hreflang link");
187
+ };
188
+ /** Fetch an alternate and check it forms a valid hreflang pair with the page. */
189
+ const verifyAlternate = async (alternateUrl, hreflang, selfUrls, fetchOptions) => {
190
+ const response = await require_scan_fetchText.fetchText(alternateUrl, fetchOptions);
191
+ const verification = {
192
+ url: alternateUrl,
193
+ hreflang
194
+ };
195
+ if (!response || BOT_BLOCKING_STATUSES.has(response.status)) return {
196
+ ...verification,
197
+ outcome: "unverifiable",
198
+ issues: []
199
+ };
200
+ if (!response.ok) return {
201
+ ...verification,
202
+ outcome: "broken",
203
+ issues: [`HTTP ${response.status}`]
204
+ };
205
+ const issues = [];
206
+ if (response.redirected) issues.push(`redirects to ${response.finalUrl}`);
207
+ if (!require_scan_parseHtml.extractHreflangs(response.text).some(({ href }) => selfUrls.has(require_scan_detection_url.normalizeUrl(href, response.finalUrl)))) issues.push("no hreflang link back to this page");
208
+ const alternateLang = require_scan_parseHtml.extractHtmlLang(response.text);
209
+ if (alternateLang && !require_scan_detection_localeCode.isSameLanguage(alternateLang, hreflang)) issues.push(`html lang is "${alternateLang}"`);
210
+ return {
211
+ ...verification,
212
+ outcome: issues.length > 0 ? "invalid" : "valid",
213
+ issues
214
+ };
215
+ };
216
+ /**
217
+ * `url_hreflangReciprocal`: every alternate answers with a 200, is not
218
+ * redirected, links back to this page and declares the expected language.
219
+ * Search engines ignore hreflang pairs without a return link.
220
+ */
221
+ const checkHreflangReciprocity = async ({ targetUrl, hreflangs, canonicalHref }, fetchOptions, emit) => {
222
+ const type = urlCheckType("url_hreflangReciprocal", targetUrl);
223
+ const selfUrls = /* @__PURE__ */ new Set([require_scan_detection_url.normalizeUrl(targetUrl)]);
224
+ if (canonicalHref) selfUrls.add(require_scan_detection_url.normalizeUrl(canonicalHref, targetUrl));
225
+ const alternatesByUrl = /* @__PURE__ */ new Map();
226
+ for (const { hreflang, href } of hreflangs) {
227
+ if (hreflang.toLowerCase() === "x-default") continue;
228
+ const absoluteUrl = require_scan_detection_url.parseUrl(href, targetUrl)?.href;
229
+ if (!absoluteUrl || selfUrls.has(require_scan_detection_url.normalizeUrl(absoluteUrl))) continue;
230
+ if (!alternatesByUrl.has(absoluteUrl)) alternatesByUrl.set(absoluteUrl, hreflang);
231
+ }
232
+ if (alternatesByUrl.size === 0) return;
233
+ const verifications = await Promise.all([...alternatesByUrl.entries()].slice(0, MAX_FETCHED_ALTERNATES).map(([alternateUrl, hreflang]) => verifyAlternate(alternateUrl, hreflang, selfUrls, fetchOptions)));
234
+ const unverifiableUrls = verifications.filter(({ outcome }) => outcome === "unverifiable").map(({ url }) => url);
235
+ const failingVerifications = verifications.filter(({ outcome }) => outcome === "broken" || outcome === "invalid");
236
+ const verifiedCount = verifications.length - unverifiableUrls.length;
237
+ if (verifiedCount === 0) {
238
+ emitCheck(emit, type, "warning", {
239
+ message: "The alternates could not be verified: they are unreachable or block automated requests",
240
+ urls: unverifiableUrls
241
+ });
242
+ return;
243
+ }
244
+ if (failingVerifications.length === 0) {
245
+ emitCheck(emit, type, "success", `${verifiedCount} alternates checked, all reciprocal`);
246
+ return;
247
+ }
248
+ emitCheck(emit, type, failingVerifications.some(({ outcome }) => outcome === "broken") ? "error" : "warning", {
249
+ message: `${failingVerifications.length} of ${verifiedCount} checked hreflang alternates are not valid return pairs`,
250
+ alternates: failingVerifications.map(({ hreflang, url, issues }) => `${hreflang} ${url}: ${issues.join(", ")}`),
251
+ unverifiableUrls
120
252
  });
121
- events.push({
122
- type: `url_allAnchorsLocalized\\${targetUrl}`,
123
- status: allAnchorsLocalized ? "success" : "warning",
124
- details: { warning: allAnchorsLocalized ? void 0 : {
125
- message: "Some internal links are not localized",
126
- links: nonLocalizedLinks
127
- } }
253
+ };
254
+ /**
255
+ * Link checks, interpreted through the detected routing strategy (so an
256
+ * unprefixed link is correct on the default locale of a `prefix-no-default`
257
+ * site, and `?lang=` links are understood on `search-params` sites):
258
+ *
259
+ * - `url_hasLocalizedLinks`: internal links keep the page locale
260
+ * - `url_allAnchorsLocalized`: no internal link loses or switches the locale
261
+ * - `url_hasLangSelector`: crawlable links to the other locale versions exist
262
+ */
263
+ const checkInternalLinks = (signals, emit) => {
264
+ const { targetUrl, routing, pageLocale } = signals;
265
+ const { internalLinks, sameLocaleLinks, otherLocaleLinks, unlocalizedLinks, switcherLinks } = require_scan_detection_classifyInternalLinks.classifyInternalLinks(signals);
266
+ const listLinks = (links) => links.slice(0, MAX_LISTED_LINKS).map(formatAnchor);
267
+ const localizedLinksType = urlCheckType("url_hasLocalizedLinks", targetUrl);
268
+ const isLocaleInUrl = routing.strategy !== "no-prefix" && routing.strategy !== "unknown";
269
+ if (internalLinks.length === 0) emitCheck(emit, localizedLinksType, "warning", {
270
+ message: "No internal links found on the page",
271
+ links: listLinks(switcherLinks)
272
+ });
273
+ else if (!isLocaleInUrl || !pageLocale) emitCheck(emit, localizedLinksType, "warning", {
274
+ message: `The locale is not reflected in the URLs (routing: ${routing.strategy}). Each locale needs its own crawlable URL to be indexed.`,
275
+ links: listLinks(internalLinks)
276
+ });
277
+ else if (sameLocaleLinks.length === 0) emitCheck(emit, localizedLinksType, "warning", {
278
+ message: `None of the ${internalLinks.length} internal links points to the "${pageLocale}" version (routing: ${routing.strategy})`,
279
+ links: listLinks(internalLinks)
280
+ });
281
+ else emitCheck(emit, localizedLinksType, "success", {
282
+ message: `${sameLocaleLinks.length} of ${internalLinks.length} internal links point to the "${pageLocale}" version (routing: ${routing.strategy})`,
283
+ links: listLinks(sameLocaleLinks)
128
284
  });
285
+ if (isLocaleInUrl && pageLocale && internalLinks.length > 0) {
286
+ const brokenLinks = [...otherLocaleLinks, ...unlocalizedLinks];
287
+ emitCheck(emit, urlCheckType("url_allAnchorsLocalized", targetUrl), brokenLinks.length === 0 ? "success" : "warning", brokenLinks.length === 0 ? `All ${internalLinks.length} internal links keep the "${pageLocale}" locale` : {
288
+ message: `${brokenLinks.length} internal links leave the "${pageLocale}" locale (${otherLocaleLinks.length} to another locale, ${unlocalizedLinks.length} without locale)`,
289
+ links: listLinks(brokenLinks)
290
+ });
291
+ }
292
+ if (routing.locales.length > 1) emitCheck(emit, urlCheckType("url_hasLangSelector", targetUrl), switcherLinks.length > 0 ? "success" : "warning", switcherLinks.length > 0 ? {
293
+ message: `${switcherLinks.length} crawlable links to other locale versions`,
294
+ links: listLinks(switcherLinks)
295
+ } : "No crawlable <a href> to the other locale versions: search engines only discover them through hreflang");
129
296
  };
297
+ const getFilename = (url) => url.split("/").pop()?.split("?")[0] ?? url;
298
+ const formatChunk = (chunk) => ({
299
+ filename: getFilename(chunk.url),
300
+ url: chunk.url,
301
+ fileSize: formatSize(chunk.fileSize),
302
+ totalLocaleSize: formatSize(chunk.totalLocaleSize),
303
+ usedLocaleSize: formatSize(chunk.usedLocaleSize),
304
+ unusedLocaleSize: formatSize(chunk.unusedLocaleSize),
305
+ dictionariesFound: chunk.dictionariesFound,
306
+ unusedPercent: `${chunk.unusedPercent}%`
307
+ });
308
+ /** Human-readable version of a {@link BundleContentAnalysis}, for display. */
309
+ const formatBundleAnalysis = (analysis) => ({
310
+ currentLocale: analysis.currentLocale,
311
+ totalPageSize: formatSize(analysis.totalPageSize),
312
+ renderedContentSize: formatSize(analysis.renderedContentSize),
313
+ contentSize: formatSize(analysis.contentSize),
314
+ totalLocaleSize: formatSize(analysis.totalLocaleSize),
315
+ totalUnusedLocaleSize: formatSize(analysis.totalUnusedLocaleSize),
316
+ unusedPercentOfLocale: `${analysis.unusedPercentOfLocale}%`,
317
+ mainBundleChunks: analysis.mainBundleChunks.map(formatChunk),
318
+ lazyBundleChunks: analysis.lazyBundleChunks.map(formatChunk)
319
+ });
130
320
  /**
131
- * Analyze the JavaScript bundles for unused locale content and emit the
132
- * corresponding event. Returns the analysis so callers can report on it.
321
+ * `url_unusedBundleContent`: share of other-locale translations shipped in the
322
+ * eagerly loaded bundles. Returns the raw analysis for reporting.
133
323
  */
134
- const checkBundleContent = (chunks, html, currentLocale, targetUrl, totalPageSize, events) => {
324
+ const checkBundleContent = (chunks, html, currentLocale, targetUrl, totalPageSize, emit) => {
325
+ const type = urlCheckType("url_unusedBundleContent", targetUrl);
135
326
  if (!currentLocale) {
136
- events.push({
137
- type: `url_unusedBundleContent\\${targetUrl}`,
138
- status: "warning",
139
- details: { warning: "Cannot analyse bundle content: page locale not detected" }
140
- });
327
+ emitCheck(emit, type, "warning", "Cannot analyse bundle content: page locale not detected");
141
328
  return;
142
329
  }
143
330
  const analysis = require_scan_analyzeBundleContent.analyzeBundleContent(chunks, html, currentLocale, totalPageSize);
144
- const mainBundleMaxUnused = analysis.mainBundleChunks.reduce((max, c) => Math.max(max, c.unusedPercent), 0);
145
- const status = mainBundleMaxUnused === 0 ? "success" : mainBundleMaxUnused <= 30 ? "warning" : "error";
146
- events.push({
147
- type: `url_unusedBundleContent\\${targetUrl}`,
148
- status,
149
- details: { [status]: analysis }
150
- });
331
+ const mainBundleMaxUnused = analysis.mainBundleChunks.reduce((max, chunk) => Math.max(max, chunk.unusedPercent), 0);
332
+ emitCheck(emit, type, mainBundleMaxUnused === 0 ? "success" : mainBundleMaxUnused <= 30 ? "warning" : "error", formatBundleAnalysis(analysis));
151
333
  return analysis;
152
334
  };
153
- /** Fetch and check `robots.txt`, emitting robots-related events. */
154
- const checkRobots = async (origin, discoveredLocales, userAgent, events) => {
155
- let robotsPresent = false;
156
- let noLocalizedUrlsForgotten = true;
157
- const errors = [];
158
- try {
159
- const response = await fetch(`${origin}/robots.txt`, { headers: { "User-Agent": userAgent } });
160
- if (response.ok) {
161
- robotsPresent = true;
162
- const content = await response.text();
163
- if (content && discoveredLocales.size > 0) {
164
- const disallowedPaths = content.split("\n").map((line) => line.trim().toLowerCase()).filter((line) => line.startsWith("disallow:")).map((line) => line.slice(9).trim());
165
- for (const locale of discoveredLocales) for (const path of disallowedPaths) if (path === `/${locale}` || path === `/${locale}/`) {
166
- noLocalizedUrlsForgotten = false;
167
- errors.push(`Locale path "${locale}" appears to be blocked in robots.txt: ${path}`);
168
- }
169
- }
170
- }
171
- } catch (error) {
172
- errors.push(`Failed to fetch robots.txt: ${error instanceof Error ? error.message : "Unknown error"}`);
335
+ /**
336
+ * Fetch `robots.txt` and check that neither the site nor its localized URLs
337
+ * are blocked for Googlebot.
338
+ *
339
+ * - `robots_robotsPresent`
340
+ * - `robots_noLocalizedUrlsForgotten`
341
+ *
342
+ * @param localizedUrls - URLs of every locale version (hreflang alternates).
343
+ * @returns The sitemaps declared in `robots.txt`.
344
+ */
345
+ const checkRobots = async (origin, localizedUrls, fetchOptions, emit) => {
346
+ const response = await require_scan_fetchText.fetchText(`${origin}/robots.txt`, fetchOptions);
347
+ if (!response?.ok) {
348
+ emitCheck(emit, "robots_robotsPresent", "warning", response ? `No robots.txt found (HTTP ${response.status})` : "robots.txt is unreachable");
349
+ return [];
173
350
  }
174
- events.push({
175
- type: "robots_robotsPresent",
176
- status: robotsPresent ? "success" : "warning",
177
- details: {
178
- warning: robotsPresent ? void 0 : "No robots.txt found",
179
- error: errors.length > 0 ? errors : void 0
180
- }
351
+ const robots = require_scan_robots.parseRobots(response.text);
352
+ emitCheck(emit, "robots_robotsPresent", "success", {
353
+ disallow: robots.disallowedPaths,
354
+ sitemaps: robots.sitemapUrls
181
355
  });
182
- if (robotsPresent) events.push({
183
- type: "robots_noLocalizedUrlsForgotten",
184
- status: noLocalizedUrlsForgotten ? "success" : "error",
185
- details: { error: noLocalizedUrlsForgotten ? void 0 : errors }
356
+ const blockedUrls = [origin, ...localizedUrls].filter((url) => {
357
+ const parsedUrl = require_scan_detection_url.parseUrl(url);
358
+ return parsedUrl?.origin === origin && !require_scan_robots.isPathAllowedByRobots(`${parsedUrl.pathname}${parsedUrl.search}`, robots);
186
359
  });
360
+ const isSiteBlocked = !require_scan_robots.isPathAllowedByRobots("/", robots);
361
+ emitCheck(emit, "robots_noLocalizedUrlsForgotten", blockedUrls.length === 0 ? "success" : "error", blockedUrls.length === 0 ? true : isSiteBlocked ? "robots.txt blocks the whole site for Googlebot (Disallow: /)" : {
362
+ message: "Localized URLs blocked by robots.txt",
363
+ urls: [...new Set(blockedUrls)]
364
+ });
365
+ return robots.sitemapUrls;
187
366
  };
188
- /** Fetch and check `sitemap.xml`, emitting sitemap-related events. */
189
- const checkSitemap = async (origin, discoveredLocales, userAgent, events) => {
190
- let sitemapPresent = false;
191
- let hasXDefault = false;
367
+ /**
368
+ * Read the sitemaps (declared in `robots.txt`, else the default locations,
369
+ * following sitemap indexes) and check their locale coverage.
370
+ *
371
+ * - `sitemap_sitemapPresent`
372
+ * - `sitemap_noLocalizedUrlsForgotten`: every locale is listed, and entries
373
+ * declaring alternates declare all of them
374
+ * - `sitemap_hasAlternates` / `sitemap_hasXDefault`
375
+ */
376
+ const checkSitemap = async (origin, declaredSitemapUrls, routing, fetchOptions, emit) => {
377
+ const { fetchedSitemapUrls, entries, isTruncated } = await require_scan_sitemap.collectSitemapEntries(declaredSitemapUrls.length > 0 ? declaredSitemapUrls : require_scan_sitemap.getDefaultSitemapUrls(origin), fetchOptions);
378
+ if (fetchedSitemapUrls.length === 0) {
379
+ emitCheck(emit, "sitemap_sitemapPresent", "warning", declaredSitemapUrls.length > 0 ? `Sitemaps declared in robots.txt are unreachable: ${declaredSitemapUrls.join(", ")}` : "No sitemap found (robots.txt Sitemap directive, /sitemap.xml, /sitemap_index.xml)");
380
+ return;
381
+ }
382
+ emitCheck(emit, "sitemap_sitemapPresent", "success", {
383
+ sitemaps: fetchedSitemapUrls,
384
+ urlCount: entries.length,
385
+ isTruncated
386
+ });
387
+ const knownLocales = routing.locales;
388
+ const foundLanguages = /* @__PURE__ */ new Set();
389
+ const entriesWithoutSelfReference = [];
390
+ const relativeAlternates = [];
391
+ let partiallyTranslatedCount = 0;
192
392
  let hasAlternates = false;
193
- let noLocalizedUrlsForgotten = true;
194
- const errors = [];
195
- try {
196
- const response = await fetch(`${origin}/sitemap.xml`, { headers: { "User-Agent": userAgent } });
197
- if (response.ok) {
198
- sitemapPresent = true;
199
- const content = await response.text();
200
- const hreflangs = (content.match(/hreflang\s*=\s*"([^"]+)"/gi) ?? []).map((m) => m.replace(/hreflang\s*=\s*"/i, "").replace(/"$/, ""));
201
- hasAlternates = hreflangs.length > 0;
202
- hasXDefault = hreflangs.includes("x-default");
203
- if (discoveredLocales.size > 0) {
204
- const urlBlocks = content.match(/<url\b[\s\S]*?<\/url>/gi) ?? [];
205
- const allFoundLocales = /* @__PURE__ */ new Set();
206
- let anyUrlMissingLocale = false;
207
- for (const block of urlBlocks) {
208
- const localesInUrl = /* @__PURE__ */ new Set();
209
- for (const hreflang of block.match(/hreflang\s*=\s*"([^"]+)"/gi) ?? []) {
210
- const value = hreflang.replace(/hreflang\s*=\s*"/i, "").replace(/"$/, "");
211
- if (value !== "x-default") {
212
- localesInUrl.add(value);
213
- allFoundLocales.add(value);
214
- }
215
- }
216
- const loc = block.match(/<loc>([\s\S]*?)<\/loc>/i)?.[1]?.trim();
217
- if (loc) try {
218
- const firstSegment = new URL(loc).pathname.split("/").filter(Boolean)[0];
219
- if (firstSegment && discoveredLocales.has(firstSegment)) {
220
- localesInUrl.add(firstSegment);
221
- allFoundLocales.add(firstSegment);
222
- }
223
- } catch {}
224
- const missing = [...discoveredLocales].filter((locale) => !localesInUrl.has(locale));
225
- if (missing.length > 0 && missing.length < discoveredLocales.size) anyUrlMissingLocale = true;
226
- }
227
- const completelyMissing = [...discoveredLocales].filter((locale) => !allFoundLocales.has(locale));
228
- if (anyUrlMissingLocale || completelyMissing.length > 0) {
229
- noLocalizedUrlsForgotten = false;
230
- if (completelyMissing.length > 0) errors.push(`The following locales are completely missing from the sitemap: ${completelyMissing.join(", ")}`);
231
- }
393
+ let hasXDefault = false;
394
+ for (const { loc, alternates } of entries) {
395
+ const locLocale = require_scan_detection_detectRoutingStrategy.getUrlLocale(loc, routing);
396
+ if (locLocale) foundLanguages.add(require_scan_detection_localeCode.getLanguageCode(locLocale));
397
+ if (alternates.length === 0) continue;
398
+ hasAlternates = true;
399
+ const entryLanguages = /* @__PURE__ */ new Set();
400
+ for (const { hreflang, href } of alternates) {
401
+ if (!require_scan_detection_url.isAbsoluteUrl(href)) relativeAlternates.push(`${loc}: ${hreflang} → ${href}`);
402
+ if (hreflang.toLowerCase() === "x-default") {
403
+ hasXDefault = true;
404
+ continue;
232
405
  }
406
+ entryLanguages.add(require_scan_detection_localeCode.getLanguageCode(hreflang));
407
+ foundLanguages.add(require_scan_detection_localeCode.getLanguageCode(hreflang));
233
408
  }
234
- } catch (error) {
235
- errors.push(`Failed to fetch sitemap.xml: ${error instanceof Error ? error.message : "Unknown error"}`);
409
+ const normalizedLoc = require_scan_detection_url.normalizeUrl(loc);
410
+ if (!alternates.some(({ href }) => require_scan_detection_url.normalizeUrl(href) === normalizedLoc)) entriesWithoutSelfReference.push(loc);
411
+ if (entryLanguages.size < knownLocales.length) partiallyTranslatedCount++;
236
412
  }
237
- events.push({
238
- type: "sitemap_sitemapPresent",
239
- status: sitemapPresent ? "success" : "warning",
240
- details: {
241
- warning: sitemapPresent ? void 0 : "No sitemap.xml found",
242
- error: errors.length > 0 ? errors : void 0
243
- }
244
- });
245
- if (sitemapPresent) {
246
- events.push({
247
- type: "sitemap_noLocalizedUrlsForgotten",
248
- status: noLocalizedUrlsForgotten ? "success" : "warning",
249
- details: { warning: noLocalizedUrlsForgotten ? void 0 : errors }
250
- });
251
- events.push({
252
- type: "sitemap_hasXDefault",
253
- status: hasXDefault ? "success" : "warning",
254
- details: { warning: hasXDefault ? void 0 : "No x-default hreflang in sitemap" }
255
- });
256
- events.push({
257
- type: "sitemap_hasAlternates",
258
- status: hasAlternates ? "success" : "warning",
259
- details: { warning: hasAlternates ? void 0 : "No alternate language links found in sitemap" }
260
- });
413
+ const isSingleOriginRouting = routing.strategy !== "subdomain" && routing.strategy !== "domain";
414
+ if (knownLocales.length > 1) {
415
+ const missingLocales = isSingleOriginRouting ? knownLocales.filter((locale) => !foundLanguages.has(require_scan_detection_localeCode.getLanguageCode(locale))) : [];
416
+ const issues = [...missingLocales.length > 0 ? [`Locales missing from the sitemap: ${missingLocales.join(", ")}`] : [], ...entriesWithoutSelfReference.slice(0, MAX_LISTED_LINKS).map((loc) => `${loc} is not listed among its own alternates`)];
417
+ emitCheck(emit, "sitemap_noLocalizedUrlsForgotten", issues.length === 0 ? "success" : "warning", issues.length === 0 ? {
418
+ message: `Every locale (${knownLocales.join(", ")}) is listed`,
419
+ partiallyTranslatedUrlCount: partiallyTranslatedCount
420
+ } : issues);
261
421
  }
422
+ if (!hasAlternates) emitCheck(emit, "sitemap_hasAlternates", "warning", "No alternate language links found in the sitemap");
423
+ else if (relativeAlternates.length > 0) emitCheck(emit, "sitemap_hasAlternates", "error", {
424
+ message: `${relativeAlternates.length} sitemap hreflang alternates are not absolute URLs (https://…): search engines ignore them`,
425
+ alternates: relativeAlternates.slice(0, MAX_LISTED_LINKS)
426
+ });
427
+ else emitCheck(emit, "sitemap_hasAlternates", "success", true);
428
+ emitCheck(emit, "sitemap_hasXDefault", hasXDefault ? "success" : "warning", hasXDefault ? true : "No x-default hreflang in the sitemap");
262
429
  };
263
430
 
264
431
  //#endregion
265
432
  exports.checkBundleContent = checkBundleContent;
266
433
  exports.checkCanonical = checkCanonical;
267
- exports.checkHtmlAttributes = checkHtmlAttributes;
268
- exports.checkLinguisticStructure = checkLinguisticStructure;
434
+ exports.checkHreflang = checkHreflang;
435
+ exports.checkHreflangReciprocity = checkHreflangReciprocity;
436
+ exports.checkHtmlDir = checkHtmlDir;
437
+ exports.checkHtmlLang = checkHtmlLang;
438
+ exports.checkInternalLinks = checkInternalLinks;
439
+ exports.checkLocaleConsistency = checkLocaleConsistency;
440
+ exports.checkOgLocale = checkOgLocale;
269
441
  exports.checkRobots = checkRobots;
270
442
  exports.checkSitemap = checkSitemap;
271
- exports.checkUrlStructure = checkUrlStructure;
443
+ exports.checkXDefault = checkXDefault;
444
+ exports.formatBundleAnalysis = formatBundleAnalysis;
272
445
  exports.formatSize = formatSize;
273
446
  //# sourceMappingURL=checks.cjs.map