repopo 0.12.0 → 0.14.0

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 (345) hide show
  1. package/CHANGELOG.md +70 -0
  2. package/README.md +57 -90
  3. package/THIRD-PARTY-LICENSES.txt +169 -11
  4. package/bin/dev.js +0 -0
  5. package/esm/adapters/fluidFramework.d.ts +13 -9
  6. package/esm/adapters/fluidFramework.d.ts.map +1 -1
  7. package/esm/adapters/fluidFramework.js +20 -43
  8. package/esm/adapters/fluidFramework.js.map +1 -1
  9. package/esm/api.d.ts +66 -1
  10. package/esm/api.d.ts.map +1 -1
  11. package/esm/api.js +18 -1
  12. package/esm/api.js.map +1 -1
  13. package/esm/baseCommand.d.ts.map +1 -1
  14. package/esm/baseCommand.js +2 -2
  15. package/esm/baseCommand.js.map +1 -1
  16. package/esm/commands/check-native.d.ts +5 -3
  17. package/esm/commands/check-native.d.ts.map +1 -1
  18. package/esm/commands/check-native.js +35 -7
  19. package/esm/commands/check-native.js.map +1 -1
  20. package/esm/commands/check.d.ts +8 -21
  21. package/esm/commands/check.d.ts.map +1 -1
  22. package/esm/commands/check.js +98 -154
  23. package/esm/commands/check.js.map +1 -1
  24. package/esm/commands/list.d.ts.map +1 -1
  25. package/esm/commands/list.js +1 -0
  26. package/esm/commands/list.js.map +1 -1
  27. package/esm/concurrency.d.ts +3 -0
  28. package/esm/concurrency.d.ts.map +1 -0
  29. package/esm/concurrency.js +8 -0
  30. package/esm/concurrency.js.map +1 -0
  31. package/esm/config.d.ts +10 -39
  32. package/esm/config.d.ts.map +1 -1
  33. package/esm/config.js +3 -7
  34. package/esm/config.js.map +1 -1
  35. package/esm/context.d.ts +10 -1
  36. package/esm/context.d.ts.map +1 -1
  37. package/esm/context.js +19 -1
  38. package/esm/context.js.map +1 -1
  39. package/esm/executionContractError.d.ts +4 -0
  40. package/esm/executionContractError.d.ts.map +1 -0
  41. package/esm/executionContractError.js +4 -0
  42. package/esm/executionContractError.js.map +1 -0
  43. package/esm/fileCache.d.ts +12 -0
  44. package/esm/fileCache.d.ts.map +1 -0
  45. package/esm/fileCache.js +37 -0
  46. package/esm/fileCache.js.map +1 -0
  47. package/esm/filePaths.d.ts +2 -0
  48. package/esm/filePaths.d.ts.map +1 -0
  49. package/esm/filePaths.js +9 -0
  50. package/esm/filePaths.js.map +1 -0
  51. package/esm/generators.d.ts +0 -20
  52. package/esm/generators.d.ts.map +1 -1
  53. package/esm/generators.js.map +1 -1
  54. package/esm/index.d.ts +3 -16
  55. package/esm/index.d.ts.map +1 -1
  56. package/esm/index.js +3 -17
  57. package/esm/index.js.map +1 -1
  58. package/esm/makePolicy.d.ts +20 -100
  59. package/esm/makePolicy.d.ts.map +1 -1
  60. package/esm/makePolicy.js +46 -132
  61. package/esm/makePolicy.js.map +1 -1
  62. package/esm/perf.d.ts +5 -2
  63. package/esm/perf.d.ts.map +1 -1
  64. package/esm/perf.js +10 -9
  65. package/esm/perf.js.map +1 -1
  66. package/esm/policies/CargoLicenceValidated.d.ts +4 -4
  67. package/esm/policies/CargoLicenceValidated.d.ts.map +1 -1
  68. package/esm/policies/CargoLicenceValidated.js +9 -7
  69. package/esm/policies/CargoLicenceValidated.js.map +1 -1
  70. package/esm/policies/CargoLintsConfigured.d.ts +4 -4
  71. package/esm/policies/CargoLintsConfigured.d.ts.map +1 -1
  72. package/esm/policies/CargoLintsConfigured.js +19 -15
  73. package/esm/policies/CargoLintsConfigured.js.map +1 -1
  74. package/esm/policies/CargoLockPolicy.d.ts +4 -4
  75. package/esm/policies/CargoLockPolicy.d.ts.map +1 -1
  76. package/esm/policies/CargoLockPolicy.js +15 -11
  77. package/esm/policies/CargoLockPolicy.js.map +1 -1
  78. package/esm/policies/CargoTomlRequired.d.ts +5 -5
  79. package/esm/policies/CargoTomlRequired.d.ts.map +1 -1
  80. package/esm/policies/CargoTomlRequired.js +7 -7
  81. package/esm/policies/CargoTomlRequired.js.map +1 -1
  82. package/esm/policies/CargoTomlSorted.d.ts +4 -4
  83. package/esm/policies/CargoTomlSorted.d.ts.map +1 -1
  84. package/esm/policies/CargoTomlSorted.js +21 -17
  85. package/esm/policies/CargoTomlSorted.js.map +1 -1
  86. package/esm/policies/ConditionalScripts.d.ts +10 -8
  87. package/esm/policies/ConditionalScripts.d.ts.map +1 -1
  88. package/esm/policies/ConditionalScripts.js +24 -22
  89. package/esm/policies/ConditionalScripts.js.map +1 -1
  90. package/esm/policies/ExactScripts.d.ts +8 -6
  91. package/esm/policies/ExactScripts.d.ts.map +1 -1
  92. package/esm/policies/ExactScripts.js +15 -18
  93. package/esm/policies/ExactScripts.js.map +1 -1
  94. package/esm/policies/GleamDepVersions.d.ts +5 -5
  95. package/esm/policies/GleamDepVersions.d.ts.map +1 -1
  96. package/esm/policies/GleamDepVersions.js +5 -5
  97. package/esm/policies/GleamDepVersions.js.map +1 -1
  98. package/esm/policies/GleamDocExists.d.ts +4 -4
  99. package/esm/policies/GleamDocExists.d.ts.map +1 -1
  100. package/esm/policies/GleamDocExists.js +6 -6
  101. package/esm/policies/GleamDocExists.js.map +1 -1
  102. package/esm/policies/GleamInternalModulesConfigured.d.ts +4 -4
  103. package/esm/policies/GleamInternalModulesConfigured.d.ts.map +1 -1
  104. package/esm/policies/GleamInternalModulesConfigured.js +8 -6
  105. package/esm/policies/GleamInternalModulesConfigured.js.map +1 -1
  106. package/esm/policies/GleamLicenceConfigured.d.ts +4 -4
  107. package/esm/policies/GleamLicenceConfigured.d.ts.map +1 -1
  108. package/esm/policies/GleamLicenceConfigured.js +8 -6
  109. package/esm/policies/GleamLicenceConfigured.js.map +1 -1
  110. package/esm/policies/GleamManifestCommitted.d.ts +10 -3
  111. package/esm/policies/GleamManifestCommitted.d.ts.map +1 -1
  112. package/esm/policies/GleamManifestCommitted.js +10 -9
  113. package/esm/policies/GleamManifestCommitted.js.map +1 -1
  114. package/esm/policies/GleamMonorepoConsistency.d.ts +13 -5
  115. package/esm/policies/GleamMonorepoConsistency.d.ts.map +1 -1
  116. package/esm/policies/GleamMonorepoConsistency.js +10 -10
  117. package/esm/policies/GleamMonorepoConsistency.js.map +1 -1
  118. package/esm/policies/GleamNameRestrictions.d.ts +4 -4
  119. package/esm/policies/GleamNameRestrictions.d.ts.map +1 -1
  120. package/esm/policies/GleamNameRestrictions.js +6 -6
  121. package/esm/policies/GleamNameRestrictions.js.map +1 -1
  122. package/esm/policies/GleamNoPathDepsInPublished.d.ts +4 -4
  123. package/esm/policies/GleamNoPathDepsInPublished.d.ts.map +1 -1
  124. package/esm/policies/GleamNoPathDepsInPublished.js +6 -6
  125. package/esm/policies/GleamNoPathDepsInPublished.js.map +1 -1
  126. package/esm/policies/GleamPathDepsValid.d.ts +3 -3
  127. package/esm/policies/GleamPathDepsValid.d.ts.map +1 -1
  128. package/esm/policies/GleamPathDepsValid.js +5 -5
  129. package/esm/policies/GleamPathDepsValid.js.map +1 -1
  130. package/esm/policies/GleamProjectStructure.d.ts +11 -4
  131. package/esm/policies/GleamProjectStructure.d.ts.map +1 -1
  132. package/esm/policies/GleamProjectStructure.js +7 -8
  133. package/esm/policies/GleamProjectStructure.js.map +1 -1
  134. package/esm/policies/GleamSharedDependencyVersions.d.ts +12 -4
  135. package/esm/policies/GleamSharedDependencyVersions.d.ts.map +1 -1
  136. package/esm/policies/GleamSharedDependencyVersions.js +10 -10
  137. package/esm/policies/GleamSharedDependencyVersions.js.map +1 -1
  138. package/esm/policies/GleamTomlRequired.d.ts +4 -4
  139. package/esm/policies/GleamTomlRequired.d.ts.map +1 -1
  140. package/esm/policies/GleamTomlRequired.js +5 -5
  141. package/esm/policies/GleamTomlRequired.js.map +1 -1
  142. package/esm/policies/GleamTomlSorted.d.ts +4 -4
  143. package/esm/policies/GleamTomlSorted.d.ts.map +1 -1
  144. package/esm/policies/GleamTomlSorted.js +21 -17
  145. package/esm/policies/GleamTomlSorted.js.map +1 -1
  146. package/esm/policies/GleamVersionConstraint.d.ts +4 -4
  147. package/esm/policies/GleamVersionConstraint.d.ts.map +1 -1
  148. package/esm/policies/GleamVersionConstraint.js +14 -10
  149. package/esm/policies/GleamVersionConstraint.js.map +1 -1
  150. package/esm/policies/HtmlFileHeaders.d.ts +4 -2
  151. package/esm/policies/HtmlFileHeaders.d.ts.map +1 -1
  152. package/esm/policies/HtmlFileHeaders.js +1 -1
  153. package/esm/policies/HtmlFileHeaders.js.map +1 -1
  154. package/esm/policies/JsTsFileHeaders.d.ts +4 -2
  155. package/esm/policies/JsTsFileHeaders.d.ts.map +1 -1
  156. package/esm/policies/JsTsFileHeaders.js +1 -1
  157. package/esm/policies/JsTsFileHeaders.js.map +1 -1
  158. package/esm/policies/LicenseFileExists.d.ts +10 -4
  159. package/esm/policies/LicenseFileExists.d.ts.map +1 -1
  160. package/esm/policies/LicenseFileExists.js +27 -20
  161. package/esm/policies/LicenseFileExists.js.map +1 -1
  162. package/esm/policies/MaxChangedFiles.d.ts +17 -0
  163. package/esm/policies/MaxChangedFiles.d.ts.map +1 -0
  164. package/esm/policies/MaxChangedFiles.js +22 -0
  165. package/esm/policies/MaxChangedFiles.js.map +1 -0
  166. package/esm/policies/MutuallyExclusiveScripts.d.ts +8 -6
  167. package/esm/policies/MutuallyExclusiveScripts.d.ts.map +1 -1
  168. package/esm/policies/MutuallyExclusiveScripts.js +9 -13
  169. package/esm/policies/MutuallyExclusiveScripts.js.map +1 -1
  170. package/esm/policies/NoBuildDirectory.d.ts +9 -3
  171. package/esm/policies/NoBuildDirectory.d.ts.map +1 -1
  172. package/esm/policies/NoBuildDirectory.js +8 -13
  173. package/esm/policies/NoBuildDirectory.js.map +1 -1
  174. package/esm/policies/NoJsFileExtensions.d.ts +9 -3
  175. package/esm/policies/NoJsFileExtensions.d.ts.map +1 -1
  176. package/esm/policies/NoJsFileExtensions.js +9 -14
  177. package/esm/policies/NoJsFileExtensions.js.map +1 -1
  178. package/esm/policies/NoLargeBinaryFiles.d.ts +9 -4
  179. package/esm/policies/NoLargeBinaryFiles.d.ts.map +1 -1
  180. package/esm/policies/NoLargeBinaryFiles.js +10 -16
  181. package/esm/policies/NoLargeBinaryFiles.js.map +1 -1
  182. package/esm/policies/NoPrivateWorkspaceDependencies.d.ts +10 -6
  183. package/esm/policies/NoPrivateWorkspaceDependencies.d.ts.map +1 -1
  184. package/esm/policies/NoPrivateWorkspaceDependencies.js +12 -14
  185. package/esm/policies/NoPrivateWorkspaceDependencies.js.map +1 -1
  186. package/esm/policies/NoTargetArtifacts.d.ts +9 -3
  187. package/esm/policies/NoTargetArtifacts.d.ts.map +1 -1
  188. package/esm/policies/NoTargetArtifacts.js +9 -14
  189. package/esm/policies/NoTargetArtifacts.js.map +1 -1
  190. package/esm/policies/NoUnsafeWithoutJustification.d.ts +11 -4
  191. package/esm/policies/NoUnsafeWithoutJustification.d.ts.map +1 -1
  192. package/esm/policies/NoUnsafeWithoutJustification.js +8 -9
  193. package/esm/policies/NoUnsafeWithoutJustification.js.map +1 -1
  194. package/esm/policies/NoWildcardDependencies.d.ts +5 -5
  195. package/esm/policies/NoWildcardDependencies.d.ts.map +1 -1
  196. package/esm/policies/NoWildcardDependencies.js +6 -6
  197. package/esm/policies/NoWildcardDependencies.js.map +1 -1
  198. package/esm/policies/PackageAllowedScopes.d.ts +9 -5
  199. package/esm/policies/PackageAllowedScopes.d.ts.map +1 -1
  200. package/esm/policies/PackageAllowedScopes.js +15 -16
  201. package/esm/policies/PackageAllowedScopes.js.map +1 -1
  202. package/esm/policies/PackageEsmType.d.ts +7 -5
  203. package/esm/policies/PackageEsmType.d.ts.map +1 -1
  204. package/esm/policies/PackageEsmType.js +31 -31
  205. package/esm/policies/PackageEsmType.js.map +1 -1
  206. package/esm/policies/PackageFolderName.d.ts +8 -6
  207. package/esm/policies/PackageFolderName.d.ts.map +1 -1
  208. package/esm/policies/PackageFolderName.js +13 -16
  209. package/esm/policies/PackageFolderName.js.map +1 -1
  210. package/esm/policies/PackageJsonProperties.d.ts +5 -3
  211. package/esm/policies/PackageJsonProperties.d.ts.map +1 -1
  212. package/esm/policies/PackageJsonProperties.js +6 -17
  213. package/esm/policies/PackageJsonProperties.js.map +1 -1
  214. package/esm/policies/PackageJsonRepoDirectoryProperty.d.ts +2 -7
  215. package/esm/policies/PackageJsonRepoDirectoryProperty.d.ts.map +1 -1
  216. package/esm/policies/PackageJsonRepoDirectoryProperty.js +28 -42
  217. package/esm/policies/PackageJsonRepoDirectoryProperty.js.map +1 -1
  218. package/esm/policies/PackageJsonSorted.d.ts +2 -2
  219. package/esm/policies/PackageJsonSorted.d.ts.map +1 -1
  220. package/esm/policies/PackageJsonSorted.js +15 -30
  221. package/esm/policies/PackageJsonSorted.js.map +1 -1
  222. package/esm/policies/PackageLicense.d.ts +4 -4
  223. package/esm/policies/PackageLicense.d.ts.map +1 -1
  224. package/esm/policies/PackageLicense.js +31 -52
  225. package/esm/policies/PackageLicense.js.map +1 -1
  226. package/esm/policies/PackagePrivateField.d.ts +12 -8
  227. package/esm/policies/PackagePrivateField.d.ts.map +1 -1
  228. package/esm/policies/PackagePrivateField.js +31 -23
  229. package/esm/policies/PackagePrivateField.js.map +1 -1
  230. package/esm/policies/PackageReadme.d.ts +6 -5
  231. package/esm/policies/PackageReadme.d.ts.map +1 -1
  232. package/esm/policies/PackageReadme.js +22 -59
  233. package/esm/policies/PackageReadme.js.map +1 -1
  234. package/esm/policies/PackageScripts.d.ts +26 -22
  235. package/esm/policies/PackageScripts.d.ts.map +1 -1
  236. package/esm/policies/PackageScripts.js +39 -36
  237. package/esm/policies/PackageScripts.js.map +1 -1
  238. package/esm/policies/PackageTestScripts.d.ts +10 -6
  239. package/esm/policies/PackageTestScripts.d.ts.map +1 -1
  240. package/esm/policies/PackageTestScripts.js +14 -16
  241. package/esm/policies/PackageTestScripts.js.map +1 -1
  242. package/esm/policies/PublicApiDocumented.d.ts +4 -4
  243. package/esm/policies/PublicApiDocumented.d.ts.map +1 -1
  244. package/esm/policies/PublicApiDocumented.js +10 -8
  245. package/esm/policies/PublicApiDocumented.js.map +1 -1
  246. package/esm/policies/RequireCompanionChanges.d.ts +16 -0
  247. package/esm/policies/RequireCompanionChanges.d.ts.map +1 -0
  248. package/esm/policies/RequireCompanionChanges.js +42 -0
  249. package/esm/policies/RequireCompanionChanges.js.map +1 -0
  250. package/esm/policies/RequiredGitignorePatterns.d.ts +11 -4
  251. package/esm/policies/RequiredGitignorePatterns.d.ts.map +1 -1
  252. package/esm/policies/RequiredGitignorePatterns.js +29 -36
  253. package/esm/policies/RequiredGitignorePatterns.js.map +1 -1
  254. package/esm/policies/RequiredScripts.d.ts +9 -7
  255. package/esm/policies/RequiredScripts.d.ts.map +1 -1
  256. package/esm/policies/RequiredScripts.js +24 -24
  257. package/esm/policies/RequiredScripts.js.map +1 -1
  258. package/esm/policies/RustDocExists.d.ts +4 -4
  259. package/esm/policies/RustDocExists.d.ts.map +1 -1
  260. package/esm/policies/RustDocExists.js +6 -6
  261. package/esm/policies/RustDocExists.js.map +1 -1
  262. package/esm/policies/RustToolchainExists.d.ts +11 -4
  263. package/esm/policies/RustToolchainExists.d.ts.map +1 -1
  264. package/esm/policies/RustToolchainExists.js +10 -9
  265. package/esm/policies/RustToolchainExists.js.map +1 -1
  266. package/esm/policies/RustfmtConfigExists.d.ts +11 -4
  267. package/esm/policies/RustfmtConfigExists.d.ts.map +1 -1
  268. package/esm/policies/RustfmtConfigExists.js +10 -9
  269. package/esm/policies/RustfmtConfigExists.js.map +1 -1
  270. package/esm/policies/ScriptContains.d.ts +9 -7
  271. package/esm/policies/ScriptContains.d.ts.map +1 -1
  272. package/esm/policies/ScriptContains.js +9 -13
  273. package/esm/policies/ScriptContains.js.map +1 -1
  274. package/esm/policies/SharedDependencyVersions.d.ts +9 -4
  275. package/esm/policies/SharedDependencyVersions.d.ts.map +1 -1
  276. package/esm/policies/SharedDependencyVersions.js +28 -20
  277. package/esm/policies/SharedDependencyVersions.js.map +1 -1
  278. package/esm/policies/WorkspaceInheritance.d.ts +11 -4
  279. package/esm/policies/WorkspaceInheritance.d.ts.map +1 -1
  280. package/esm/policies/WorkspaceInheritance.js +10 -11
  281. package/esm/policies/WorkspaceInheritance.js.map +1 -1
  282. package/esm/policies/WorkspaceMembersValid.d.ts +11 -4
  283. package/esm/policies/WorkspaceMembersValid.d.ts.map +1 -1
  284. package/esm/policies/WorkspaceMembersValid.js +10 -11
  285. package/esm/policies/WorkspaceMembersValid.js.map +1 -1
  286. package/esm/policies/constants.d.ts.map +1 -1
  287. package/esm/policies/constants.js +1 -1
  288. package/esm/policies/constants.js.map +1 -1
  289. package/esm/policies.d.ts +15 -5
  290. package/esm/policies.d.ts.map +1 -1
  291. package/esm/policies.js +14 -5
  292. package/esm/policies.js.map +1 -1
  293. package/esm/policy.d.ts +67 -289
  294. package/esm/policy.d.ts.map +1 -1
  295. package/esm/policy.js +1 -122
  296. package/esm/policy.js.map +1 -1
  297. package/esm/policyDefiners/defineCargoPolicy.d.ts +25 -9
  298. package/esm/policyDefiners/defineCargoPolicy.d.ts.map +1 -1
  299. package/esm/policyDefiners/defineCargoPolicy.js +10 -28
  300. package/esm/policyDefiners/defineCargoPolicy.js.map +1 -1
  301. package/esm/policyDefiners/defineFileHeaderPolicy.d.ts +27 -14
  302. package/esm/policyDefiners/defineFileHeaderPolicy.d.ts.map +1 -1
  303. package/esm/policyDefiners/defineFileHeaderPolicy.js +16 -35
  304. package/esm/policyDefiners/defineFileHeaderPolicy.js.map +1 -1
  305. package/esm/policyDefiners/defineGleamPolicy.d.ts +25 -9
  306. package/esm/policyDefiners/defineGleamPolicy.d.ts.map +1 -1
  307. package/esm/policyDefiners/defineGleamPolicy.js +10 -27
  308. package/esm/policyDefiners/defineGleamPolicy.js.map +1 -1
  309. package/esm/policyDefiners/definePackagePolicy.d.ts +24 -13
  310. package/esm/policyDefiners/definePackagePolicy.d.ts.map +1 -1
  311. package/esm/policyDefiners/definePackagePolicy.js +9 -32
  312. package/esm/policyDefiners/definePackagePolicy.js.map +1 -1
  313. package/esm/policyDefiners/spdxValidator.d.ts +4 -12
  314. package/esm/policyDefiners/spdxValidator.d.ts.map +1 -1
  315. package/esm/policyDefiners/spdxValidator.js +26 -14
  316. package/esm/policyDefiners/spdxValidator.js.map +1 -1
  317. package/esm/policyExecution.d.ts +20 -0
  318. package/esm/policyExecution.d.ts.map +1 -0
  319. package/esm/policyExecution.js +234 -0
  320. package/esm/policyExecution.js.map +1 -0
  321. package/esm/repositoryChanges.d.ts +26 -0
  322. package/esm/repositoryChanges.d.ts.map +1 -0
  323. package/esm/repositoryChanges.js +172 -0
  324. package/esm/repositoryChanges.js.map +1 -0
  325. package/esm/repositoryFiles.d.ts +6 -0
  326. package/esm/repositoryFiles.d.ts.map +1 -0
  327. package/esm/repositoryFiles.js +11 -0
  328. package/esm/repositoryFiles.js.map +1 -0
  329. package/esm/runner.d.ts +73 -47
  330. package/esm/runner.d.ts.map +1 -1
  331. package/esm/runner.js +158 -144
  332. package/esm/runner.js.map +1 -1
  333. package/esm/settlePolicyPromise.d.ts +4 -0
  334. package/esm/settlePolicyPromise.d.ts.map +1 -0
  335. package/esm/settlePolicyPromise.js +10 -0
  336. package/esm/settlePolicyPromise.js.map +1 -0
  337. package/esm/tsdoc-metadata.json +1 -1
  338. package/esm/utils/safePaths.d.ts +9 -0
  339. package/esm/utils/safePaths.d.ts.map +1 -0
  340. package/esm/utils/safePaths.js +40 -0
  341. package/esm/utils/safePaths.js.map +1 -0
  342. package/oclif.manifest.json +57 -2
  343. package/package.json +42 -27
  344. package/scripts/benchmark.mjs +341 -0
  345. package/sidecar/sidecar.mjs +388 -417
@@ -34,36 +34,20 @@ function createReadmeContent(packageName, requiredContent) {
34
34
  /**
35
35
  * Handle case when README is missing.
36
36
  */
37
- async function handleMissingReadme(readmePath, packageName, file, resolve, requiredContent) {
37
+ async function handleMissingReadme(readmePath, packageName, _file, resolve, requiredContent) {
38
38
  if (resolve) {
39
39
  try {
40
40
  await writeFile(readmePath, createReadmeContent(packageName, requiredContent));
41
- return {
42
- name: "PackageReadme",
43
- file,
44
- resolved: true,
45
- autoFixable: true,
46
- errorMessages: [`Created README.md for package "${packageName}"`],
47
- };
41
+ return [`Created README.md for package "${packageName}"`].map((message) => ({ message, fixStatus: "fixed" }));
48
42
  }
49
43
  catch {
50
- return {
51
- name: "PackageReadme",
52
- file,
53
- resolved: false,
54
- autoFixable: true,
55
- errorMessages: [
56
- `Failed to create README.md for package "${packageName}"`,
57
- ],
58
- };
44
+ return [`Failed to create README.md for package "${packageName}"`].map((message) => ({ message, fixStatus: "failed" }));
59
45
  }
60
46
  }
61
- return {
62
- name: "PackageReadme",
63
- file,
64
- autoFixable: true,
65
- errorMessages: [`README.md missing for package "${packageName}"`],
66
- };
47
+ return [`README.md missing for package "${packageName}"`].map((message) => ({
48
+ message,
49
+ fixStatus: "available",
50
+ }));
67
51
  }
68
52
  /**
69
53
  * Validate README title against package name.
@@ -82,7 +66,7 @@ function validateTitle(readmeContent, packageName) {
82
66
  */
83
67
  async function handleRequiredContent(readmePath, readmeContent, requiredContent, resolve) {
84
68
  if (readmeContent.includes(requiredContent)) {
85
- return { fixed: false };
69
+ return undefined;
86
70
  }
87
71
  if (resolve) {
88
72
  try {
@@ -91,8 +75,8 @@ async function handleRequiredContent(readmePath, readmeContent, requiredContent,
91
75
  : `${readmeContent}\n${requiredContent}\n`;
92
76
  await writeFile(readmePath, newContent);
93
77
  return {
94
- error: `README missing required content: "${requiredContent}"`,
95
- fixed: true,
78
+ message: `README missing required content: "${requiredContent}"`,
79
+ fixStatus: "fixed",
96
80
  };
97
81
  }
98
82
  catch {
@@ -100,52 +84,30 @@ async function handleRequiredContent(readmePath, readmeContent, requiredContent,
100
84
  }
101
85
  }
102
86
  return {
103
- error: `README missing required content: "${requiredContent}"`,
104
- fixed: false,
87
+ message: `README missing required content: "${requiredContent}"`,
88
+ fixStatus: resolve ? "failed" : "available",
105
89
  };
106
90
  }
107
91
  /**
108
92
  * Validate existing README content.
109
93
  */
110
94
  async function validateExistingReadme(ctx) {
111
- const errors = [];
112
- let wasFixed = false;
95
+ const findings = [];
113
96
  // Check title matches package name
114
97
  if (ctx.requireMatchingTitle) {
115
98
  const titleResult = validateTitle(ctx.readmeContent, ctx.packageName);
116
99
  if (titleResult !== true) {
117
- errors.push(titleResult);
100
+ findings.push({ message: titleResult });
118
101
  }
119
102
  }
120
103
  // Check required content
121
104
  if (ctx.requiredContent) {
122
105
  const contentResult = await handleRequiredContent(ctx.readmePath, ctx.readmeContent, ctx.requiredContent, ctx.resolve);
123
- if (contentResult.error) {
124
- errors.push(contentResult.error);
106
+ if (contentResult) {
107
+ findings.push(contentResult);
125
108
  }
126
- if (contentResult.fixed) {
127
- wasFixed = true;
128
- }
129
- }
130
- if (errors.length === 0) {
131
- return true;
132
- }
133
- if (wasFixed) {
134
- return {
135
- name: "PackageReadme",
136
- file: ctx.file,
137
- resolved: true,
138
- autoFixable: true,
139
- errorMessages: [errors.join("; ")],
140
- };
141
109
  }
142
- return {
143
- name: "PackageReadme",
144
- file: ctx.file,
145
- autoFixable: ctx.requiredContent !== undefined &&
146
- !ctx.readmeContent.includes(ctx.requiredContent),
147
- errorMessages: [errors.join("; ")],
148
- };
110
+ return findings;
149
111
  }
150
112
  /**
151
113
  * A repo policy that ensures each package has a README.md file with proper content.
@@ -167,17 +129,18 @@ async function validateExistingReadme(ctx) {
167
129
  * const config: RepopoConfig = {
168
130
  * policies: [
169
131
  * // Basic README validation
170
- * makePolicy(PackageReadme),
132
+ * makePolicy(PackageReadme, { instanceId: "readme" }),
171
133
  *
172
134
  * // Require trademark notice in all READMEs
173
135
  * makePolicy(PackageReadme, {
174
- * requiredContent: "## Trademark",
136
+ * instanceId: "readme-trademark",
137
+ * config: { requiredContent: "## Trademark" },
175
138
  * }),
176
139
  * ],
177
140
  * };
178
141
  * ```
179
142
  *
180
- * @alpha
143
+ * @public
181
144
  */
182
145
  export const PackageReadme = definePackagePolicy({
183
146
  name: "PackageReadme",
@@ -188,7 +151,7 @@ export const PackageReadme = definePackagePolicy({
188
151
  const requiredContent = config?.requiredContent;
189
152
  // Skip private packages if configured to do so
190
153
  if (skipPrivate && json.private === true) {
191
- return true;
154
+ return [];
192
155
  }
193
156
  const packageName = json.name ?? "unknown";
194
157
  const packageDir = path.dirname(path.resolve(root, file));
@@ -1 +1 @@
1
- {"version":3,"file":"PackageReadme.js","sourceRoot":"","sources":["../../src/policies/PackageReadme.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync } from \"node:fs\";\nimport { readFile, writeFile } from \"node:fs/promises\";\nimport path from \"pathe\";\nimport type { PackageJson } from \"type-fest\";\nimport type { PolicyFailure, PolicyFixResult } from \"../policy.js\";\nimport { definePackagePolicy } from \"../policyDefiners/definePackagePolicy.js\";\n\n// Pre-compiled regex for performance\nconst LINE_SPLIT_REGEX = /\\r?\\n/;\nconst HEADING_REGEX = /^#\\s+(.+)$/;\n\n/**\n * Policy settings for the PackageReadme repo policy.\n *\n * @alpha\n */\nexport interface PackageReadmeSettings {\n\t/**\n\t * Whether to skip validation for private packages.\n\t *\n\t * @defaultValue true\n\t *\n\t * @remarks\n\t *\n\t * Private packages are typically not published and may not need a README file.\n\t * Set to `false` to require README files for all packages.\n\t */\n\tskipPrivate?: boolean;\n\n\t/**\n\t * Whether to require the README title to match the package name.\n\t *\n\t * @defaultValue true\n\t *\n\t * @remarks\n\t *\n\t * When enabled, the first line of the README must be `# package-name`.\n\t * This ensures consistency and helps users identify the package.\n\t */\n\trequireMatchingTitle?: boolean;\n\n\t/**\n\t * A string that must appear in the README content.\n\t *\n\t * @remarks\n\t *\n\t * Useful for ensuring all READMEs include required content like trademark\n\t * notices, disclaimers, or standard documentation sections.\n\t *\n\t * @example\n\t * ```typescript\n\t * requiredContent: \"## Trademark\"\n\t * ```\n\t */\n\trequiredContent?: string;\n}\n\n/**\n * Extract the title from a README file (first markdown H1 heading).\n *\n * @param content - The README content\n * @returns The title text (without the `# ` prefix) or `undefined` if not found\n */\nfunction extractReadmeTitle(content: string): string | undefined {\n\tconst lines = content.split(LINE_SPLIT_REGEX);\n\tfor (const line of lines) {\n\t\tconst match = HEADING_REGEX.exec(line);\n\t\tif (match?.[1]) {\n\t\t\treturn match[1].trim();\n\t\t}\n\t}\n\treturn undefined;\n}\n\n/**\n * Create a basic README content with package name as title.\n */\nfunction createReadmeContent(\n\tpackageName: string,\n\trequiredContent?: string,\n): string {\n\tlet content = `# ${packageName}\\n`;\n\tif (requiredContent) {\n\t\tcontent += `\\n${requiredContent}\\n`;\n\t}\n\treturn content;\n}\n\n/**\n * Handle case when README is missing.\n */\nasync function handleMissingReadme(\n\treadmePath: string,\n\tpackageName: string,\n\tfile: string,\n\tresolve: boolean,\n\trequiredContent?: string,\n): Promise<PolicyFailure | PolicyFixResult> {\n\tif (resolve) {\n\t\ttry {\n\t\t\tawait writeFile(\n\t\t\t\treadmePath,\n\t\t\t\tcreateReadmeContent(packageName, requiredContent),\n\t\t\t);\n\t\t\treturn {\n\t\t\t\tname: \"PackageReadme\",\n\t\t\t\tfile,\n\t\t\t\tresolved: true,\n\t\t\t\tautoFixable: true,\n\t\t\t\terrorMessages: [`Created README.md for package \"${packageName}\"`],\n\t\t\t};\n\t\t} catch {\n\t\t\treturn {\n\t\t\t\tname: \"PackageReadme\",\n\t\t\t\tfile,\n\t\t\t\tresolved: false,\n\t\t\t\tautoFixable: true,\n\t\t\t\terrorMessages: [\n\t\t\t\t\t`Failed to create README.md for package \"${packageName}\"`,\n\t\t\t\t],\n\t\t\t};\n\t\t}\n\t}\n\n\treturn {\n\t\tname: \"PackageReadme\",\n\t\tfile,\n\t\tautoFixable: true,\n\t\terrorMessages: [`README.md missing for package \"${packageName}\"`],\n\t};\n}\n\n/**\n * Validate README title against package name.\n *\n * @returns `true` if valid, or an error message string if invalid\n */\nfunction validateTitle(\n\treadmeContent: string,\n\tpackageName: string,\n): true | string {\n\tconst title = extractReadmeTitle(readmeContent);\n\tif (title !== packageName) {\n\t\treturn `README title \"${title ?? \"(none)\"}\" doesn't match package name \"${packageName}\"`;\n\t}\n\treturn true;\n}\n\n/**\n * Handle required content validation and auto-fix.\n */\nasync function handleRequiredContent(\n\treadmePath: string,\n\treadmeContent: string,\n\trequiredContent: string,\n\tresolve: boolean,\n): Promise<{ error?: string; fixed: boolean }> {\n\tif (readmeContent.includes(requiredContent)) {\n\t\treturn { fixed: false };\n\t}\n\n\tif (resolve) {\n\t\ttry {\n\t\t\tconst newContent = readmeContent.endsWith(\"\\n\")\n\t\t\t\t? `${readmeContent}${requiredContent}\\n`\n\t\t\t\t: `${readmeContent}\\n${requiredContent}\\n`;\n\t\t\tawait writeFile(readmePath, newContent);\n\t\t\treturn {\n\t\t\t\terror: `README missing required content: \"${requiredContent}\"`,\n\t\t\t\tfixed: true,\n\t\t\t};\n\t\t} catch {\n\t\t\t// Fall through to report error\n\t\t}\n\t}\n\n\treturn {\n\t\terror: `README missing required content: \"${requiredContent}\"`,\n\t\tfixed: false,\n\t};\n}\n\ninterface ValidationContext {\n\treadmePath: string;\n\treadmeContent: string;\n\tpackageName: string;\n\tfile: string;\n\tresolve: boolean;\n\trequireMatchingTitle: boolean;\n\trequiredContent: string | undefined;\n}\n\n/**\n * Validate existing README content.\n */\nasync function validateExistingReadme(\n\tctx: ValidationContext,\n): Promise<true | PolicyFailure | PolicyFixResult> {\n\tconst errors: string[] = [];\n\tlet wasFixed = false;\n\n\t// Check title matches package name\n\tif (ctx.requireMatchingTitle) {\n\t\tconst titleResult = validateTitle(ctx.readmeContent, ctx.packageName);\n\t\tif (titleResult !== true) {\n\t\t\terrors.push(titleResult);\n\t\t}\n\t}\n\n\t// Check required content\n\tif (ctx.requiredContent) {\n\t\tconst contentResult = await handleRequiredContent(\n\t\t\tctx.readmePath,\n\t\t\tctx.readmeContent,\n\t\t\tctx.requiredContent,\n\t\t\tctx.resolve,\n\t\t);\n\t\tif (contentResult.error) {\n\t\t\terrors.push(contentResult.error);\n\t\t}\n\t\tif (contentResult.fixed) {\n\t\t\twasFixed = true;\n\t\t}\n\t}\n\n\tif (errors.length === 0) {\n\t\treturn true;\n\t}\n\n\tif (wasFixed) {\n\t\treturn {\n\t\t\tname: \"PackageReadme\",\n\t\t\tfile: ctx.file,\n\t\t\tresolved: true,\n\t\t\tautoFixable: true,\n\t\t\terrorMessages: [errors.join(\"; \")],\n\t\t};\n\t}\n\n\treturn {\n\t\tname: \"PackageReadme\",\n\t\tfile: ctx.file,\n\t\tautoFixable:\n\t\t\tctx.requiredContent !== undefined &&\n\t\t\t!ctx.readmeContent.includes(ctx.requiredContent),\n\t\terrorMessages: [errors.join(\"; \")],\n\t};\n}\n\n/**\n * A repo policy that ensures each package has a README.md file with proper content.\n *\n * @remarks\n *\n * This policy validates that packages have documentation and can enforce:\n * 1. README.md file exists\n * 2. README title matches the package name\n * 3. README contains required content (e.g., trademark notices)\n *\n * The policy supports auto-fixing by creating a basic README with the package name as title.\n *\n * @example\n * ```typescript\n * import { makePolicy } from \"repopo\";\n * import { PackageReadme } from \"repopo/policies\";\n *\n * const config: RepopoConfig = {\n * policies: [\n * // Basic README validation\n * makePolicy(PackageReadme),\n *\n * // Require trademark notice in all READMEs\n * makePolicy(PackageReadme, {\n * requiredContent: \"## Trademark\",\n * }),\n * ],\n * };\n * ```\n *\n * @alpha\n */\nexport const PackageReadme = definePackagePolicy<\n\tPackageJson,\n\tPackageReadmeSettings | undefined\n>({\n\tname: \"PackageReadme\",\n\tdescription:\n\t\t\"Ensures each package has a README.md file with proper title and required content.\",\n\thandler: async (json, { file, root, resolve, config }) => {\n\t\tconst skipPrivate = config?.skipPrivate ?? true;\n\t\tconst requireMatchingTitle = config?.requireMatchingTitle ?? true;\n\t\tconst requiredContent = config?.requiredContent;\n\n\t\t// Skip private packages if configured to do so\n\t\tif (skipPrivate && json.private === true) {\n\t\t\treturn true;\n\t\t}\n\n\t\tconst packageName = json.name ?? \"unknown\";\n\t\tconst packageDir = path.dirname(path.resolve(root, file));\n\t\tconst readmePath = path.join(packageDir, \"README.md\");\n\n\t\t// Check if README exists\n\t\tif (!existsSync(readmePath)) {\n\t\t\treturn await handleMissingReadme(\n\t\t\t\treadmePath,\n\t\t\t\tpackageName,\n\t\t\t\tfile,\n\t\t\t\tresolve,\n\t\t\t\trequiredContent,\n\t\t\t);\n\t\t}\n\n\t\t// README exists, validate content\n\t\tconst readmeContent = await readFile(readmePath, \"utf-8\");\n\t\treturn await validateExistingReadme({\n\t\t\treadmePath,\n\t\t\treadmeContent,\n\t\t\tpackageName,\n\t\t\tfile,\n\t\t\tresolve,\n\t\t\trequireMatchingTitle,\n\t\t\trequiredContent,\n\t\t});\n\t},\n});\n"]}
1
+ {"version":3,"file":"PackageReadme.js","sourceRoot":"","sources":["../../src/policies/PackageReadme.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync } from \"node:fs\";\nimport { readFile, writeFile } from \"node:fs/promises\";\nimport path from \"pathe\";\nimport type { PackageJson } from \"type-fest\";\nimport type { PolicyFinding } from \"../policy.js\";\nimport { definePackagePolicy } from \"../policyDefiners/definePackagePolicy.js\";\n\n// Pre-compiled regex for performance\nconst LINE_SPLIT_REGEX = /\\r?\\n/;\nconst HEADING_REGEX = /^#\\s+(.+)$/;\n\n/**\n * Policy settings for the PackageReadme repo policy.\n *\n * @public\n */\nexport interface PackageReadmeSettings {\n\t/**\n\t * Whether to skip validation for private packages.\n\t *\n\t * @defaultValue true\n\t *\n\t * @remarks\n\t *\n\t * Private packages are typically not published and may not need a README file.\n\t * Set to `false` to require README files for all packages.\n\t */\n\tskipPrivate?: boolean;\n\n\t/**\n\t * Whether to require the README title to match the package name.\n\t *\n\t * @defaultValue true\n\t *\n\t * @remarks\n\t *\n\t * When enabled, the first line of the README must be `# package-name`.\n\t * This ensures consistency and helps users identify the package.\n\t */\n\trequireMatchingTitle?: boolean;\n\n\t/**\n\t * A string that must appear in the README content.\n\t *\n\t * @remarks\n\t *\n\t * Useful for ensuring all READMEs include required content like trademark\n\t * notices, disclaimers, or standard documentation sections.\n\t *\n\t * @example\n\t * ```typescript\n\t * requiredContent: \"## Trademark\"\n\t * ```\n\t */\n\trequiredContent?: string;\n}\n\n/**\n * Extract the title from a README file (first markdown H1 heading).\n *\n * @param content - The README content\n * @returns The title text (without the `# ` prefix) or `undefined` if not found\n */\nfunction extractReadmeTitle(content: string): string | undefined {\n\tconst lines = content.split(LINE_SPLIT_REGEX);\n\tfor (const line of lines) {\n\t\tconst match = HEADING_REGEX.exec(line);\n\t\tif (match?.[1]) {\n\t\t\treturn match[1].trim();\n\t\t}\n\t}\n\treturn undefined;\n}\n\n/**\n * Create a basic README content with package name as title.\n */\nfunction createReadmeContent(\n\tpackageName: string,\n\trequiredContent?: string,\n): string {\n\tlet content = `# ${packageName}\\n`;\n\tif (requiredContent) {\n\t\tcontent += `\\n${requiredContent}\\n`;\n\t}\n\treturn content;\n}\n\n/**\n * Handle case when README is missing.\n */\nasync function handleMissingReadme(\n\treadmePath: string,\n\tpackageName: string,\n\t_file: string,\n\tresolve: boolean,\n\trequiredContent?: string,\n): Promise<PolicyFinding[]> {\n\tif (resolve) {\n\t\ttry {\n\t\t\tawait writeFile(\n\t\t\t\treadmePath,\n\t\t\t\tcreateReadmeContent(packageName, requiredContent),\n\t\t\t);\n\t\t\treturn [`Created README.md for package \"${packageName}\"`].map(\n\t\t\t\t(message) => ({ message, fixStatus: \"fixed\" }),\n\t\t\t);\n\t\t} catch {\n\t\t\treturn [`Failed to create README.md for package \"${packageName}\"`].map(\n\t\t\t\t(message) => ({ message, fixStatus: \"failed\" }),\n\t\t\t);\n\t\t}\n\t}\n\treturn [`README.md missing for package \"${packageName}\"`].map((message) => ({\n\t\tmessage,\n\t\tfixStatus: \"available\",\n\t}));\n}\n\n/**\n * Validate README title against package name.\n *\n * @returns `true` if valid, or an error message string if invalid\n */\nfunction validateTitle(\n\treadmeContent: string,\n\tpackageName: string,\n): true | string {\n\tconst title = extractReadmeTitle(readmeContent);\n\tif (title !== packageName) {\n\t\treturn `README title \"${title ?? \"(none)\"}\" doesn't match package name \"${packageName}\"`;\n\t}\n\treturn true;\n}\n\n/**\n * Handle required content validation and auto-fix.\n */\nasync function handleRequiredContent(\n\treadmePath: string,\n\treadmeContent: string,\n\trequiredContent: string,\n\tresolve: boolean,\n): Promise<PolicyFinding | undefined> {\n\tif (readmeContent.includes(requiredContent)) {\n\t\treturn undefined;\n\t}\n\n\tif (resolve) {\n\t\ttry {\n\t\t\tconst newContent = readmeContent.endsWith(\"\\n\")\n\t\t\t\t? `${readmeContent}${requiredContent}\\n`\n\t\t\t\t: `${readmeContent}\\n${requiredContent}\\n`;\n\t\t\tawait writeFile(readmePath, newContent);\n\t\t\treturn {\n\t\t\t\tmessage: `README missing required content: \"${requiredContent}\"`,\n\t\t\t\tfixStatus: \"fixed\",\n\t\t\t};\n\t\t} catch {\n\t\t\t// Fall through to report error\n\t\t}\n\t}\n\n\treturn {\n\t\tmessage: `README missing required content: \"${requiredContent}\"`,\n\t\tfixStatus: resolve ? \"failed\" : \"available\",\n\t};\n}\n\ninterface ValidationContext {\n\treadmePath: string;\n\treadmeContent: string;\n\tpackageName: string;\n\tfile: string;\n\tresolve: boolean;\n\trequireMatchingTitle: boolean;\n\trequiredContent: string | undefined;\n}\n\n/**\n * Validate existing README content.\n */\nasync function validateExistingReadme(\n\tctx: ValidationContext,\n): Promise<PolicyFinding[]> {\n\tconst findings: PolicyFinding[] = [];\n\t// Check title matches package name\n\tif (ctx.requireMatchingTitle) {\n\t\tconst titleResult = validateTitle(ctx.readmeContent, ctx.packageName);\n\t\tif (titleResult !== true) {\n\t\t\tfindings.push({ message: titleResult });\n\t\t}\n\t}\n\n\t// Check required content\n\tif (ctx.requiredContent) {\n\t\tconst contentResult = await handleRequiredContent(\n\t\t\tctx.readmePath,\n\t\t\tctx.readmeContent,\n\t\t\tctx.requiredContent,\n\t\t\tctx.resolve,\n\t\t);\n\t\tif (contentResult) {\n\t\t\tfindings.push(contentResult);\n\t\t}\n\t}\n\treturn findings;\n}\n\n/**\n * A repo policy that ensures each package has a README.md file with proper content.\n *\n * @remarks\n *\n * This policy validates that packages have documentation and can enforce:\n * 1. README.md file exists\n * 2. README title matches the package name\n * 3. README contains required content (e.g., trademark notices)\n *\n * The policy supports auto-fixing by creating a basic README with the package name as title.\n *\n * @example\n * ```typescript\n * import { makePolicy } from \"repopo\";\n * import { PackageReadme } from \"repopo/policies\";\n *\n * const config: RepopoConfig = {\n * policies: [\n * // Basic README validation\n * makePolicy(PackageReadme, { instanceId: \"readme\" }),\n *\n * // Require trademark notice in all READMEs\n * makePolicy(PackageReadme, {\n * instanceId: \"readme-trademark\",\n * config: { requiredContent: \"## Trademark\" },\n * }),\n * ],\n * };\n * ```\n *\n * @public\n */\nexport const PackageReadme = definePackagePolicy<\n\tPackageJson,\n\tPackageReadmeSettings | undefined\n>({\n\tname: \"PackageReadme\",\n\tdescription:\n\t\t\"Ensures each package has a README.md file with proper title and required content.\",\n\thandler: async (json, { file, root, resolve, config }) => {\n\t\tconst skipPrivate = config?.skipPrivate ?? true;\n\t\tconst requireMatchingTitle = config?.requireMatchingTitle ?? true;\n\t\tconst requiredContent = config?.requiredContent;\n\n\t\t// Skip private packages if configured to do so\n\t\tif (skipPrivate && json.private === true) {\n\t\t\treturn [];\n\t\t}\n\n\t\tconst packageName = json.name ?? \"unknown\";\n\t\tconst packageDir = path.dirname(path.resolve(root, file));\n\t\tconst readmePath = path.join(packageDir, \"README.md\");\n\n\t\t// Check if README exists\n\t\tif (!existsSync(readmePath)) {\n\t\t\treturn await handleMissingReadme(\n\t\t\t\treadmePath,\n\t\t\t\tpackageName,\n\t\t\t\tfile,\n\t\t\t\tresolve,\n\t\t\t\trequiredContent,\n\t\t\t);\n\t\t}\n\n\t\t// README exists, validate content\n\t\tconst readmeContent = await readFile(readmePath, \"utf-8\");\n\t\treturn await validateExistingReadme({\n\t\t\treadmePath,\n\t\t\treadmeContent,\n\t\t\tpackageName,\n\t\t\tfile,\n\t\t\tresolve,\n\t\t\trequireMatchingTitle,\n\t\t\trequiredContent,\n\t\t});\n\t},\n});\n"]}
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Configuration for validating that a script's body contains required substrings.
3
3
  *
4
- * @alpha
4
+ * @public
5
5
  */
6
6
  export interface ScriptMustContainRule {
7
7
  /**
@@ -17,7 +17,7 @@ export interface ScriptMustContainRule {
17
17
  /**
18
18
  * Configuration for conditional script requirements.
19
19
  *
20
- * @alpha
20
+ * @public
21
21
  */
22
22
  export interface ConditionalScriptRule {
23
23
  /**
@@ -56,13 +56,13 @@ export interface ConditionalScriptRule {
56
56
  * { lint: "biome lint ." }
57
57
  * ```
58
58
  *
59
- * @alpha
59
+ * @public
60
60
  */
61
61
  export type RequiredScriptEntry = string | Record<string, string>;
62
62
  /**
63
63
  * Policy settings for the PackageScripts repo policy.
64
64
  *
65
- * @alpha
65
+ * @public
66
66
  */
67
67
  export interface PackageScriptsSettings {
68
68
  /**
@@ -180,29 +180,33 @@ export interface PackageScriptsSettings {
180
180
  * const config: RepopoConfig = {
181
181
  * policies: [
182
182
  * makePolicy(PackageScripts, {
183
- * // Scripts that must exist
184
- * must: [
185
- * "test", // Must exist, no auto-fix
186
- * { build: "tsc" }, // Must exist, auto-fix available
187
- * { lint: "biome lint ." }, // Must exist, auto-fix available
188
- * ],
189
- * // Scripts that must exist AND match exactly
190
- * exact: {
191
- * clean: "rimraf dist esm",
192
- * format: "biome format --write .",
183
+ * config: {
184
+ * // Scripts that must exist
185
+ * must: [
186
+ * "test", // Must exist, no auto-fix
187
+ * { build: "tsc" }, // Must exist, auto-fix available
188
+ * { lint: "biome lint ." }, // Must exist, auto-fix available
189
+ * ],
190
+ * // Scripts that must exist AND match exactly
191
+ * exact: {
192
+ * clean: "rimraf dist esm",
193
+ * format: "biome format --write .",
194
+ * },
195
+ * // If "build" exists, "clean" must also exist (with inline auto-fix default)
196
+ * conditionalRequired: [{ ifPresent: "build", requires: [{ clean: "rimraf dist" }] }],
197
+ * // Script body must contain these substrings
198
+ * scriptMustContain: [{ script: "build", mustContain: ["tsc"] }],
199
+ * // Only one of these can exist
200
+ * mutuallyExclusive: [["lint:eslint", "lint:biome"]],
193
201
  * },
194
- * // If "build" exists, "clean" must also exist (with inline auto-fix default)
195
- * conditionalRequired: [{ ifPresent: "build", requires: [{ clean: "rimraf dist" }] }],
196
- * // Script body must contain these substrings
197
- * scriptMustContain: [{ script: "build", mustContain: ["tsc"] }],
198
- * // Only one of these can exist
199
- * mutuallyExclusive: [["lint:eslint", "lint:biome"]],
200
202
  * }),
201
203
  * ],
202
204
  * };
203
205
  * ```
204
206
  *
205
- * @alpha
207
+ * @public
206
208
  */
207
- export declare const PackageScripts: import("../policy.js").PolicyShape<PackageScriptsSettings | undefined>;
209
+ export declare const PackageScripts: import("../policy.js").FilePolicyDefinition<PackageScriptsSettings | undefined> & {
210
+ requiresConfig: true;
211
+ };
208
212
  //# sourceMappingURL=PackageScripts.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"PackageScripts.d.ts","sourceRoot":"","sources":["../../src/policies/PackageScripts.ts"],"names":[],"mappings":"AAKA;;;;GAIG;AACH,MAAM,WAAW,qBAAqB;IACrC;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IAEf;;;OAGG;IACH,WAAW,EAAE,MAAM,EAAE,CAAC;CACtB;AAED;;;;GAIG;AACH,MAAM,WAAW,qBAAqB;IACrC;;;OAGG;IACH,SAAS,EAAE,MAAM,CAAC;IAElB;;;;;;;;;;;;;;OAcG;IACH,QAAQ,EAAE,mBAAmB,EAAE,CAAC;CAChC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,MAAM,mBAAmB,GAAG,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAElE;;;;GAIG;AACH,MAAM,WAAW,sBAAsB;IACtC;;;;;;;;;;;;;;;;;;OAkBG;IACH,IAAI,CAAC,EAAE,mBAAmB,EAAE,CAAC;IAE7B;;;;;;;;;;;;;;;;OAgBG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAE/B;;;;;;;;;;;;OAYG;IACH,iBAAiB,CAAC,EAAE,MAAM,EAAE,EAAE,CAAC;IAE/B;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,mBAAmB,CAAC,EAAE,qBAAqB,EAAE,CAAC;IAE9C;;;;;;;;;;;;;;;;;;OAkBG;IACH,iBAAiB,CAAC,EAAE,qBAAqB,EAAE,CAAC;CAC5C;AAseD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,eAAO,MAAM,cAAc,wEAgEzB,CAAC"}
1
+ {"version":3,"file":"PackageScripts.d.ts","sourceRoot":"","sources":["../../src/policies/PackageScripts.ts"],"names":[],"mappings":"AAKA;;;;GAIG;AACH,MAAM,WAAW,qBAAqB;IACrC;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IAEf;;;OAGG;IACH,WAAW,EAAE,MAAM,EAAE,CAAC;CACtB;AAED;;;;GAIG;AACH,MAAM,WAAW,qBAAqB;IACrC;;;OAGG;IACH,SAAS,EAAE,MAAM,CAAC;IAElB;;;;;;;;;;;;;;OAcG;IACH,QAAQ,EAAE,mBAAmB,EAAE,CAAC;CAChC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,MAAM,mBAAmB,GAAG,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAElE;;;;GAIG;AACH,MAAM,WAAW,sBAAsB;IACtC;;;;;;;;;;;;;;;;;;OAkBG;IACH,IAAI,CAAC,EAAE,mBAAmB,EAAE,CAAC;IAE7B;;;;;;;;;;;;;;;;OAgBG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAE/B;;;;;;;;;;;;OAYG;IACH,iBAAiB,CAAC,EAAE,MAAM,EAAE,EAAE,CAAC;IAE/B;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,mBAAmB,CAAC,EAAE,qBAAqB,EAAE,CAAC;IAE9C;;;;;;;;;;;;;;;;;;OAkBG;IACH,iBAAiB,CAAC,EAAE,qBAAqB,EAAE,CAAC;CAC5C;AAseD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AACH,eAAO,MAAM,cAAc;;CA+DzB,CAAC"}
@@ -1,7 +1,6 @@
1
1
  import jsonfile from "jsonfile";
2
2
  import { resolve as resolvePath } from "pathe";
3
3
  import { definePackagePolicy } from "../policyDefiners/definePackagePolicy.js";
4
- const POLICY_NAME = "PackageScripts";
5
4
  /**
6
5
  * Extracts script name and optional default value from a RequiredScriptEntry.
7
6
  */
@@ -318,42 +317,45 @@ function runAllValidations(config, scripts, defaultScripts) {
318
317
  * const config: RepopoConfig = {
319
318
  * policies: [
320
319
  * makePolicy(PackageScripts, {
321
- * // Scripts that must exist
322
- * must: [
323
- * "test", // Must exist, no auto-fix
324
- * { build: "tsc" }, // Must exist, auto-fix available
325
- * { lint: "biome lint ." }, // Must exist, auto-fix available
326
- * ],
327
- * // Scripts that must exist AND match exactly
328
- * exact: {
329
- * clean: "rimraf dist esm",
330
- * format: "biome format --write .",
320
+ * config: {
321
+ * // Scripts that must exist
322
+ * must: [
323
+ * "test", // Must exist, no auto-fix
324
+ * { build: "tsc" }, // Must exist, auto-fix available
325
+ * { lint: "biome lint ." }, // Must exist, auto-fix available
326
+ * ],
327
+ * // Scripts that must exist AND match exactly
328
+ * exact: {
329
+ * clean: "rimraf dist esm",
330
+ * format: "biome format --write .",
331
+ * },
332
+ * // If "build" exists, "clean" must also exist (with inline auto-fix default)
333
+ * conditionalRequired: [{ ifPresent: "build", requires: [{ clean: "rimraf dist" }] }],
334
+ * // Script body must contain these substrings
335
+ * scriptMustContain: [{ script: "build", mustContain: ["tsc"] }],
336
+ * // Only one of these can exist
337
+ * mutuallyExclusive: [["lint:eslint", "lint:biome"]],
331
338
  * },
332
- * // If "build" exists, "clean" must also exist (with inline auto-fix default)
333
- * conditionalRequired: [{ ifPresent: "build", requires: [{ clean: "rimraf dist" }] }],
334
- * // Script body must contain these substrings
335
- * scriptMustContain: [{ script: "build", mustContain: ["tsc"] }],
336
- * // Only one of these can exist
337
- * mutuallyExclusive: [["lint:eslint", "lint:biome"]],
338
339
  * }),
339
340
  * ],
340
341
  * };
341
342
  * ```
342
343
  *
343
- * @alpha
344
+ * @public
344
345
  */
345
346
  export const PackageScripts = definePackagePolicy({
346
347
  name: "PackageScripts",
348
+ requiresConfig: true,
347
349
  description: "Validates package.json scripts based on configurable rules including required scripts, exact content, and conditional requirements.",
348
350
  handler: async (json, { file, root, resolve, config }) => {
349
351
  if (config === undefined) {
350
- return true;
352
+ return [];
351
353
  }
352
354
  const scripts = Object.hasOwn(json, "scripts") ? json.scripts : {};
353
355
  const defaultScripts = buildDefaultsMap(config);
354
356
  const { errors, missingWithDefaults, mismatchedScripts } = runAllValidations(config, scripts, defaultScripts);
355
357
  if (errors.length === 0) {
356
- return true;
358
+ return [];
357
359
  }
358
360
  const hasAutoFixableErrors = missingWithDefaults.length > 0 || mismatchedScripts.length > 0;
359
361
  if (resolve && hasAutoFixableErrors) {
@@ -361,27 +363,28 @@ export const PackageScripts = definePackagePolicy({
361
363
  try {
362
364
  await jsonfile.writeFile(resolvePath(root, file), { ...json, scripts: updatedScripts }, { spaces: 2 });
363
365
  const postFixErrors = revalidateAfterFix(config, updatedScripts);
364
- return {
365
- name: POLICY_NAME,
366
- file,
367
- resolved: postFixErrors.length === 0,
368
- errorMessages: postFixErrors.length > 0
369
- ? [
370
- `Fixed scripts: ${fixedScripts.join(", ")}. Remaining errors:\n${postFixErrors.join("\n\n")}`,
371
- ]
372
- : [`Fixed scripts: ${fixedScripts.join(", ")}`],
373
- };
366
+ return [
367
+ ...fixedScripts.map((name) => ({
368
+ message: `Fixed scripts: ${name}`,
369
+ fixStatus: "fixed",
370
+ })),
371
+ ...postFixErrors.map((message) => ({
372
+ message: `Remaining errors:\n${message}`,
373
+ })),
374
+ ];
374
375
  }
375
376
  catch {
376
377
  // Fall through to return regular failure
377
378
  }
378
379
  }
379
- return {
380
- name: POLICY_NAME,
381
- file,
382
- autoFixable: hasAutoFixableErrors,
383
- errorMessages: errors,
384
- };
380
+ return errors.map((message) => ({
381
+ message,
382
+ fixStatus: hasAutoFixableErrors
383
+ ? resolve
384
+ ? "failed"
385
+ : "available"
386
+ : undefined,
387
+ }));
385
388
  },
386
389
  });
387
390
  //# sourceMappingURL=PackageScripts.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"PackageScripts.js","sourceRoot":"","sources":["../../src/policies/PackageScripts.ts"],"names":[],"mappings":"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","sourcesContent":["import jsonfile from \"jsonfile\";\nimport { resolve as resolvePath } from \"pathe\";\nimport type { PackageJson } from \"type-fest\";\nimport { definePackagePolicy } from \"../policyDefiners/definePackagePolicy.js\";\n\n/**\n * Configuration for validating that a script's body contains required substrings.\n *\n * @alpha\n */\nexport interface ScriptMustContainRule {\n\t/**\n\t * The name of the script to validate.\n\t */\n\tscript: string;\n\n\t/**\n\t * Substrings that must appear in the script body.\n\t * All substrings must be present for the script to pass validation.\n\t */\n\tmustContain: string[];\n}\n\n/**\n * Configuration for conditional script requirements.\n *\n * @alpha\n */\nexport interface ConditionalScriptRule {\n\t/**\n\t * The script that triggers the requirement.\n\t * If this script exists, then `requires` must also exist.\n\t */\n\tifPresent: string;\n\n\t/**\n\t * Scripts that must be present if `ifPresent` script exists.\n\t *\n\t * @remarks\n\t *\n\t * Each entry can be:\n\t * - A string: Script must exist, no auto-fix available\n\t * - An object `{ scriptName: \"default value\" }`: Script must exist, auto-fix adds the\n\t * default value if missing\n\t *\n\t * @example\n\t * ```typescript\n\t * requires: [\"test\", { clean: \"rimraf dist\" }]\n\t * ```\n\t */\n\trequires: RequiredScriptEntry[];\n}\n\n/**\n * A required script entry. Can be either:\n * - A string (script name only, no default value)\n * - An object with a single key-value pair (script name → default value)\n *\n * @example\n * ```typescript\n * // Script must exist, no auto-fix available\n * \"test\"\n *\n * // Script must exist, auto-fix will add this value if missing\n * { lint: \"biome lint .\" }\n * ```\n *\n * @alpha\n */\nexport type RequiredScriptEntry = string | Record<string, string>;\n\n/**\n * Policy settings for the PackageScripts repo policy.\n *\n * @alpha\n */\nexport interface PackageScriptsSettings {\n\t/**\n\t * Scripts that must be present in package.json.\n\t *\n\t * @remarks\n\t *\n\t * Each entry can be:\n\t * - A string: Script must exist, no auto-fix available, content not validated\n\t * - An object `{ scriptName: \"default value\" }`: Script must exist, auto-fix adds the\n\t * default value if missing, but existing scripts with different content still pass\n\t *\n\t * @example\n\t * ```typescript\n\t * must: [\n\t * \"test\", // Must exist, no auto-fix\n\t * { lint: \"biome lint .\" }, // Must exist, auto-fix available\n\t * { clean: \"rimraf dist esm\" }, // Must exist, auto-fix available\n\t * ]\n\t * ```\n\t */\n\tmust?: RequiredScriptEntry[];\n\n\t/**\n\t * Scripts that must exist AND have exact content.\n\t *\n\t * @remarks\n\t *\n\t * Unlike {@link PackageScriptsSettings.must}, scripts in `exact` are validated against\n\t * the specified value. If a script exists but has different content, it fails validation.\n\t * Both missing and mismatched scripts are auto-fixable.\n\t *\n\t * @example\n\t * ```typescript\n\t * exact: {\n\t * clean: \"rimraf dist esm\",\n\t * format: \"biome format --write .\",\n\t * }\n\t * ```\n\t */\n\texact?: Record<string, string>;\n\n\t/**\n\t * List of mutually exclusive script groups. Each group is an array of script names\n\t * where at most one script from the group can be present.\n\t *\n\t * Packages can have zero scripts from the group (all optional), but cannot have\n\t * multiple scripts from the same group.\n\t *\n\t * @example\n\t * ```typescript\n\t * // Allow \"test:unit\" OR \"test:vitest\" OR neither, but not both\n\t * mutuallyExclusive: [[\"test:unit\", \"test:vitest\"]]\n\t * ```\n\t */\n\tmutuallyExclusive?: string[][];\n\n\t/**\n\t * Conditional script requirements. If a script is present, other scripts must also exist.\n\t *\n\t * @remarks\n\t *\n\t * This is useful for enforcing that packages with a \"build\" script also have a \"clean\" script,\n\t * or packages with \"test\" also have \"test:coverage\".\n\t *\n\t * The `requires` field accepts the same format as {@link PackageScriptsSettings.must}:\n\t * - Strings: Script must exist, no auto-fix available\n\t * - Objects `{ scriptName: \"default\" }`: Script must exist, auto-fix adds the default if missing\n\t *\n\t * @example\n\t * ```typescript\n\t * // If \"build\" exists, \"clean\" must also exist (with auto-fix default)\n\t * conditionalRequired: [{ ifPresent: \"build\", requires: [{ clean: \"rimraf dist\" }] }]\n\t *\n\t * // Mixed: \"test\" required without default, \"clean\" with default\n\t * conditionalRequired: [{ ifPresent: \"build\", requires: [\"test\", { clean: \"rimraf dist\" }] }]\n\t * ```\n\t */\n\tconditionalRequired?: ConditionalScriptRule[];\n\n\t/**\n\t * Rules for validating script body content.\n\t *\n\t * @remarks\n\t *\n\t * This allows you to ensure that specific scripts contain required commands or patterns\n\t * without requiring an exact match. For example, requiring that the \"clean\" script uses\n\t * \"rimraf\" or that \"build\" includes \"tsc\".\n\t *\n\t * For exact content matching, use {@link PackageScriptsSettings.exact} instead.\n\t *\n\t * @example\n\t * ```typescript\n\t * scriptMustContain: [\n\t * { script: \"clean\", mustContain: [\"rimraf\"] },\n\t * { script: \"build\", mustContain: [\"tsc\"] },\n\t * ]\n\t * ```\n\t */\n\tscriptMustContain?: ScriptMustContainRule[];\n}\n\nconst POLICY_NAME = \"PackageScripts\";\n\n/**\n * Extracts script name and optional default value from a RequiredScriptEntry.\n */\nfunction parseRequiredEntry(entry: RequiredScriptEntry): {\n\tname: string;\n\tdefaultValue?: string;\n} {\n\tif (typeof entry === \"string\") {\n\t\treturn { name: entry };\n\t}\n\tconst keys = Object.keys(entry);\n\tif (keys.length !== 1) {\n\t\tthrow new Error(\n\t\t\t`Invalid required script entry: expected single key-value pair, got ${keys.length} keys`,\n\t\t);\n\t}\n\t// biome-ignore lint/style/noNonNullAssertion: we verified length is 1\n\tconst name = keys[0]!;\n\tconst defaultValue = entry[name];\n\t// Only include defaultValue if it's defined (exactOptionalPropertyTypes)\n\treturn defaultValue !== undefined ? { name, defaultValue } : { name };\n}\n\n/**\n * Extract defaults from RequiredScriptEntry array.\n */\nfunction extractDefaultsFromEntries(\n\tentries: RequiredScriptEntry[],\n\tdefaults: Record<string, string>,\n): void {\n\tfor (const entry of entries) {\n\t\tconst { name, defaultValue } = parseRequiredEntry(entry);\n\t\tif (defaultValue !== undefined) {\n\t\t\tdefaults[name] = defaultValue;\n\t\t}\n\t}\n}\n\n/**\n * Builds a map of script names to their default values from must, conditionalRequired, and exact configs.\n */\nfunction buildDefaultsMap(\n\tconfig: PackageScriptsSettings,\n): Record<string, string> {\n\tconst defaults: Record<string, string> = {};\n\n\t// Add defaults from must entries\n\tif (config.must) {\n\t\textractDefaultsFromEntries(config.must, defaults);\n\t}\n\n\t// Add defaults from conditionalRequired entries\n\tif (config.conditionalRequired) {\n\t\tfor (const rule of config.conditionalRequired) {\n\t\t\textractDefaultsFromEntries(rule.requires, defaults);\n\t\t}\n\t}\n\n\t// Add defaults from exact (these override must/conditionalRequired if both specified)\n\tif (config.exact) {\n\t\tObject.assign(defaults, config.exact);\n\t}\n\n\treturn defaults;\n}\n\n/**\n * Validates mutually exclusive script groups and returns error messages for violations.\n */\nfunction validateMutuallyExclusiveScripts(\n\tgroups: string[][],\n\tscripts: PackageJson[\"scripts\"],\n): string[] {\n\tconst errors: string[] = [];\n\n\tfor (const group of groups) {\n\t\tconst presentScripts = group.filter(\n\t\t\t(scriptName) => scripts && Object.hasOwn(scripts, scriptName),\n\t\t);\n\n\t\t// Only fail if MORE than one script from the group is present\n\t\tif (presentScripts.length > 1) {\n\t\t\terrors.push(\n\t\t\t\t`Scripts are mutually exclusive, but found ${presentScripts.length}: ${presentScripts.join(\", \")}`,\n\t\t\t);\n\t\t}\n\t}\n\n\treturn errors;\n}\n\n/**\n * Validates conditional script requirements (if X exists, Y must also exist).\n */\nfunction validateConditionalRequirements(\n\trules: ConditionalScriptRule[],\n\tscripts: PackageJson[\"scripts\"],\n): string[] {\n\tconst errors: string[] = [];\n\n\tfor (const rule of rules) {\n\t\t// Check if the triggering script exists\n\t\tif (scripts && Object.hasOwn(scripts, rule.ifPresent)) {\n\t\t\t// Find missing required scripts\n\t\t\tconst missingRequired: string[] = [];\n\t\t\tfor (const entry of rule.requires) {\n\t\t\t\tconst { name } = parseRequiredEntry(entry);\n\t\t\t\tif (!Object.hasOwn(scripts, name)) {\n\t\t\t\t\tmissingRequired.push(name);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (missingRequired.length > 0) {\n\t\t\t\terrors.push(\n\t\t\t\t\t`Script \"${rule.ifPresent}\" is present, but required companion script(s) missing: ${missingRequired.join(\", \")}`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn errors;\n}\n\n/**\n * Validates that script bodies contain required substrings.\n */\nfunction validateScriptContents(\n\trules: ScriptMustContainRule[],\n\tscripts: PackageJson[\"scripts\"],\n): string[] {\n\tconst errors: string[] = [];\n\n\tfor (const rule of rules) {\n\t\t// Only validate if the script exists\n\t\tif (scripts && Object.hasOwn(scripts, rule.script)) {\n\t\t\tconst scriptBody = scripts[rule.script];\n\t\t\tif (typeof scriptBody === \"string\") {\n\t\t\t\tconst missingSubstrings = rule.mustContain.filter(\n\t\t\t\t\t(substring) => !scriptBody.includes(substring),\n\t\t\t\t);\n\n\t\t\t\tif (missingSubstrings.length > 0) {\n\t\t\t\t\terrors.push(\n\t\t\t\t\t\t`Script \"${rule.script}\" must contain: ${missingSubstrings.join(\", \")}`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn errors;\n}\n\n/**\n * Result of validating required scripts.\n */\ninterface MustScriptsResult {\n\terrors: string[];\n\tmissingWithDefaults: string[];\n}\n\n/**\n * Validates required scripts from 'must' config.\n */\nfunction validateMustScripts(\n\tmust: RequiredScriptEntry[],\n\tscripts: PackageJson[\"scripts\"],\n): MustScriptsResult {\n\tconst missingScripts: string[] = [];\n\tconst missingWithDefaults: string[] = [];\n\n\tfor (const entry of must) {\n\t\tconst { name, defaultValue } = parseRequiredEntry(entry);\n\t\tif (!(scripts && Object.hasOwn(scripts, name))) {\n\t\t\tmissingScripts.push(name);\n\t\t\tif (defaultValue !== undefined) {\n\t\t\t\tmissingWithDefaults.push(name);\n\t\t\t}\n\t\t}\n\t}\n\n\tconst errors: string[] = [];\n\tif (missingScripts.length > 0) {\n\t\terrors.push(`Missing required scripts:\\n\\t${missingScripts.join(\"\\n\\t\")}`);\n\t}\n\n\treturn { errors, missingWithDefaults };\n}\n\n/**\n * Result of validating exact scripts.\n */\ninterface ExactScriptsResult {\n\terrors: string[];\n\tmissingWithDefaults: string[];\n\tmismatchedScripts: string[];\n}\n\n/**\n * Validates scripts from 'exact' config - must exist AND match exactly.\n */\nfunction validateExactScripts(\n\texact: Record<string, string>,\n\tscripts: PackageJson[\"scripts\"],\n): ExactScriptsResult {\n\tconst errors: string[] = [];\n\tconst missingWithDefaults: string[] = [];\n\tconst mismatchedScripts: string[] = [];\n\n\tfor (const [scriptName, expectedValue] of Object.entries(exact)) {\n\t\tif (scripts && Object.hasOwn(scripts, scriptName)) {\n\t\t\tconst actualValue = scripts[scriptName];\n\t\t\tif (actualValue !== expectedValue) {\n\t\t\t\tmismatchedScripts.push(scriptName);\n\t\t\t\terrors.push(\n\t\t\t\t\t`Script \"${scriptName}\" must be \"${expectedValue}\", but found \"${actualValue}\"`,\n\t\t\t\t);\n\t\t\t}\n\t\t} else {\n\t\t\tmissingWithDefaults.push(scriptName);\n\t\t\terrors.push(`Missing required script: ${scriptName}`);\n\t\t}\n\t}\n\n\treturn { errors, missingWithDefaults, mismatchedScripts };\n}\n\n/**\n * Result of validating conditional requirements.\n */\ninterface ConditionalResult {\n\terrors: string[];\n\tmissingWithDefaults: string[];\n}\n\n/**\n * Find missing scripts from a conditional rule's requirements.\n */\nfunction findMissingFromRule(\n\trule: ConditionalScriptRule,\n\tscripts: PackageJson[\"scripts\"],\n): string[] {\n\treturn rule.requires\n\t\t.map((entry) => parseRequiredEntry(entry).name)\n\t\t.filter((name) => !(scripts && Object.hasOwn(scripts, name)));\n}\n\n/**\n * Validates conditional requirements and tracks missing scripts with defaults.\n */\nfunction validateConditionalWithDefaults(\n\trules: ConditionalScriptRule[],\n\tscripts: PackageJson[\"scripts\"],\n\tdefaultScripts: Record<string, string>,\n\texistingMissingWithDefaults: string[],\n): ConditionalResult {\n\tconst errors: string[] = [];\n\tconst missingWithDefaults: string[] = [];\n\tconst allTracked = new Set(existingMissingWithDefaults);\n\n\tfor (const rule of rules) {\n\t\tif (!(scripts && Object.hasOwn(scripts, rule.ifPresent))) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst missingRequired = findMissingFromRule(rule, scripts);\n\n\t\tfor (const scriptName of missingRequired) {\n\t\t\tif (scriptName in defaultScripts && !allTracked.has(scriptName)) {\n\t\t\t\tmissingWithDefaults.push(scriptName);\n\t\t\t\tallTracked.add(scriptName);\n\t\t\t}\n\t\t}\n\n\t\tif (missingRequired.length > 0) {\n\t\t\terrors.push(\n\t\t\t\t`Script \"${rule.ifPresent}\" is present, but required companion script(s) missing: ${missingRequired.join(\", \")}`,\n\t\t\t);\n\t\t}\n\t}\n\n\treturn { errors, missingWithDefaults };\n}\n\n/**\n * Apply fixes to scripts and return the updated scripts object.\n */\nfunction applyScriptFixes(\n\tscripts: PackageJson[\"scripts\"],\n\tmissingScriptsWithDefaults: string[],\n\tmismatchedScripts: string[],\n\tdefaultScripts: Record<string, string>,\n): { updatedScripts: Record<string, string>; fixedScripts: string[] } {\n\tconst updatedScripts: Record<string, string> = {};\n\n\t// Copy existing scripts, filtering out undefined values\n\tif (scripts) {\n\t\tfor (const [key, value] of Object.entries(scripts)) {\n\t\t\tif (value !== undefined) {\n\t\t\t\tupdatedScripts[key] = value;\n\t\t\t}\n\t\t}\n\t}\n\n\tconst fixedScripts: string[] = [];\n\n\tfor (const scriptName of missingScriptsWithDefaults) {\n\t\tconst defaultValue = defaultScripts[scriptName];\n\t\tif (defaultValue !== undefined) {\n\t\t\tupdatedScripts[scriptName] = defaultValue;\n\t\t\tfixedScripts.push(scriptName);\n\t\t}\n\t}\n\n\tfor (const scriptName of mismatchedScripts) {\n\t\tconst defaultValue = defaultScripts[scriptName];\n\t\tif (defaultValue !== undefined && !fixedScripts.includes(scriptName)) {\n\t\t\tupdatedScripts[scriptName] = defaultValue;\n\t\t\tfixedScripts.push(scriptName);\n\t\t}\n\t}\n\n\treturn { updatedScripts, fixedScripts };\n}\n\n/**\n * Validate 'must' scripts and return any remaining missing scripts.\n */\nfunction validateRemainingMustScripts(\n\tmust: RequiredScriptEntry[],\n\tscripts: Record<string, string>,\n): string[] {\n\tconst stillMissing = must\n\t\t.map((entry) => parseRequiredEntry(entry).name)\n\t\t.filter((name) => !Object.hasOwn(scripts, name));\n\n\tif (stillMissing.length > 0) {\n\t\treturn [`Missing required scripts:\\n\\t${stillMissing.join(\"\\n\\t\")}`];\n\t}\n\treturn [];\n}\n\n/**\n * Validate 'exact' scripts and return any errors.\n */\nfunction validateRemainingExactScripts(\n\texact: Record<string, string>,\n\tscripts: Record<string, string>,\n): string[] {\n\tconst errors: string[] = [];\n\tfor (const [scriptName, expectedValue] of Object.entries(exact)) {\n\t\tif (!Object.hasOwn(scripts, scriptName)) {\n\t\t\terrors.push(`Missing required script: ${scriptName}`);\n\t\t} else if (scripts[scriptName] !== expectedValue) {\n\t\t\terrors.push(\n\t\t\t\t`Script \"${scriptName}\" must be \"${expectedValue}\", but found \"${scripts[scriptName]}\"`,\n\t\t\t);\n\t\t}\n\t}\n\treturn errors;\n}\n\n/**\n * Re-validate all rules after applying fixes.\n */\nfunction revalidateAfterFix(\n\tconfig: PackageScriptsSettings,\n\tupdatedScripts: Record<string, string>,\n): string[] {\n\tconst errors: string[] = [];\n\n\tif (\n\t\tconfig.mutuallyExclusive !== undefined &&\n\t\tconfig.mutuallyExclusive.length > 0\n\t) {\n\t\terrors.push(\n\t\t\t...validateMutuallyExclusiveScripts(\n\t\t\t\tconfig.mutuallyExclusive,\n\t\t\t\tupdatedScripts,\n\t\t\t),\n\t\t);\n\t}\n\n\tif (\n\t\tconfig.scriptMustContain !== undefined &&\n\t\tconfig.scriptMustContain.length > 0\n\t) {\n\t\terrors.push(\n\t\t\t...validateScriptContents(config.scriptMustContain, updatedScripts),\n\t\t);\n\t}\n\n\tif (config.must !== undefined && config.must.length > 0) {\n\t\terrors.push(...validateRemainingMustScripts(config.must, updatedScripts));\n\t}\n\n\tif (config.exact !== undefined && Object.keys(config.exact).length > 0) {\n\t\terrors.push(...validateRemainingExactScripts(config.exact, updatedScripts));\n\t}\n\n\tif (\n\t\tconfig.conditionalRequired !== undefined &&\n\t\tconfig.conditionalRequired.length > 0\n\t) {\n\t\terrors.push(\n\t\t\t...validateConditionalRequirements(\n\t\t\t\tconfig.conditionalRequired,\n\t\t\t\tupdatedScripts,\n\t\t\t),\n\t\t);\n\t}\n\n\treturn errors;\n}\n\n/**\n * Result of running all script validations.\n */\ninterface AllValidationsResult {\n\terrors: string[];\n\tmissingWithDefaults: string[];\n\tmismatchedScripts: string[];\n}\n\n/**\n * Run all script validations and aggregate results.\n */\nfunction runAllValidations(\n\tconfig: PackageScriptsSettings,\n\tscripts: PackageJson[\"scripts\"],\n\tdefaultScripts: Record<string, string>,\n): AllValidationsResult {\n\tconst errors: string[] = [];\n\tconst missingWithDefaults: string[] = [];\n\tlet mismatchedScripts: string[] = [];\n\n\tif (config.must !== undefined && config.must.length > 0) {\n\t\tconst result = validateMustScripts(config.must, scripts);\n\t\terrors.push(...result.errors);\n\t\tmissingWithDefaults.push(...result.missingWithDefaults);\n\t}\n\n\tif (config.exact !== undefined && Object.keys(config.exact).length > 0) {\n\t\tconst result = validateExactScripts(config.exact, scripts);\n\t\terrors.push(...result.errors);\n\t\tmissingWithDefaults.push(...result.missingWithDefaults);\n\t\tmismatchedScripts = result.mismatchedScripts;\n\t}\n\n\tif (\n\t\tconfig.mutuallyExclusive !== undefined &&\n\t\tconfig.mutuallyExclusive.length > 0\n\t) {\n\t\terrors.push(\n\t\t\t...validateMutuallyExclusiveScripts(config.mutuallyExclusive, scripts),\n\t\t);\n\t}\n\n\tif (\n\t\tconfig.conditionalRequired !== undefined &&\n\t\tconfig.conditionalRequired.length > 0\n\t) {\n\t\tconst result = validateConditionalWithDefaults(\n\t\t\tconfig.conditionalRequired,\n\t\t\tscripts,\n\t\t\tdefaultScripts,\n\t\t\tmissingWithDefaults,\n\t\t);\n\t\terrors.push(...result.errors);\n\t\tmissingWithDefaults.push(...result.missingWithDefaults);\n\t}\n\n\tif (\n\t\tconfig.scriptMustContain !== undefined &&\n\t\tconfig.scriptMustContain.length > 0\n\t) {\n\t\terrors.push(...validateScriptContents(config.scriptMustContain, scripts));\n\t}\n\n\treturn { errors, missingWithDefaults, mismatchedScripts };\n}\n\n/**\n * A RepoPolicy that validates package.json scripts based on configurable rules.\n *\n * @remarks\n *\n * This policy provides comprehensive script validation including:\n * - Required scripts that must be present (with optional defaults for auto-fix)\n * - Exact script content enforcement\n * - Mutually exclusive script groups (at most one can exist)\n * - Conditional requirements (if X exists, Y must also exist)\n * - Script body content validation (script must contain specific substrings)\n *\n * @example\n * ```typescript\n * import { makePolicy } from \"repopo\";\n * import { PackageScripts } from \"repopo/policies\";\n *\n * const config: RepopoConfig = {\n * policies: [\n * makePolicy(PackageScripts, {\n * // Scripts that must exist\n * must: [\n * \"test\", // Must exist, no auto-fix\n * { build: \"tsc\" }, // Must exist, auto-fix available\n * { lint: \"biome lint .\" }, // Must exist, auto-fix available\n * ],\n * // Scripts that must exist AND match exactly\n * exact: {\n * clean: \"rimraf dist esm\",\n * format: \"biome format --write .\",\n * },\n * // If \"build\" exists, \"clean\" must also exist (with inline auto-fix default)\n * conditionalRequired: [{ ifPresent: \"build\", requires: [{ clean: \"rimraf dist\" }] }],\n * // Script body must contain these substrings\n * scriptMustContain: [{ script: \"build\", mustContain: [\"tsc\"] }],\n * // Only one of these can exist\n * mutuallyExclusive: [[\"lint:eslint\", \"lint:biome\"]],\n * }),\n * ],\n * };\n * ```\n *\n * @alpha\n */\nexport const PackageScripts = definePackagePolicy<\n\tPackageJson,\n\tPackageScriptsSettings | undefined\n>({\n\tname: \"PackageScripts\",\n\tdescription:\n\t\t\"Validates package.json scripts based on configurable rules including required scripts, exact content, and conditional requirements.\",\n\thandler: async (json, { file, root, resolve, config }) => {\n\t\tif (config === undefined) {\n\t\t\treturn true;\n\t\t}\n\n\t\tconst scripts = Object.hasOwn(json, \"scripts\") ? json.scripts : {};\n\t\tconst defaultScripts = buildDefaultsMap(config);\n\n\t\tconst { errors, missingWithDefaults, mismatchedScripts } =\n\t\t\trunAllValidations(config, scripts, defaultScripts);\n\n\t\tif (errors.length === 0) {\n\t\t\treturn true;\n\t\t}\n\n\t\tconst hasAutoFixableErrors =\n\t\t\tmissingWithDefaults.length > 0 || mismatchedScripts.length > 0;\n\n\t\tif (resolve && hasAutoFixableErrors) {\n\t\t\tconst { updatedScripts, fixedScripts } = applyScriptFixes(\n\t\t\t\tscripts,\n\t\t\t\tmissingWithDefaults,\n\t\t\t\tmismatchedScripts,\n\t\t\t\tdefaultScripts,\n\t\t\t);\n\n\t\t\ttry {\n\t\t\t\tawait jsonfile.writeFile(\n\t\t\t\t\tresolvePath(root, file),\n\t\t\t\t\t{ ...json, scripts: updatedScripts },\n\t\t\t\t\t{ spaces: 2 },\n\t\t\t\t);\n\t\t\t\tconst postFixErrors = revalidateAfterFix(config, updatedScripts);\n\n\t\t\t\treturn {\n\t\t\t\t\tname: POLICY_NAME,\n\t\t\t\t\tfile,\n\t\t\t\t\tresolved: postFixErrors.length === 0,\n\t\t\t\t\terrorMessages:\n\t\t\t\t\t\tpostFixErrors.length > 0\n\t\t\t\t\t\t\t? [\n\t\t\t\t\t\t\t\t\t`Fixed scripts: ${fixedScripts.join(\", \")}. Remaining errors:\\n${postFixErrors.join(\"\\n\\n\")}`,\n\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t: [`Fixed scripts: ${fixedScripts.join(\", \")}`],\n\t\t\t\t};\n\t\t\t} catch {\n\t\t\t\t// Fall through to return regular failure\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\tname: POLICY_NAME,\n\t\t\tfile,\n\t\t\tautoFixable: hasAutoFixableErrors,\n\t\t\terrorMessages: errors,\n\t\t};\n\t},\n});\n"]}
1
+ {"version":3,"file":"PackageScripts.js","sourceRoot":"","sources":["../../src/policies/PackageScripts.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,UAAU,CAAC;AAChC,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,OAAO,CAAC;AAE/C,OAAO,EAAE,mBAAmB,EAAE,MAAM,0CAA0C,CAAC;AA6K/E;;GAEG;AACH,SAAS,kBAAkB,CAAC,KAA0B;IAIrD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC/B,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;IACxB,CAAC;IACD,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAChC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CACd,sEAAsE,IAAI,CAAC,MAAM,OAAO,CACxF,CAAC;IACH,CAAC;IACD,sEAAsE;IACtE,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,CAAE,CAAC;IACtB,MAAM,YAAY,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC;IACjC,yEAAyE;IACzE,OAAO,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC;AACvE,CAAC;AAED;;GAEG;AACH,SAAS,0BAA0B,CAClC,OAA8B,EAC9B,QAAgC;IAEhC,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC7B,MAAM,EAAE,IAAI,EAAE,YAAY,EAAE,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAC;QACzD,IAAI,YAAY,KAAK,SAAS,EAAE,CAAC;YAChC,QAAQ,CAAC,IAAI,CAAC,GAAG,YAAY,CAAC;QAC/B,CAAC;IACF,CAAC;AACF,CAAC;AAED;;GAEG;AACH,SAAS,gBAAgB,CACxB,MAA8B;IAE9B,MAAM,QAAQ,GAA2B,EAAE,CAAC;IAE5C,iCAAiC;IACjC,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;QACjB,0BAA0B,CAAC,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IACnD,CAAC;IAED,gDAAgD;IAChD,IAAI,MAAM,CAAC,mBAAmB,EAAE,CAAC;QAChC,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,mBAAmB,EAAE,CAAC;YAC/C,0BAA0B,CAAC,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;QACrD,CAAC;IACF,CAAC;IAED,sFAAsF;IACtF,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;QAClB,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;IACvC,CAAC;IAED,OAAO,QAAQ,CAAC;AACjB,CAAC;AAED;;GAEG;AACH,SAAS,gCAAgC,CACxC,MAAkB,EAClB,OAA+B;IAE/B,MAAM,MAAM,GAAa,EAAE,CAAC;IAE5B,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC5B,MAAM,cAAc,GAAG,KAAK,CAAC,MAAM,CAClC,CAAC,UAAU,EAAE,EAAE,CAAC,OAAO,IAAI,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,CAC7D,CAAC;QAEF,8DAA8D;QAC9D,IAAI,cAAc,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC/B,MAAM,CAAC,IAAI,CACV,6CAA6C,cAAc,CAAC,MAAM,KAAK,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAClG,CAAC;QACH,CAAC;IACF,CAAC;IAED,OAAO,MAAM,CAAC;AACf,CAAC;AAED;;GAEG;AACH,SAAS,+BAA+B,CACvC,KAA8B,EAC9B,OAA+B;IAE/B,MAAM,MAAM,GAAa,EAAE,CAAC;IAE5B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QAC1B,wCAAwC;QACxC,IAAI,OAAO,IAAI,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;YACvD,gCAAgC;YAChC,MAAM,eAAe,GAAa,EAAE,CAAC;YACrC,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACnC,MAAM,EAAE,IAAI,EAAE,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAC;gBAC3C,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,EAAE,CAAC;oBACnC,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBAC5B,CAAC;YACF,CAAC;YAED,IAAI,eAAe,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAChC,MAAM,CAAC,IAAI,CACV,WAAW,IAAI,CAAC,SAAS,2DAA2D,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAChH,CAAC;YACH,CAAC;QACF,CAAC;IACF,CAAC;IAED,OAAO,MAAM,CAAC;AACf,CAAC;AAED;;GAEG;AACH,SAAS,sBAAsB,CAC9B,KAA8B,EAC9B,OAA+B;IAE/B,MAAM,MAAM,GAAa,EAAE,CAAC;IAE5B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QAC1B,qCAAqC;QACrC,IAAI,OAAO,IAAI,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;YACpD,MAAM,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACxC,IAAI,OAAO,UAAU,KAAK,QAAQ,EAAE,CAAC;gBACpC,MAAM,iBAAiB,GAAG,IAAI,CAAC,WAAW,CAAC,MAAM,CAChD,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,UAAU,CAAC,QAAQ,CAAC,SAAS,CAAC,CAC9C,CAAC;gBAEF,IAAI,iBAAiB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBAClC,MAAM,CAAC,IAAI,CACV,WAAW,IAAI,CAAC,MAAM,mBAAmB,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CACvE,CAAC;gBACH,CAAC;YACF,CAAC;QACF,CAAC;IACF,CAAC;IAED,OAAO,MAAM,CAAC;AACf,CAAC;AAUD;;GAEG;AACH,SAAS,mBAAmB,CAC3B,IAA2B,EAC3B,OAA+B;IAE/B,MAAM,cAAc,GAAa,EAAE,CAAC;IACpC,MAAM,mBAAmB,GAAa,EAAE,CAAC;IAEzC,KAAK,MAAM,KAAK,IAAI,IAAI,EAAE,CAAC;QAC1B,MAAM,EAAE,IAAI,EAAE,YAAY,EAAE,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAC;QACzD,IAAI,CAAC,CAAC,OAAO,IAAI,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC;YAChD,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC1B,IAAI,YAAY,KAAK,SAAS,EAAE,CAAC;gBAChC,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAChC,CAAC;QACF,CAAC;IACF,CAAC;IAED,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,IAAI,cAAc,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC/B,MAAM,CAAC,IAAI,CAAC,gCAAgC,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IAC5E,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,mBAAmB,EAAE,CAAC;AACxC,CAAC;AAWD;;GAEG;AACH,SAAS,oBAAoB,CAC5B,KAA6B,EAC7B,OAA+B;IAE/B,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,MAAM,mBAAmB,GAAa,EAAE,CAAC;IACzC,MAAM,iBAAiB,GAAa,EAAE,CAAC;IAEvC,KAAK,MAAM,CAAC,UAAU,EAAE,aAAa,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACjE,IAAI,OAAO,IAAI,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,EAAE,CAAC;YACnD,MAAM,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;YACxC,IAAI,WAAW,KAAK,aAAa,EAAE,CAAC;gBACnC,iBAAiB,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;gBACnC,MAAM,CAAC,IAAI,CACV,WAAW,UAAU,cAAc,aAAa,iBAAiB,WAAW,GAAG,CAC/E,CAAC;YACH,CAAC;QACF,CAAC;aAAM,CAAC;YACP,mBAAmB,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YACrC,MAAM,CAAC,IAAI,CAAC,4BAA4B,UAAU,EAAE,CAAC,CAAC;QACvD,CAAC;IACF,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,CAAC;AAC3D,CAAC;AAUD;;GAEG;AACH,SAAS,mBAAmB,CAC3B,IAA2B,EAC3B,OAA+B;IAE/B,OAAO,IAAI,CAAC,QAAQ;SAClB,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC;SAC9C,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,IAAI,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;AAChE,CAAC;AAED;;GAEG;AACH,SAAS,+BAA+B,CACvC,KAA8B,EAC9B,OAA+B,EAC/B,cAAsC,EACtC,2BAAqC;IAErC,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,MAAM,mBAAmB,GAAa,EAAE,CAAC;IACzC,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,2BAA2B,CAAC,CAAC;IAExD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QAC1B,IAAI,CAAC,CAAC,OAAO,IAAI,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC;YAC1D,SAAS;QACV,CAAC;QAED,MAAM,eAAe,GAAG,mBAAmB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAE3D,KAAK,MAAM,UAAU,IAAI,eAAe,EAAE,CAAC;YAC1C,IAAI,UAAU,IAAI,cAAc,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC;gBACjE,mBAAmB,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;gBACrC,UAAU,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;YAC5B,CAAC;QACF,CAAC;QAED,IAAI,eAAe,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAChC,MAAM,CAAC,IAAI,CACV,WAAW,IAAI,CAAC,SAAS,2DAA2D,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAChH,CAAC;QACH,CAAC;IACF,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,mBAAmB,EAAE,CAAC;AACxC,CAAC;AAED;;GAEG;AACH,SAAS,gBAAgB,CACxB,OAA+B,EAC/B,0BAAoC,EACpC,iBAA2B,EAC3B,cAAsC;IAKtC,MAAM,cAAc,GAA2B,EAAE,CAAC;IAElD,wDAAwD;IACxD,IAAI,OAAO,EAAE,CAAC;QACb,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;YACpD,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;gBACzB,cAAc,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;YAC7B,CAAC;QACF,CAAC;IACF,CAAC;IAED,MAAM,YAAY,GAAa,EAAE,CAAC;IAElC,KAAK,MAAM,UAAU,IAAI,0BAA0B,EAAE,CAAC;QACrD,MAAM,YAAY,GAAG,cAAc,CAAC,UAAU,CAAC,CAAC;QAChD,IAAI,YAAY,KAAK,SAAS,EAAE,CAAC;YAChC,cAAc,CAAC,UAAU,CAAC,GAAG,YAAY,CAAC;YAC1C,YAAY,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAC/B,CAAC;IACF,CAAC;IAED,KAAK,MAAM,UAAU,IAAI,iBAAiB,EAAE,CAAC;QAC5C,MAAM,YAAY,GAAG,cAAc,CAAC,UAAU,CAAC,CAAC;QAChD,IAAI,YAAY,KAAK,SAAS,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;YACtE,cAAc,CAAC,UAAU,CAAC,GAAG,YAAY,CAAC;YAC1C,YAAY,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAC/B,CAAC;IACF,CAAC;IAED,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,CAAC;AACzC,CAAC;AAED;;GAEG;AACH,SAAS,4BAA4B,CACpC,IAA2B,EAC3B,OAA+B;IAE/B,MAAM,YAAY,GAAG,IAAI;SACvB,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC;SAC9C,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC;IAElD,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC7B,OAAO,CAAC,gCAAgC,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACtE,CAAC;IACD,OAAO,EAAE,CAAC;AACX,CAAC;AAED;;GAEG;AACH,SAAS,6BAA6B,CACrC,KAA6B,EAC7B,OAA+B;IAE/B,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,KAAK,MAAM,CAAC,UAAU,EAAE,aAAa,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACjE,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,EAAE,CAAC;YACzC,MAAM,CAAC,IAAI,CAAC,4BAA4B,UAAU,EAAE,CAAC,CAAC;QACvD,CAAC;aAAM,IAAI,OAAO,CAAC,UAAU,CAAC,KAAK,aAAa,EAAE,CAAC;YAClD,MAAM,CAAC,IAAI,CACV,WAAW,UAAU,cAAc,aAAa,iBAAiB,OAAO,CAAC,UAAU,CAAC,GAAG,CACvF,CAAC;QACH,CAAC;IACF,CAAC;IACD,OAAO,MAAM,CAAC;AACf,CAAC;AAED;;GAEG;AACH,SAAS,kBAAkB,CAC1B,MAA8B,EAC9B,cAAsC;IAEtC,MAAM,MAAM,GAAa,EAAE,CAAC;IAE5B,IACC,MAAM,CAAC,iBAAiB,KAAK,SAAS;QACtC,MAAM,CAAC,iBAAiB,CAAC,MAAM,GAAG,CAAC,EAClC,CAAC;QACF,MAAM,CAAC,IAAI,CACV,GAAG,gCAAgC,CAClC,MAAM,CAAC,iBAAiB,EACxB,cAAc,CACd,CACD,CAAC;IACH,CAAC;IAED,IACC,MAAM,CAAC,iBAAiB,KAAK,SAAS;QACtC,MAAM,CAAC,iBAAiB,CAAC,MAAM,GAAG,CAAC,EAClC,CAAC;QACF,MAAM,CAAC,IAAI,CACV,GAAG,sBAAsB,CAAC,MAAM,CAAC,iBAAiB,EAAE,cAAc,CAAC,CACnE,CAAC;IACH,CAAC;IAED,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACzD,MAAM,CAAC,IAAI,CAAC,GAAG,4BAA4B,CAAC,MAAM,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC,CAAC;IAC3E,CAAC;IAED,IAAI,MAAM,CAAC,KAAK,KAAK,SAAS,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxE,MAAM,CAAC,IAAI,CAAC,GAAG,6BAA6B,CAAC,MAAM,CAAC,KAAK,EAAE,cAAc,CAAC,CAAC,CAAC;IAC7E,CAAC;IAED,IACC,MAAM,CAAC,mBAAmB,KAAK,SAAS;QACxC,MAAM,CAAC,mBAAmB,CAAC,MAAM,GAAG,CAAC,EACpC,CAAC;QACF,MAAM,CAAC,IAAI,CACV,GAAG,+BAA+B,CACjC,MAAM,CAAC,mBAAmB,EAC1B,cAAc,CACd,CACD,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AACf,CAAC;AAWD;;GAEG;AACH,SAAS,iBAAiB,CACzB,MAA8B,EAC9B,OAA+B,EAC/B,cAAsC;IAEtC,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,MAAM,mBAAmB,GAAa,EAAE,CAAC;IACzC,IAAI,iBAAiB,GAAa,EAAE,CAAC;IAErC,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACzD,MAAM,MAAM,GAAG,mBAAmB,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QACzD,MAAM,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;QAC9B,mBAAmB,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,mBAAmB,CAAC,CAAC;IACzD,CAAC;IAED,IAAI,MAAM,CAAC,KAAK,KAAK,SAAS,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxE,MAAM,MAAM,GAAG,oBAAoB,CAAC,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;QAC3D,MAAM,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;QAC9B,mBAAmB,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,mBAAmB,CAAC,CAAC;QACxD,iBAAiB,GAAG,MAAM,CAAC,iBAAiB,CAAC;IAC9C,CAAC;IAED,IACC,MAAM,CAAC,iBAAiB,KAAK,SAAS;QACtC,MAAM,CAAC,iBAAiB,CAAC,MAAM,GAAG,CAAC,EAClC,CAAC;QACF,MAAM,CAAC,IAAI,CACV,GAAG,gCAAgC,CAAC,MAAM,CAAC,iBAAiB,EAAE,OAAO,CAAC,CACtE,CAAC;IACH,CAAC;IAED,IACC,MAAM,CAAC,mBAAmB,KAAK,SAAS;QACxC,MAAM,CAAC,mBAAmB,CAAC,MAAM,GAAG,CAAC,EACpC,CAAC;QACF,MAAM,MAAM,GAAG,+BAA+B,CAC7C,MAAM,CAAC,mBAAmB,EAC1B,OAAO,EACP,cAAc,EACd,mBAAmB,CACnB,CAAC;QACF,MAAM,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;QAC9B,mBAAmB,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,mBAAmB,CAAC,CAAC;IACzD,CAAC;IAED,IACC,MAAM,CAAC,iBAAiB,KAAK,SAAS;QACtC,MAAM,CAAC,iBAAiB,CAAC,MAAM,GAAG,CAAC,EAClC,CAAC;QACF,MAAM,CAAC,IAAI,CAAC,GAAG,sBAAsB,CAAC,MAAM,CAAC,iBAAiB,EAAE,OAAO,CAAC,CAAC,CAAC;IAC3E,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,CAAC;AAC3D,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,mBAAmB,CAG/C;IACD,IAAI,EAAE,gBAAgB;IACtB,cAAc,EAAE,IAAI;IACpB,WAAW,EACV,qIAAqI;IACtI,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE;QACxD,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YAC1B,OAAO,EAAE,CAAC;QACX,CAAC;QAED,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;QACnE,MAAM,cAAc,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAC;QAEhD,MAAM,EAAE,MAAM,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,GACvD,iBAAiB,CAAC,MAAM,EAAE,OAAO,EAAE,cAAc,CAAC,CAAC;QAEpD,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACzB,OAAO,EAAE,CAAC;QACX,CAAC;QAED,MAAM,oBAAoB,GACzB,mBAAmB,CAAC,MAAM,GAAG,CAAC,IAAI,iBAAiB,CAAC,MAAM,GAAG,CAAC,CAAC;QAEhE,IAAI,OAAO,IAAI,oBAAoB,EAAE,CAAC;YACrC,MAAM,EAAE,cAAc,EAAE,YAAY,EAAE,GAAG,gBAAgB,CACxD,OAAO,EACP,mBAAmB,EACnB,iBAAiB,EACjB,cAAc,CACd,CAAC;YAEF,IAAI,CAAC;gBACJ,MAAM,QAAQ,CAAC,SAAS,CACvB,WAAW,CAAC,IAAI,EAAE,IAAI,CAAC,EACvB,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,cAAc,EAAE,EACpC,EAAE,MAAM,EAAE,CAAC,EAAE,CACb,CAAC;gBACF,MAAM,aAAa,GAAG,kBAAkB,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;gBACjE,OAAO;oBACN,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;wBAC9B,OAAO,EAAE,kBAAkB,IAAI,EAAE;wBACjC,SAAS,EAAE,OAAgB;qBAC3B,CAAC,CAAC;oBACH,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;wBAClC,OAAO,EAAE,sBAAsB,OAAO,EAAE;qBACxC,CAAC,CAAC;iBACH,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACR,yCAAyC;YAC1C,CAAC;QACF,CAAC;QACD,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;YAC/B,OAAO;YACP,SAAS,EAAE,oBAAoB;gBAC9B,CAAC,CAAC,OAAO;oBACR,CAAC,CAAC,QAAQ;oBACV,CAAC,CAAC,WAAW;gBACd,CAAC,CAAC,SAAS;SACZ,CAAC,CAAC,CAAC;IACL,CAAC;CACD,CAAC,CAAC","sourcesContent":["import jsonfile from \"jsonfile\";\nimport { resolve as resolvePath } from \"pathe\";\nimport type { PackageJson } from \"type-fest\";\nimport { definePackagePolicy } from \"../policyDefiners/definePackagePolicy.js\";\n\n/**\n * Configuration for validating that a script's body contains required substrings.\n *\n * @public\n */\nexport interface ScriptMustContainRule {\n\t/**\n\t * The name of the script to validate.\n\t */\n\tscript: string;\n\n\t/**\n\t * Substrings that must appear in the script body.\n\t * All substrings must be present for the script to pass validation.\n\t */\n\tmustContain: string[];\n}\n\n/**\n * Configuration for conditional script requirements.\n *\n * @public\n */\nexport interface ConditionalScriptRule {\n\t/**\n\t * The script that triggers the requirement.\n\t * If this script exists, then `requires` must also exist.\n\t */\n\tifPresent: string;\n\n\t/**\n\t * Scripts that must be present if `ifPresent` script exists.\n\t *\n\t * @remarks\n\t *\n\t * Each entry can be:\n\t * - A string: Script must exist, no auto-fix available\n\t * - An object `{ scriptName: \"default value\" }`: Script must exist, auto-fix adds the\n\t * default value if missing\n\t *\n\t * @example\n\t * ```typescript\n\t * requires: [\"test\", { clean: \"rimraf dist\" }]\n\t * ```\n\t */\n\trequires: RequiredScriptEntry[];\n}\n\n/**\n * A required script entry. Can be either:\n * - A string (script name only, no default value)\n * - An object with a single key-value pair (script name → default value)\n *\n * @example\n * ```typescript\n * // Script must exist, no auto-fix available\n * \"test\"\n *\n * // Script must exist, auto-fix will add this value if missing\n * { lint: \"biome lint .\" }\n * ```\n *\n * @public\n */\nexport type RequiredScriptEntry = string | Record<string, string>;\n\n/**\n * Policy settings for the PackageScripts repo policy.\n *\n * @public\n */\nexport interface PackageScriptsSettings {\n\t/**\n\t * Scripts that must be present in package.json.\n\t *\n\t * @remarks\n\t *\n\t * Each entry can be:\n\t * - A string: Script must exist, no auto-fix available, content not validated\n\t * - An object `{ scriptName: \"default value\" }`: Script must exist, auto-fix adds the\n\t * default value if missing, but existing scripts with different content still pass\n\t *\n\t * @example\n\t * ```typescript\n\t * must: [\n\t * \"test\", // Must exist, no auto-fix\n\t * { lint: \"biome lint .\" }, // Must exist, auto-fix available\n\t * { clean: \"rimraf dist esm\" }, // Must exist, auto-fix available\n\t * ]\n\t * ```\n\t */\n\tmust?: RequiredScriptEntry[];\n\n\t/**\n\t * Scripts that must exist AND have exact content.\n\t *\n\t * @remarks\n\t *\n\t * Unlike {@link PackageScriptsSettings.must}, scripts in `exact` are validated against\n\t * the specified value. If a script exists but has different content, it fails validation.\n\t * Both missing and mismatched scripts are auto-fixable.\n\t *\n\t * @example\n\t * ```typescript\n\t * exact: {\n\t * clean: \"rimraf dist esm\",\n\t * format: \"biome format --write .\",\n\t * }\n\t * ```\n\t */\n\texact?: Record<string, string>;\n\n\t/**\n\t * List of mutually exclusive script groups. Each group is an array of script names\n\t * where at most one script from the group can be present.\n\t *\n\t * Packages can have zero scripts from the group (all optional), but cannot have\n\t * multiple scripts from the same group.\n\t *\n\t * @example\n\t * ```typescript\n\t * // Allow \"test:unit\" OR \"test:vitest\" OR neither, but not both\n\t * mutuallyExclusive: [[\"test:unit\", \"test:vitest\"]]\n\t * ```\n\t */\n\tmutuallyExclusive?: string[][];\n\n\t/**\n\t * Conditional script requirements. If a script is present, other scripts must also exist.\n\t *\n\t * @remarks\n\t *\n\t * This is useful for enforcing that packages with a \"build\" script also have a \"clean\" script,\n\t * or packages with \"test\" also have \"test:coverage\".\n\t *\n\t * The `requires` field accepts the same format as {@link PackageScriptsSettings.must}:\n\t * - Strings: Script must exist, no auto-fix available\n\t * - Objects `{ scriptName: \"default\" }`: Script must exist, auto-fix adds the default if missing\n\t *\n\t * @example\n\t * ```typescript\n\t * // If \"build\" exists, \"clean\" must also exist (with auto-fix default)\n\t * conditionalRequired: [{ ifPresent: \"build\", requires: [{ clean: \"rimraf dist\" }] }]\n\t *\n\t * // Mixed: \"test\" required without default, \"clean\" with default\n\t * conditionalRequired: [{ ifPresent: \"build\", requires: [\"test\", { clean: \"rimraf dist\" }] }]\n\t * ```\n\t */\n\tconditionalRequired?: ConditionalScriptRule[];\n\n\t/**\n\t * Rules for validating script body content.\n\t *\n\t * @remarks\n\t *\n\t * This allows you to ensure that specific scripts contain required commands or patterns\n\t * without requiring an exact match. For example, requiring that the \"clean\" script uses\n\t * \"rimraf\" or that \"build\" includes \"tsc\".\n\t *\n\t * For exact content matching, use {@link PackageScriptsSettings.exact} instead.\n\t *\n\t * @example\n\t * ```typescript\n\t * scriptMustContain: [\n\t * { script: \"clean\", mustContain: [\"rimraf\"] },\n\t * { script: \"build\", mustContain: [\"tsc\"] },\n\t * ]\n\t * ```\n\t */\n\tscriptMustContain?: ScriptMustContainRule[];\n}\n/**\n * Extracts script name and optional default value from a RequiredScriptEntry.\n */\nfunction parseRequiredEntry(entry: RequiredScriptEntry): {\n\tname: string;\n\tdefaultValue?: string;\n} {\n\tif (typeof entry === \"string\") {\n\t\treturn { name: entry };\n\t}\n\tconst keys = Object.keys(entry);\n\tif (keys.length !== 1) {\n\t\tthrow new Error(\n\t\t\t`Invalid required script entry: expected single key-value pair, got ${keys.length} keys`,\n\t\t);\n\t}\n\t// biome-ignore lint/style/noNonNullAssertion: we verified length is 1\n\tconst name = keys[0]!;\n\tconst defaultValue = entry[name];\n\t// Only include defaultValue if it's defined (exactOptionalPropertyTypes)\n\treturn defaultValue !== undefined ? { name, defaultValue } : { name };\n}\n\n/**\n * Extract defaults from RequiredScriptEntry array.\n */\nfunction extractDefaultsFromEntries(\n\tentries: RequiredScriptEntry[],\n\tdefaults: Record<string, string>,\n): void {\n\tfor (const entry of entries) {\n\t\tconst { name, defaultValue } = parseRequiredEntry(entry);\n\t\tif (defaultValue !== undefined) {\n\t\t\tdefaults[name] = defaultValue;\n\t\t}\n\t}\n}\n\n/**\n * Builds a map of script names to their default values from must, conditionalRequired, and exact configs.\n */\nfunction buildDefaultsMap(\n\tconfig: PackageScriptsSettings,\n): Record<string, string> {\n\tconst defaults: Record<string, string> = {};\n\n\t// Add defaults from must entries\n\tif (config.must) {\n\t\textractDefaultsFromEntries(config.must, defaults);\n\t}\n\n\t// Add defaults from conditionalRequired entries\n\tif (config.conditionalRequired) {\n\t\tfor (const rule of config.conditionalRequired) {\n\t\t\textractDefaultsFromEntries(rule.requires, defaults);\n\t\t}\n\t}\n\n\t// Add defaults from exact (these override must/conditionalRequired if both specified)\n\tif (config.exact) {\n\t\tObject.assign(defaults, config.exact);\n\t}\n\n\treturn defaults;\n}\n\n/**\n * Validates mutually exclusive script groups and returns error messages for violations.\n */\nfunction validateMutuallyExclusiveScripts(\n\tgroups: string[][],\n\tscripts: PackageJson[\"scripts\"],\n): string[] {\n\tconst errors: string[] = [];\n\n\tfor (const group of groups) {\n\t\tconst presentScripts = group.filter(\n\t\t\t(scriptName) => scripts && Object.hasOwn(scripts, scriptName),\n\t\t);\n\n\t\t// Only fail if MORE than one script from the group is present\n\t\tif (presentScripts.length > 1) {\n\t\t\terrors.push(\n\t\t\t\t`Scripts are mutually exclusive, but found ${presentScripts.length}: ${presentScripts.join(\", \")}`,\n\t\t\t);\n\t\t}\n\t}\n\n\treturn errors;\n}\n\n/**\n * Validates conditional script requirements (if X exists, Y must also exist).\n */\nfunction validateConditionalRequirements(\n\trules: ConditionalScriptRule[],\n\tscripts: PackageJson[\"scripts\"],\n): string[] {\n\tconst errors: string[] = [];\n\n\tfor (const rule of rules) {\n\t\t// Check if the triggering script exists\n\t\tif (scripts && Object.hasOwn(scripts, rule.ifPresent)) {\n\t\t\t// Find missing required scripts\n\t\t\tconst missingRequired: string[] = [];\n\t\t\tfor (const entry of rule.requires) {\n\t\t\t\tconst { name } = parseRequiredEntry(entry);\n\t\t\t\tif (!Object.hasOwn(scripts, name)) {\n\t\t\t\t\tmissingRequired.push(name);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (missingRequired.length > 0) {\n\t\t\t\terrors.push(\n\t\t\t\t\t`Script \"${rule.ifPresent}\" is present, but required companion script(s) missing: ${missingRequired.join(\", \")}`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn errors;\n}\n\n/**\n * Validates that script bodies contain required substrings.\n */\nfunction validateScriptContents(\n\trules: ScriptMustContainRule[],\n\tscripts: PackageJson[\"scripts\"],\n): string[] {\n\tconst errors: string[] = [];\n\n\tfor (const rule of rules) {\n\t\t// Only validate if the script exists\n\t\tif (scripts && Object.hasOwn(scripts, rule.script)) {\n\t\t\tconst scriptBody = scripts[rule.script];\n\t\t\tif (typeof scriptBody === \"string\") {\n\t\t\t\tconst missingSubstrings = rule.mustContain.filter(\n\t\t\t\t\t(substring) => !scriptBody.includes(substring),\n\t\t\t\t);\n\n\t\t\t\tif (missingSubstrings.length > 0) {\n\t\t\t\t\terrors.push(\n\t\t\t\t\t\t`Script \"${rule.script}\" must contain: ${missingSubstrings.join(\", \")}`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn errors;\n}\n\n/**\n * Result of validating required scripts.\n */\ninterface MustScriptsResult {\n\terrors: string[];\n\tmissingWithDefaults: string[];\n}\n\n/**\n * Validates required scripts from 'must' config.\n */\nfunction validateMustScripts(\n\tmust: RequiredScriptEntry[],\n\tscripts: PackageJson[\"scripts\"],\n): MustScriptsResult {\n\tconst missingScripts: string[] = [];\n\tconst missingWithDefaults: string[] = [];\n\n\tfor (const entry of must) {\n\t\tconst { name, defaultValue } = parseRequiredEntry(entry);\n\t\tif (!(scripts && Object.hasOwn(scripts, name))) {\n\t\t\tmissingScripts.push(name);\n\t\t\tif (defaultValue !== undefined) {\n\t\t\t\tmissingWithDefaults.push(name);\n\t\t\t}\n\t\t}\n\t}\n\n\tconst errors: string[] = [];\n\tif (missingScripts.length > 0) {\n\t\terrors.push(`Missing required scripts:\\n\\t${missingScripts.join(\"\\n\\t\")}`);\n\t}\n\n\treturn { errors, missingWithDefaults };\n}\n\n/**\n * Result of validating exact scripts.\n */\ninterface ExactScriptsResult {\n\terrors: string[];\n\tmissingWithDefaults: string[];\n\tmismatchedScripts: string[];\n}\n\n/**\n * Validates scripts from 'exact' config - must exist AND match exactly.\n */\nfunction validateExactScripts(\n\texact: Record<string, string>,\n\tscripts: PackageJson[\"scripts\"],\n): ExactScriptsResult {\n\tconst errors: string[] = [];\n\tconst missingWithDefaults: string[] = [];\n\tconst mismatchedScripts: string[] = [];\n\n\tfor (const [scriptName, expectedValue] of Object.entries(exact)) {\n\t\tif (scripts && Object.hasOwn(scripts, scriptName)) {\n\t\t\tconst actualValue = scripts[scriptName];\n\t\t\tif (actualValue !== expectedValue) {\n\t\t\t\tmismatchedScripts.push(scriptName);\n\t\t\t\terrors.push(\n\t\t\t\t\t`Script \"${scriptName}\" must be \"${expectedValue}\", but found \"${actualValue}\"`,\n\t\t\t\t);\n\t\t\t}\n\t\t} else {\n\t\t\tmissingWithDefaults.push(scriptName);\n\t\t\terrors.push(`Missing required script: ${scriptName}`);\n\t\t}\n\t}\n\n\treturn { errors, missingWithDefaults, mismatchedScripts };\n}\n\n/**\n * Result of validating conditional requirements.\n */\ninterface ConditionalResult {\n\terrors: string[];\n\tmissingWithDefaults: string[];\n}\n\n/**\n * Find missing scripts from a conditional rule's requirements.\n */\nfunction findMissingFromRule(\n\trule: ConditionalScriptRule,\n\tscripts: PackageJson[\"scripts\"],\n): string[] {\n\treturn rule.requires\n\t\t.map((entry) => parseRequiredEntry(entry).name)\n\t\t.filter((name) => !(scripts && Object.hasOwn(scripts, name)));\n}\n\n/**\n * Validates conditional requirements and tracks missing scripts with defaults.\n */\nfunction validateConditionalWithDefaults(\n\trules: ConditionalScriptRule[],\n\tscripts: PackageJson[\"scripts\"],\n\tdefaultScripts: Record<string, string>,\n\texistingMissingWithDefaults: string[],\n): ConditionalResult {\n\tconst errors: string[] = [];\n\tconst missingWithDefaults: string[] = [];\n\tconst allTracked = new Set(existingMissingWithDefaults);\n\n\tfor (const rule of rules) {\n\t\tif (!(scripts && Object.hasOwn(scripts, rule.ifPresent))) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst missingRequired = findMissingFromRule(rule, scripts);\n\n\t\tfor (const scriptName of missingRequired) {\n\t\t\tif (scriptName in defaultScripts && !allTracked.has(scriptName)) {\n\t\t\t\tmissingWithDefaults.push(scriptName);\n\t\t\t\tallTracked.add(scriptName);\n\t\t\t}\n\t\t}\n\n\t\tif (missingRequired.length > 0) {\n\t\t\terrors.push(\n\t\t\t\t`Script \"${rule.ifPresent}\" is present, but required companion script(s) missing: ${missingRequired.join(\", \")}`,\n\t\t\t);\n\t\t}\n\t}\n\n\treturn { errors, missingWithDefaults };\n}\n\n/**\n * Apply fixes to scripts and return the updated scripts object.\n */\nfunction applyScriptFixes(\n\tscripts: PackageJson[\"scripts\"],\n\tmissingScriptsWithDefaults: string[],\n\tmismatchedScripts: string[],\n\tdefaultScripts: Record<string, string>,\n): {\n\tupdatedScripts: Record<string, string>;\n\tfixedScripts: string[];\n} {\n\tconst updatedScripts: Record<string, string> = {};\n\n\t// Copy existing scripts, filtering out undefined values\n\tif (scripts) {\n\t\tfor (const [key, value] of Object.entries(scripts)) {\n\t\t\tif (value !== undefined) {\n\t\t\t\tupdatedScripts[key] = value;\n\t\t\t}\n\t\t}\n\t}\n\n\tconst fixedScripts: string[] = [];\n\n\tfor (const scriptName of missingScriptsWithDefaults) {\n\t\tconst defaultValue = defaultScripts[scriptName];\n\t\tif (defaultValue !== undefined) {\n\t\t\tupdatedScripts[scriptName] = defaultValue;\n\t\t\tfixedScripts.push(scriptName);\n\t\t}\n\t}\n\n\tfor (const scriptName of mismatchedScripts) {\n\t\tconst defaultValue = defaultScripts[scriptName];\n\t\tif (defaultValue !== undefined && !fixedScripts.includes(scriptName)) {\n\t\t\tupdatedScripts[scriptName] = defaultValue;\n\t\t\tfixedScripts.push(scriptName);\n\t\t}\n\t}\n\n\treturn { updatedScripts, fixedScripts };\n}\n\n/**\n * Validate 'must' scripts and return any remaining missing scripts.\n */\nfunction validateRemainingMustScripts(\n\tmust: RequiredScriptEntry[],\n\tscripts: Record<string, string>,\n): string[] {\n\tconst stillMissing = must\n\t\t.map((entry) => parseRequiredEntry(entry).name)\n\t\t.filter((name) => !Object.hasOwn(scripts, name));\n\n\tif (stillMissing.length > 0) {\n\t\treturn [`Missing required scripts:\\n\\t${stillMissing.join(\"\\n\\t\")}`];\n\t}\n\treturn [];\n}\n\n/**\n * Validate 'exact' scripts and return any errors.\n */\nfunction validateRemainingExactScripts(\n\texact: Record<string, string>,\n\tscripts: Record<string, string>,\n): string[] {\n\tconst errors: string[] = [];\n\tfor (const [scriptName, expectedValue] of Object.entries(exact)) {\n\t\tif (!Object.hasOwn(scripts, scriptName)) {\n\t\t\terrors.push(`Missing required script: ${scriptName}`);\n\t\t} else if (scripts[scriptName] !== expectedValue) {\n\t\t\terrors.push(\n\t\t\t\t`Script \"${scriptName}\" must be \"${expectedValue}\", but found \"${scripts[scriptName]}\"`,\n\t\t\t);\n\t\t}\n\t}\n\treturn errors;\n}\n\n/**\n * Re-validate all rules after applying fixes.\n */\nfunction revalidateAfterFix(\n\tconfig: PackageScriptsSettings,\n\tupdatedScripts: Record<string, string>,\n): string[] {\n\tconst errors: string[] = [];\n\n\tif (\n\t\tconfig.mutuallyExclusive !== undefined &&\n\t\tconfig.mutuallyExclusive.length > 0\n\t) {\n\t\terrors.push(\n\t\t\t...validateMutuallyExclusiveScripts(\n\t\t\t\tconfig.mutuallyExclusive,\n\t\t\t\tupdatedScripts,\n\t\t\t),\n\t\t);\n\t}\n\n\tif (\n\t\tconfig.scriptMustContain !== undefined &&\n\t\tconfig.scriptMustContain.length > 0\n\t) {\n\t\terrors.push(\n\t\t\t...validateScriptContents(config.scriptMustContain, updatedScripts),\n\t\t);\n\t}\n\n\tif (config.must !== undefined && config.must.length > 0) {\n\t\terrors.push(...validateRemainingMustScripts(config.must, updatedScripts));\n\t}\n\n\tif (config.exact !== undefined && Object.keys(config.exact).length > 0) {\n\t\terrors.push(...validateRemainingExactScripts(config.exact, updatedScripts));\n\t}\n\n\tif (\n\t\tconfig.conditionalRequired !== undefined &&\n\t\tconfig.conditionalRequired.length > 0\n\t) {\n\t\terrors.push(\n\t\t\t...validateConditionalRequirements(\n\t\t\t\tconfig.conditionalRequired,\n\t\t\t\tupdatedScripts,\n\t\t\t),\n\t\t);\n\t}\n\n\treturn errors;\n}\n\n/**\n * Result of running all script validations.\n */\ninterface AllValidationsResult {\n\terrors: string[];\n\tmissingWithDefaults: string[];\n\tmismatchedScripts: string[];\n}\n\n/**\n * Run all script validations and aggregate results.\n */\nfunction runAllValidations(\n\tconfig: PackageScriptsSettings,\n\tscripts: PackageJson[\"scripts\"],\n\tdefaultScripts: Record<string, string>,\n): AllValidationsResult {\n\tconst errors: string[] = [];\n\tconst missingWithDefaults: string[] = [];\n\tlet mismatchedScripts: string[] = [];\n\n\tif (config.must !== undefined && config.must.length > 0) {\n\t\tconst result = validateMustScripts(config.must, scripts);\n\t\terrors.push(...result.errors);\n\t\tmissingWithDefaults.push(...result.missingWithDefaults);\n\t}\n\n\tif (config.exact !== undefined && Object.keys(config.exact).length > 0) {\n\t\tconst result = validateExactScripts(config.exact, scripts);\n\t\terrors.push(...result.errors);\n\t\tmissingWithDefaults.push(...result.missingWithDefaults);\n\t\tmismatchedScripts = result.mismatchedScripts;\n\t}\n\n\tif (\n\t\tconfig.mutuallyExclusive !== undefined &&\n\t\tconfig.mutuallyExclusive.length > 0\n\t) {\n\t\terrors.push(\n\t\t\t...validateMutuallyExclusiveScripts(config.mutuallyExclusive, scripts),\n\t\t);\n\t}\n\n\tif (\n\t\tconfig.conditionalRequired !== undefined &&\n\t\tconfig.conditionalRequired.length > 0\n\t) {\n\t\tconst result = validateConditionalWithDefaults(\n\t\t\tconfig.conditionalRequired,\n\t\t\tscripts,\n\t\t\tdefaultScripts,\n\t\t\tmissingWithDefaults,\n\t\t);\n\t\terrors.push(...result.errors);\n\t\tmissingWithDefaults.push(...result.missingWithDefaults);\n\t}\n\n\tif (\n\t\tconfig.scriptMustContain !== undefined &&\n\t\tconfig.scriptMustContain.length > 0\n\t) {\n\t\terrors.push(...validateScriptContents(config.scriptMustContain, scripts));\n\t}\n\n\treturn { errors, missingWithDefaults, mismatchedScripts };\n}\n\n/**\n * A RepoPolicy that validates package.json scripts based on configurable rules.\n *\n * @remarks\n *\n * This policy provides comprehensive script validation including:\n * - Required scripts that must be present (with optional defaults for auto-fix)\n * - Exact script content enforcement\n * - Mutually exclusive script groups (at most one can exist)\n * - Conditional requirements (if X exists, Y must also exist)\n * - Script body content validation (script must contain specific substrings)\n *\n * @example\n * ```typescript\n * import { makePolicy } from \"repopo\";\n * import { PackageScripts } from \"repopo/policies\";\n *\n * const config: RepopoConfig = {\n * policies: [\n * makePolicy(PackageScripts, {\n * config: {\n * // Scripts that must exist\n * must: [\n * \"test\", // Must exist, no auto-fix\n * { build: \"tsc\" }, // Must exist, auto-fix available\n * { lint: \"biome lint .\" }, // Must exist, auto-fix available\n * ],\n * // Scripts that must exist AND match exactly\n * exact: {\n * clean: \"rimraf dist esm\",\n * format: \"biome format --write .\",\n * },\n * // If \"build\" exists, \"clean\" must also exist (with inline auto-fix default)\n * conditionalRequired: [{ ifPresent: \"build\", requires: [{ clean: \"rimraf dist\" }] }],\n * // Script body must contain these substrings\n * scriptMustContain: [{ script: \"build\", mustContain: [\"tsc\"] }],\n * // Only one of these can exist\n * mutuallyExclusive: [[\"lint:eslint\", \"lint:biome\"]],\n * },\n * }),\n * ],\n * };\n * ```\n *\n * @public\n */\nexport const PackageScripts = definePackagePolicy<\n\tPackageJson,\n\tPackageScriptsSettings | undefined\n>({\n\tname: \"PackageScripts\",\n\trequiresConfig: true,\n\tdescription:\n\t\t\"Validates package.json scripts based on configurable rules including required scripts, exact content, and conditional requirements.\",\n\thandler: async (json, { file, root, resolve, config }) => {\n\t\tif (config === undefined) {\n\t\t\treturn [];\n\t\t}\n\n\t\tconst scripts = Object.hasOwn(json, \"scripts\") ? json.scripts : {};\n\t\tconst defaultScripts = buildDefaultsMap(config);\n\n\t\tconst { errors, missingWithDefaults, mismatchedScripts } =\n\t\t\trunAllValidations(config, scripts, defaultScripts);\n\n\t\tif (errors.length === 0) {\n\t\t\treturn [];\n\t\t}\n\n\t\tconst hasAutoFixableErrors =\n\t\t\tmissingWithDefaults.length > 0 || mismatchedScripts.length > 0;\n\n\t\tif (resolve && hasAutoFixableErrors) {\n\t\t\tconst { updatedScripts, fixedScripts } = applyScriptFixes(\n\t\t\t\tscripts,\n\t\t\t\tmissingWithDefaults,\n\t\t\t\tmismatchedScripts,\n\t\t\t\tdefaultScripts,\n\t\t\t);\n\n\t\t\ttry {\n\t\t\t\tawait jsonfile.writeFile(\n\t\t\t\t\tresolvePath(root, file),\n\t\t\t\t\t{ ...json, scripts: updatedScripts },\n\t\t\t\t\t{ spaces: 2 },\n\t\t\t\t);\n\t\t\t\tconst postFixErrors = revalidateAfterFix(config, updatedScripts);\n\t\t\t\treturn [\n\t\t\t\t\t...fixedScripts.map((name) => ({\n\t\t\t\t\t\tmessage: `Fixed scripts: ${name}`,\n\t\t\t\t\t\tfixStatus: \"fixed\" as const,\n\t\t\t\t\t})),\n\t\t\t\t\t...postFixErrors.map((message) => ({\n\t\t\t\t\t\tmessage: `Remaining errors:\\n${message}`,\n\t\t\t\t\t})),\n\t\t\t\t];\n\t\t\t} catch {\n\t\t\t\t// Fall through to return regular failure\n\t\t\t}\n\t\t}\n\t\treturn errors.map((message) => ({\n\t\t\tmessage,\n\t\t\tfixStatus: hasAutoFixableErrors\n\t\t\t\t? resolve\n\t\t\t\t\t? \"failed\"\n\t\t\t\t\t: \"available\"\n\t\t\t\t: undefined,\n\t\t}));\n\t},\n});\n"]}
@@ -21,7 +21,7 @@
21
21
  * };
22
22
  * ```
23
23
  *
24
- * @alpha
24
+ * @public
25
25
  */
26
26
  export interface PackageTestScriptsConfig {
27
27
  /**
@@ -86,15 +86,19 @@ export interface PackageTestScriptsConfig {
86
86
  * const config: RepopoConfig = {
87
87
  * policies: [
88
88
  * makePolicy(PackageTestScripts, {
89
- * testDirectories: ["test", "tests"],
90
- * testDependencies: ["vitest", "jest"],
91
- * requiredScripts: ["test"],
89
+ * config: {
90
+ * testDirectories: ["test", "tests"],
91
+ * testDependencies: ["vitest", "jest"],
92
+ * requiredScripts: ["test"],
93
+ * },
92
94
  * }),
93
95
  * ],
94
96
  * };
95
97
  * ```
96
98
  *
97
- * @alpha
99
+ * @public
98
100
  */
99
- export declare const PackageTestScripts: import("../policy.js").PolicyShape<PackageTestScriptsConfig | undefined>;
101
+ export declare const PackageTestScripts: import("../policy.js").FilePolicyDefinition<PackageTestScriptsConfig | undefined> & {
102
+ requiresConfig: true;
103
+ };
100
104
  //# sourceMappingURL=PackageTestScripts.d.ts.map