repopo 0.9.0 → 0.12.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 (271) hide show
  1. package/CHANGELOG.md +81 -0
  2. package/README.md +41 -2
  3. package/THIRD-PARTY-LICENSES.txt +71 -11
  4. package/bin/dev.js +5 -1
  5. package/esm/baseCommand.d.ts +1 -1
  6. package/esm/baseCommand.d.ts.map +1 -1
  7. package/esm/baseCommand.js +6 -9
  8. package/esm/baseCommand.js.map +1 -1
  9. package/esm/commands/check-native.d.ts +25 -0
  10. package/esm/commands/check-native.d.ts.map +1 -0
  11. package/esm/commands/check-native.js +137 -0
  12. package/esm/commands/check-native.js.map +1 -0
  13. package/esm/commands/check.d.ts +8 -19
  14. package/esm/commands/check.d.ts.map +1 -1
  15. package/esm/commands/check.js +97 -169
  16. package/esm/commands/check.js.map +1 -1
  17. package/esm/commands/list.d.ts.map +1 -1
  18. package/esm/commands/list.js +5 -6
  19. package/esm/commands/list.js.map +1 -1
  20. package/esm/config.d.ts +19 -4
  21. package/esm/config.d.ts.map +1 -1
  22. package/esm/config.js +2 -3
  23. package/esm/config.js.map +1 -1
  24. package/esm/context.d.ts +13 -3
  25. package/esm/context.d.ts.map +1 -1
  26. package/esm/context.js +11 -1
  27. package/esm/context.js.map +1 -1
  28. package/esm/index.d.ts +7 -2
  29. package/esm/index.d.ts.map +1 -1
  30. package/esm/index.js +9 -1
  31. package/esm/index.js.map +1 -1
  32. package/esm/makePolicy.d.ts +77 -8
  33. package/esm/makePolicy.d.ts.map +1 -1
  34. package/esm/makePolicy.js +108 -4
  35. package/esm/makePolicy.js.map +1 -1
  36. package/esm/perf.d.ts +7 -2
  37. package/esm/perf.d.ts.map +1 -1
  38. package/esm/perf.js.map +1 -1
  39. package/esm/policies/CargoLicenceValidated.d.ts +41 -0
  40. package/esm/policies/CargoLicenceValidated.d.ts.map +1 -0
  41. package/esm/policies/CargoLicenceValidated.js +45 -0
  42. package/esm/policies/CargoLicenceValidated.js.map +1 -0
  43. package/esm/policies/CargoLintsConfigured.d.ts +30 -0
  44. package/esm/policies/CargoLintsConfigured.d.ts.map +1 -0
  45. package/esm/policies/CargoLintsConfigured.js +61 -0
  46. package/esm/policies/CargoLintsConfigured.js.map +1 -0
  47. package/esm/policies/CargoLockPolicy.d.ts +34 -0
  48. package/esm/policies/CargoLockPolicy.d.ts.map +1 -0
  49. package/esm/policies/CargoLockPolicy.js +57 -0
  50. package/esm/policies/CargoLockPolicy.js.map +1 -0
  51. package/esm/policies/CargoTomlRequired.d.ts +34 -0
  52. package/esm/policies/CargoTomlRequired.d.ts.map +1 -0
  53. package/esm/policies/CargoTomlRequired.js +77 -0
  54. package/esm/policies/CargoTomlRequired.js.map +1 -0
  55. package/esm/policies/CargoTomlSorted.d.ts +20 -0
  56. package/esm/policies/CargoTomlSorted.d.ts.map +1 -0
  57. package/esm/policies/CargoTomlSorted.js +164 -0
  58. package/esm/policies/CargoTomlSorted.js.map +1 -0
  59. package/esm/policies/ConditionalScripts.d.ts +94 -0
  60. package/esm/policies/ConditionalScripts.d.ts.map +1 -0
  61. package/esm/policies/ConditionalScripts.js +145 -0
  62. package/esm/policies/ConditionalScripts.js.map +1 -0
  63. package/esm/policies/ExactScripts.d.ts +51 -0
  64. package/esm/policies/ExactScripts.d.ts.map +1 -0
  65. package/esm/policies/ExactScripts.js +108 -0
  66. package/esm/policies/ExactScripts.js.map +1 -0
  67. package/esm/policies/GleamDepVersions.d.ts +25 -0
  68. package/esm/policies/GleamDepVersions.d.ts.map +1 -0
  69. package/esm/policies/GleamDepVersions.js +67 -0
  70. package/esm/policies/GleamDepVersions.js.map +1 -0
  71. package/esm/policies/GleamDocExists.d.ts +24 -0
  72. package/esm/policies/GleamDocExists.d.ts.map +1 -0
  73. package/esm/policies/GleamDocExists.js +39 -0
  74. package/esm/policies/GleamDocExists.js.map +1 -0
  75. package/esm/policies/GleamInternalModulesConfigured.d.ts +21 -0
  76. package/esm/policies/GleamInternalModulesConfigured.d.ts.map +1 -0
  77. package/esm/policies/GleamInternalModulesConfigured.js +30 -0
  78. package/esm/policies/GleamInternalModulesConfigured.js.map +1 -0
  79. package/esm/policies/GleamLicenceConfigured.d.ts +29 -0
  80. package/esm/policies/GleamLicenceConfigured.d.ts.map +1 -0
  81. package/esm/policies/GleamLicenceConfigured.js +34 -0
  82. package/esm/policies/GleamLicenceConfigured.js.map +1 -0
  83. package/esm/policies/GleamManifestCommitted.d.ts +9 -0
  84. package/esm/policies/GleamManifestCommitted.d.ts.map +1 -0
  85. package/esm/policies/GleamManifestCommitted.js +26 -0
  86. package/esm/policies/GleamManifestCommitted.js.map +1 -0
  87. package/esm/policies/GleamMonorepoConsistency.d.ts +30 -0
  88. package/esm/policies/GleamMonorepoConsistency.d.ts.map +1 -0
  89. package/esm/policies/GleamMonorepoConsistency.js +74 -0
  90. package/esm/policies/GleamMonorepoConsistency.js.map +1 -0
  91. package/esm/policies/GleamNameRestrictions.d.ts +25 -0
  92. package/esm/policies/GleamNameRestrictions.d.ts.map +1 -0
  93. package/esm/policies/GleamNameRestrictions.js +44 -0
  94. package/esm/policies/GleamNameRestrictions.js.map +1 -0
  95. package/esm/policies/GleamNoPathDepsInPublished.d.ts +22 -0
  96. package/esm/policies/GleamNoPathDepsInPublished.d.ts.map +1 -0
  97. package/esm/policies/GleamNoPathDepsInPublished.js +43 -0
  98. package/esm/policies/GleamNoPathDepsInPublished.js.map +1 -0
  99. package/esm/policies/GleamPathDepsValid.d.ts +9 -0
  100. package/esm/policies/GleamPathDepsValid.d.ts.map +1 -0
  101. package/esm/policies/GleamPathDepsValid.js +59 -0
  102. package/esm/policies/GleamPathDepsValid.js.map +1 -0
  103. package/esm/policies/GleamProjectStructure.d.ts +25 -0
  104. package/esm/policies/GleamProjectStructure.d.ts.map +1 -0
  105. package/esm/policies/GleamProjectStructure.js +33 -0
  106. package/esm/policies/GleamProjectStructure.js.map +1 -0
  107. package/esm/policies/GleamSharedDependencyVersions.d.ts +29 -0
  108. package/esm/policies/GleamSharedDependencyVersions.d.ts.map +1 -0
  109. package/esm/policies/GleamSharedDependencyVersions.js +101 -0
  110. package/esm/policies/GleamSharedDependencyVersions.js.map +1 -0
  111. package/esm/policies/GleamTomlRequired.d.ts +30 -0
  112. package/esm/policies/GleamTomlRequired.d.ts.map +1 -0
  113. package/esm/policies/GleamTomlRequired.js +37 -0
  114. package/esm/policies/GleamTomlRequired.js.map +1 -0
  115. package/esm/policies/GleamTomlSorted.d.ts +20 -0
  116. package/esm/policies/GleamTomlSorted.d.ts.map +1 -0
  117. package/esm/policies/GleamTomlSorted.js +153 -0
  118. package/esm/policies/GleamTomlSorted.js.map +1 -0
  119. package/esm/policies/GleamVersionConstraint.d.ts +20 -0
  120. package/esm/policies/GleamVersionConstraint.d.ts.map +1 -0
  121. package/esm/policies/GleamVersionConstraint.js +64 -0
  122. package/esm/policies/GleamVersionConstraint.js.map +1 -0
  123. package/esm/policies/HtmlFileHeaders.d.ts +1 -1
  124. package/esm/policies/HtmlFileHeaders.d.ts.map +1 -1
  125. package/esm/policies/HtmlFileHeaders.js +1 -1
  126. package/esm/policies/HtmlFileHeaders.js.map +1 -1
  127. package/esm/policies/JsTsFileHeaders.d.ts +1 -1
  128. package/esm/policies/JsTsFileHeaders.d.ts.map +1 -1
  129. package/esm/policies/MutuallyExclusiveScripts.d.ts +46 -0
  130. package/esm/policies/MutuallyExclusiveScripts.d.ts.map +1 -0
  131. package/esm/policies/MutuallyExclusiveScripts.js +52 -0
  132. package/esm/policies/MutuallyExclusiveScripts.js.map +1 -0
  133. package/esm/policies/NoBuildDirectory.d.ts +9 -0
  134. package/esm/policies/NoBuildDirectory.d.ts.map +1 -0
  135. package/esm/policies/NoBuildDirectory.js +29 -0
  136. package/esm/policies/NoBuildDirectory.js.map +1 -0
  137. package/esm/policies/NoPrivateWorkspaceDependencies.d.ts +1 -1
  138. package/esm/policies/NoPrivateWorkspaceDependencies.d.ts.map +1 -1
  139. package/esm/policies/NoTargetArtifacts.d.ts +8 -0
  140. package/esm/policies/NoTargetArtifacts.d.ts.map +1 -0
  141. package/esm/policies/NoTargetArtifacts.js +42 -0
  142. package/esm/policies/NoTargetArtifacts.js.map +1 -0
  143. package/esm/policies/NoUnsafeWithoutJustification.d.ts +24 -0
  144. package/esm/policies/NoUnsafeWithoutJustification.d.ts.map +1 -0
  145. package/esm/policies/NoUnsafeWithoutJustification.js +80 -0
  146. package/esm/policies/NoUnsafeWithoutJustification.js.map +1 -0
  147. package/esm/policies/NoWildcardDependencies.d.ts +30 -0
  148. package/esm/policies/NoWildcardDependencies.d.ts.map +1 -0
  149. package/esm/policies/NoWildcardDependencies.js +83 -0
  150. package/esm/policies/NoWildcardDependencies.js.map +1 -0
  151. package/esm/policies/PackageAllowedScopes.d.ts +1 -1
  152. package/esm/policies/PackageAllowedScopes.d.ts.map +1 -1
  153. package/esm/policies/PackageEsmType.d.ts +1 -1
  154. package/esm/policies/PackageEsmType.d.ts.map +1 -1
  155. package/esm/policies/PackageEsmType.js +7 -5
  156. package/esm/policies/PackageEsmType.js.map +1 -1
  157. package/esm/policies/PackageFolderName.d.ts +1 -1
  158. package/esm/policies/PackageFolderName.d.ts.map +1 -1
  159. package/esm/policies/PackageJsonProperties.d.ts +1 -1
  160. package/esm/policies/PackageJsonProperties.d.ts.map +1 -1
  161. package/esm/policies/PackageJsonProperties.js +12 -4
  162. package/esm/policies/PackageJsonProperties.js.map +1 -1
  163. package/esm/policies/PackageJsonRepoDirectoryProperty.d.ts +1 -1
  164. package/esm/policies/PackageJsonRepoDirectoryProperty.d.ts.map +1 -1
  165. package/esm/policies/PackageJsonRepoDirectoryProperty.js +3 -2
  166. package/esm/policies/PackageJsonRepoDirectoryProperty.js.map +1 -1
  167. package/esm/policies/PackageJsonSorted.d.ts +1 -1
  168. package/esm/policies/PackageJsonSorted.d.ts.map +1 -1
  169. package/esm/policies/PackageJsonSorted.js +42 -7
  170. package/esm/policies/PackageJsonSorted.js.map +1 -1
  171. package/esm/policies/PackageLicense.d.ts +1 -1
  172. package/esm/policies/PackageLicense.d.ts.map +1 -1
  173. package/esm/policies/PackageLicense.js +1 -2
  174. package/esm/policies/PackageLicense.js.map +1 -1
  175. package/esm/policies/PackagePrivateField.d.ts +1 -1
  176. package/esm/policies/PackagePrivateField.d.ts.map +1 -1
  177. package/esm/policies/PackagePrivateField.js +3 -2
  178. package/esm/policies/PackagePrivateField.js.map +1 -1
  179. package/esm/policies/PackageReadme.d.ts +1 -1
  180. package/esm/policies/PackageReadme.d.ts.map +1 -1
  181. package/esm/policies/PackageReadme.js +2 -2
  182. package/esm/policies/PackageReadme.js.map +1 -1
  183. package/esm/policies/PackageScripts.d.ts +1 -1
  184. package/esm/policies/PackageScripts.d.ts.map +1 -1
  185. package/esm/policies/PackageScripts.js +3 -2
  186. package/esm/policies/PackageScripts.js.map +1 -1
  187. package/esm/policies/PackageTestScripts.d.ts +1 -1
  188. package/esm/policies/PackageTestScripts.d.ts.map +1 -1
  189. package/esm/policies/PackageTestScripts.js +3 -3
  190. package/esm/policies/PackageTestScripts.js.map +1 -1
  191. package/esm/policies/PublicApiDocumented.d.ts +21 -0
  192. package/esm/policies/PublicApiDocumented.d.ts.map +1 -0
  193. package/esm/policies/PublicApiDocumented.js +36 -0
  194. package/esm/policies/PublicApiDocumented.js.map +1 -0
  195. package/esm/policies/RequiredScripts.d.ts +71 -0
  196. package/esm/policies/RequiredScripts.d.ts.map +1 -0
  197. package/esm/policies/RequiredScripts.js +121 -0
  198. package/esm/policies/RequiredScripts.js.map +1 -0
  199. package/esm/policies/RustDocExists.d.ts +24 -0
  200. package/esm/policies/RustDocExists.d.ts.map +1 -0
  201. package/esm/policies/RustDocExists.js +49 -0
  202. package/esm/policies/RustDocExists.js.map +1 -0
  203. package/esm/policies/RustToolchainExists.d.ts +20 -0
  204. package/esm/policies/RustToolchainExists.d.ts.map +1 -0
  205. package/esm/policies/RustToolchainExists.js +27 -0
  206. package/esm/policies/RustToolchainExists.js.map +1 -0
  207. package/esm/policies/RustfmtConfigExists.d.ts +20 -0
  208. package/esm/policies/RustfmtConfigExists.d.ts.map +1 -0
  209. package/esm/policies/RustfmtConfigExists.js +27 -0
  210. package/esm/policies/RustfmtConfigExists.js.map +1 -0
  211. package/esm/policies/ScriptContains.d.ts +64 -0
  212. package/esm/policies/ScriptContains.d.ts.map +1 -0
  213. package/esm/policies/ScriptContains.js +62 -0
  214. package/esm/policies/ScriptContains.js.map +1 -0
  215. package/esm/policies/SharedDependencyVersions.d.ts +24 -0
  216. package/esm/policies/SharedDependencyVersions.d.ts.map +1 -0
  217. package/esm/policies/SharedDependencyVersions.js +135 -0
  218. package/esm/policies/SharedDependencyVersions.js.map +1 -0
  219. package/esm/policies/WorkspaceInheritance.d.ts +24 -0
  220. package/esm/policies/WorkspaceInheritance.d.ts.map +1 -0
  221. package/esm/policies/WorkspaceInheritance.js +94 -0
  222. package/esm/policies/WorkspaceInheritance.js.map +1 -0
  223. package/esm/policies/WorkspaceMembersValid.d.ts +20 -0
  224. package/esm/policies/WorkspaceMembersValid.d.ts.map +1 -0
  225. package/esm/policies/WorkspaceMembersValid.js +88 -0
  226. package/esm/policies/WorkspaceMembersValid.js.map +1 -0
  227. package/esm/policies/constants.d.ts +8 -0
  228. package/esm/policies/constants.d.ts.map +1 -1
  229. package/esm/policies/constants.js +8 -0
  230. package/esm/policies/constants.js.map +1 -1
  231. package/esm/policies.d.ts +36 -1
  232. package/esm/policies.d.ts.map +1 -1
  233. package/esm/policies.js +68 -0
  234. package/esm/policies.js.map +1 -1
  235. package/esm/policy.d.ts +202 -84
  236. package/esm/policy.d.ts.map +1 -1
  237. package/esm/policy.js +103 -21
  238. package/esm/policy.js.map +1 -1
  239. package/esm/policyDefiners/defineCargoPolicy.d.ts +66 -0
  240. package/esm/policyDefiners/defineCargoPolicy.d.ts.map +1 -0
  241. package/esm/policyDefiners/defineCargoPolicy.js +60 -0
  242. package/esm/policyDefiners/defineCargoPolicy.js.map +1 -0
  243. package/esm/policyDefiners/defineFileHeaderPolicy.d.ts +7 -2
  244. package/esm/policyDefiners/defineFileHeaderPolicy.d.ts.map +1 -1
  245. package/esm/policyDefiners/defineFileHeaderPolicy.js +11 -7
  246. package/esm/policyDefiners/defineFileHeaderPolicy.js.map +1 -1
  247. package/esm/policyDefiners/defineGleamPolicy.d.ts +65 -0
  248. package/esm/policyDefiners/defineGleamPolicy.d.ts.map +1 -0
  249. package/esm/policyDefiners/defineGleamPolicy.js +59 -0
  250. package/esm/policyDefiners/defineGleamPolicy.js.map +1 -0
  251. package/esm/policyDefiners/definePackagePolicy.d.ts +18 -5
  252. package/esm/policyDefiners/definePackagePolicy.d.ts.map +1 -1
  253. package/esm/policyDefiners/definePackagePolicy.js +14 -4
  254. package/esm/policyDefiners/definePackagePolicy.js.map +1 -1
  255. package/esm/policyDefiners/spdxValidator.d.ts +74 -0
  256. package/esm/policyDefiners/spdxValidator.d.ts.map +1 -0
  257. package/esm/policyDefiners/spdxValidator.js +127 -0
  258. package/esm/policyDefiners/spdxValidator.js.map +1 -0
  259. package/esm/policyDefiners/tomlParser.d.ts +8 -0
  260. package/esm/policyDefiners/tomlParser.d.ts.map +1 -0
  261. package/esm/policyDefiners/tomlParser.js +17 -0
  262. package/esm/policyDefiners/tomlParser.js.map +1 -0
  263. package/esm/runner.d.ts +60 -0
  264. package/esm/runner.d.ts.map +1 -0
  265. package/esm/runner.js +174 -0
  266. package/esm/runner.js.map +1 -0
  267. package/esm/tsdoc-metadata.json +1 -1
  268. package/oclif.manifest.json +100 -3
  269. package/package.json +46 -31
  270. package/scripts/postinstall.mjs +154 -0
  271. package/sidecar/sidecar.mjs +468 -0
@@ -0,0 +1,80 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { resolve } from "pathe";
3
+ import { makePolicyDefinition } from "../makePolicy.js";
4
+ const UNSAFE_PATTERN = /\bunsafe\s*\{|\bunsafe\s+fn\b|\bunsafe\s+impl\b/;
5
+ /**
6
+ * Check whether the lines preceding an unsafe block contain the required safety comment.
7
+ */
8
+ function hasJustificationComment(lines, lineIndex, requiredComment) {
9
+ for (let j = lineIndex - 1; j >= Math.max(0, lineIndex - 5); j--) {
10
+ const prevLine = lines[j];
11
+ if (prevLine === undefined) {
12
+ continue;
13
+ }
14
+ const trimmedPrev = prevLine.trim();
15
+ if (trimmedPrev === "") {
16
+ continue;
17
+ }
18
+ if (trimmedPrev.toUpperCase().includes(requiredComment.toUpperCase())) {
19
+ return true;
20
+ }
21
+ // Stop if we hit a non-comment, non-attribute line
22
+ if (!(trimmedPrev.startsWith("//") || trimmedPrev.startsWith("#["))) {
23
+ break;
24
+ }
25
+ }
26
+ return false;
27
+ }
28
+ /**
29
+ * Check if a file path should be excluded from the unsafe check.
30
+ */
31
+ function isExcluded(file, excludePaths) {
32
+ if (!excludePaths) {
33
+ return false;
34
+ }
35
+ for (const excludePath of excludePaths) {
36
+ if (file.includes(excludePath)) {
37
+ return true;
38
+ }
39
+ }
40
+ return false;
41
+ }
42
+ /**
43
+ * A policy that requires SAFETY comments on unsafe blocks in Rust source files.
44
+ *
45
+ * @alpha
46
+ */
47
+ export const NoUnsafeWithoutJustification = makePolicyDefinition({
48
+ name: "NoUnsafeWithoutJustification",
49
+ description: "Requires safety justification comments on unsafe blocks in Rust source files.",
50
+ match: /\.rs$/,
51
+ handler: async ({ file, root, config }) => {
52
+ const requiredComment = config?.requireComment ?? "// SAFETY:";
53
+ if (isExcluded(file, config?.excludePaths)) {
54
+ return true;
55
+ }
56
+ const content = await readFile(resolve(root, file), "utf-8");
57
+ const lines = content.split("\n");
58
+ const violations = [];
59
+ for (let i = 0; i < lines.length; i++) {
60
+ const line = lines[i];
61
+ if (line === undefined) {
62
+ continue;
63
+ }
64
+ const trimmed = line.trim();
65
+ // Look for `unsafe` keyword usage (blocks, functions, impls)
66
+ if (UNSAFE_PATTERN.test(trimmed) &&
67
+ !hasJustificationComment(lines, i, requiredComment)) {
68
+ violations.push(`Line ${i + 1}: unsafe block without ${requiredComment} comment`);
69
+ }
70
+ }
71
+ if (violations.length > 0) {
72
+ return {
73
+ error: violations.join("; "),
74
+ manualFix: `Add a "${requiredComment}" comment above each unsafe block explaining why it is safe.`,
75
+ };
76
+ }
77
+ return true;
78
+ },
79
+ });
80
+ //# sourceMappingURL=NoUnsafeWithoutJustification.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NoUnsafeWithoutJustification.js","sourceRoot":"","sources":["../../src/policies/NoUnsafeWithoutJustification.ts"],"names":[],"mappings":"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","sourcesContent":["import { readFile } from \"node:fs/promises\";\nimport { resolve } from \"pathe\";\nimport { makePolicyDefinition } from \"../makePolicy.js\";\nimport type { PolicyDefinition } from \"../policy.js\";\n\n/**\n * Configuration for the NoUnsafeWithoutJustification policy.\n *\n * @alpha\n */\nexport interface NoUnsafeWithoutJustificationConfig {\n\t/**\n\t * The comment prefix to look for before unsafe blocks.\n\t * @defaultValue \"// SAFETY:\"\n\t */\n\trequireComment?: string;\n\n\t/**\n\t * Paths to exclude from this check (e.g., FFI modules).\n\t */\n\texcludePaths?: string[];\n}\n\nconst UNSAFE_PATTERN = /\\bunsafe\\s*\\{|\\bunsafe\\s+fn\\b|\\bunsafe\\s+impl\\b/;\n\n/**\n * Check whether the lines preceding an unsafe block contain the required safety comment.\n */\nfunction hasJustificationComment(\n\tlines: string[],\n\tlineIndex: number,\n\trequiredComment: string,\n): boolean {\n\tfor (let j = lineIndex - 1; j >= Math.max(0, lineIndex - 5); j--) {\n\t\tconst prevLine = lines[j];\n\t\tif (prevLine === undefined) {\n\t\t\tcontinue;\n\t\t}\n\t\tconst trimmedPrev = prevLine.trim();\n\t\tif (trimmedPrev === \"\") {\n\t\t\tcontinue;\n\t\t}\n\t\tif (trimmedPrev.toUpperCase().includes(requiredComment.toUpperCase())) {\n\t\t\treturn true;\n\t\t}\n\t\t// Stop if we hit a non-comment, non-attribute line\n\t\tif (!(trimmedPrev.startsWith(\"//\") || trimmedPrev.startsWith(\"#[\"))) {\n\t\t\tbreak;\n\t\t}\n\t}\n\treturn false;\n}\n\n/**\n * Check if a file path should be excluded from the unsafe check.\n */\nfunction isExcluded(file: string, excludePaths: string[] | undefined): boolean {\n\tif (!excludePaths) {\n\t\treturn false;\n\t}\n\tfor (const excludePath of excludePaths) {\n\t\tif (file.includes(excludePath)) {\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n\n/**\n * A policy that requires SAFETY comments on unsafe blocks in Rust source files.\n *\n * @alpha\n */\nexport const NoUnsafeWithoutJustification: PolicyDefinition<NoUnsafeWithoutJustificationConfig> =\n\tmakePolicyDefinition({\n\t\tname: \"NoUnsafeWithoutJustification\",\n\t\tdescription:\n\t\t\t\"Requires safety justification comments on unsafe blocks in Rust source files.\",\n\t\tmatch: /\\.rs$/,\n\t\thandler: async ({ file, root, config }) => {\n\t\t\tconst requiredComment = config?.requireComment ?? \"// SAFETY:\";\n\n\t\t\tif (isExcluded(file, config?.excludePaths)) {\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tconst content = await readFile(resolve(root, file), \"utf-8\");\n\t\t\tconst lines = content.split(\"\\n\");\n\t\t\tconst violations: string[] = [];\n\n\t\t\tfor (let i = 0; i < lines.length; i++) {\n\t\t\t\tconst line = lines[i];\n\t\t\t\tif (line === undefined) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst trimmed = line.trim();\n\n\t\t\t\t// Look for `unsafe` keyword usage (blocks, functions, impls)\n\t\t\t\tif (\n\t\t\t\t\tUNSAFE_PATTERN.test(trimmed) &&\n\t\t\t\t\t!hasJustificationComment(lines, i, requiredComment)\n\t\t\t\t) {\n\t\t\t\t\tviolations.push(\n\t\t\t\t\t\t`Line ${i + 1}: unsafe block without ${requiredComment} comment`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (violations.length > 0) {\n\t\t\t\treturn {\n\t\t\t\t\terror: violations.join(\"; \"),\n\t\t\t\t\tmanualFix: `Add a \"${requiredComment}\" comment above each unsafe block explaining why it is safe.`,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\treturn true;\n\t\t},\n\t});\n"]}
@@ -0,0 +1,30 @@
1
+ import type { PolicyShape } from "../policy.js";
2
+ /**
3
+ * Configuration for the NoWildcardDependencies policy.
4
+ *
5
+ * @alpha
6
+ */
7
+ export interface NoWildcardDependenciesConfig {
8
+ /**
9
+ * Version patterns to deny outright.
10
+ * @defaultValue ["*"]
11
+ */
12
+ deny?: string[];
13
+ /**
14
+ * Version patterns to warn about.
15
+ * @defaultValue [">="]
16
+ */
17
+ warn?: string[];
18
+ /**
19
+ * If true, dev-dependencies are checked more leniently (only deny, not warn).
20
+ * @defaultValue true
21
+ */
22
+ allowDev?: boolean;
23
+ }
24
+ /**
25
+ * A policy that prevents wildcard or overly permissive version specifications in Cargo.toml.
26
+ *
27
+ * @alpha
28
+ */
29
+ export declare const NoWildcardDependencies: PolicyShape<NoWildcardDependenciesConfig>;
30
+ //# sourceMappingURL=NoWildcardDependencies.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NoWildcardDependencies.d.ts","sourceRoot":"","sources":["../../src/policies/NoWildcardDependencies.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAMhD;;;;GAIG;AACH,MAAM,WAAW,4BAA4B;IAC5C;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAEhB;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAEhB;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACnB;AAkED;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,EAAE,WAAW,CAAC,4BAA4B,CAiD1E,CAAC"}
@@ -0,0 +1,83 @@
1
+ import { defineCargoPolicy, } from "../policyDefiners/defineCargoPolicy.js";
2
+ const DEFAULT_DENY = ["*"];
3
+ const DEFAULT_WARN = [">="];
4
+ function checkVersionSpec(version, denyPatterns, warnPatterns) {
5
+ for (const pattern of denyPatterns) {
6
+ if (version === pattern || version.startsWith(pattern)) {
7
+ return "deny";
8
+ }
9
+ }
10
+ for (const pattern of warnPatterns) {
11
+ if (version.startsWith(pattern)) {
12
+ return "warn";
13
+ }
14
+ }
15
+ return "ok";
16
+ }
17
+ function extractVersion(dep) {
18
+ if (typeof dep === "string") {
19
+ return dep;
20
+ }
21
+ if (typeof dep === "object" && dep !== null) {
22
+ const record = dep;
23
+ if (typeof record.version === "string") {
24
+ return record.version;
25
+ }
26
+ }
27
+ return undefined;
28
+ }
29
+ function checkSectionDeps(deps, sectionName, isDev, allowDev, denyPatterns, warnPatterns, errors) {
30
+ for (const [depName, depValue] of Object.entries(deps)) {
31
+ const version = extractVersion(depValue);
32
+ if (!version) {
33
+ continue;
34
+ }
35
+ const effectiveWarn = isDev && allowDev ? [] : warnPatterns;
36
+ const result = checkVersionSpec(version, denyPatterns, effectiveWarn);
37
+ if (result === "deny") {
38
+ errors.push(`[${sectionName}] ${depName} = "${version}" uses a denied version pattern`);
39
+ }
40
+ else if (result === "warn") {
41
+ errors.push(`[${sectionName}] ${depName} = "${version}" uses an overly permissive version range`);
42
+ }
43
+ }
44
+ }
45
+ /**
46
+ * A policy that prevents wildcard or overly permissive version specifications in Cargo.toml.
47
+ *
48
+ * @alpha
49
+ */
50
+ export const NoWildcardDependencies = defineCargoPolicy({
51
+ name: "NoWildcardDependencies",
52
+ description: "Prevents wildcard or overly permissive dependency version specifications in Cargo.toml.",
53
+ handler: async (toml, { config }) => {
54
+ // Skip workspace root
55
+ if (toml.workspace !== undefined && toml.package === undefined) {
56
+ return true;
57
+ }
58
+ const denyPatterns = config?.deny ?? DEFAULT_DENY;
59
+ const warnPatterns = config?.warn ?? DEFAULT_WARN;
60
+ const allowDev = config?.allowDev ?? true;
61
+ const errors = [];
62
+ const sections = [
63
+ { name: "dependencies", isDev: false },
64
+ { name: "build-dependencies", isDev: false },
65
+ { name: "dev-dependencies", isDev: true },
66
+ ];
67
+ for (const { name, isDev } of sections) {
68
+ const deps = toml[name];
69
+ if (!deps) {
70
+ continue;
71
+ }
72
+ checkSectionDeps(deps, name, isDev, allowDev, denyPatterns, warnPatterns, errors);
73
+ }
74
+ if (errors.length > 0) {
75
+ return {
76
+ error: errors.join("; "),
77
+ manualFix: 'Use specific version ranges (e.g., "1.0", "^1.0", "~1.0") instead of wildcards.',
78
+ };
79
+ }
80
+ return true;
81
+ },
82
+ });
83
+ //# sourceMappingURL=NoWildcardDependencies.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NoWildcardDependencies.js","sourceRoot":"","sources":["../../src/policies/NoWildcardDependencies.ts"],"names":[],"mappings":"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","sourcesContent":["import type { PolicyShape } from \"../policy.js\";\nimport {\n\ttype CargoToml,\n\tdefineCargoPolicy,\n} from \"../policyDefiners/defineCargoPolicy.js\";\n\n/**\n * Configuration for the NoWildcardDependencies policy.\n *\n * @alpha\n */\nexport interface NoWildcardDependenciesConfig {\n\t/**\n\t * Version patterns to deny outright.\n\t * @defaultValue [\"*\"]\n\t */\n\tdeny?: string[];\n\n\t/**\n\t * Version patterns to warn about.\n\t * @defaultValue [\">=\"]\n\t */\n\twarn?: string[];\n\n\t/**\n\t * If true, dev-dependencies are checked more leniently (only deny, not warn).\n\t * @defaultValue true\n\t */\n\tallowDev?: boolean;\n}\n\nconst DEFAULT_DENY = [\"*\"];\nconst DEFAULT_WARN = [\">=\"];\n\nfunction checkVersionSpec(\n\tversion: string,\n\tdenyPatterns: string[],\n\twarnPatterns: string[],\n): \"deny\" | \"warn\" | \"ok\" {\n\tfor (const pattern of denyPatterns) {\n\t\tif (version === pattern || version.startsWith(pattern)) {\n\t\t\treturn \"deny\";\n\t\t}\n\t}\n\tfor (const pattern of warnPatterns) {\n\t\tif (version.startsWith(pattern)) {\n\t\t\treturn \"warn\";\n\t\t}\n\t}\n\treturn \"ok\";\n}\n\nfunction extractVersion(dep: unknown): string | undefined {\n\tif (typeof dep === \"string\") {\n\t\treturn dep;\n\t}\n\tif (typeof dep === \"object\" && dep !== null) {\n\t\tconst record = dep as Record<string, unknown>;\n\t\tif (typeof record.version === \"string\") {\n\t\t\treturn record.version;\n\t\t}\n\t}\n\treturn undefined;\n}\n\nfunction checkSectionDeps(\n\tdeps: Record<string, unknown>,\n\tsectionName: string,\n\tisDev: boolean,\n\tallowDev: boolean,\n\tdenyPatterns: string[],\n\twarnPatterns: string[],\n\terrors: string[],\n): void {\n\tfor (const [depName, depValue] of Object.entries(deps)) {\n\t\tconst version = extractVersion(depValue);\n\t\tif (!version) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst effectiveWarn = isDev && allowDev ? [] : warnPatterns;\n\t\tconst result = checkVersionSpec(version, denyPatterns, effectiveWarn);\n\n\t\tif (result === \"deny\") {\n\t\t\terrors.push(\n\t\t\t\t`[${sectionName}] ${depName} = \"${version}\" uses a denied version pattern`,\n\t\t\t);\n\t\t} else if (result === \"warn\") {\n\t\t\terrors.push(\n\t\t\t\t`[${sectionName}] ${depName} = \"${version}\" uses an overly permissive version range`,\n\t\t\t);\n\t\t}\n\t}\n}\n\n/**\n * A policy that prevents wildcard or overly permissive version specifications in Cargo.toml.\n *\n * @alpha\n */\nexport const NoWildcardDependencies: PolicyShape<NoWildcardDependenciesConfig> =\n\tdefineCargoPolicy({\n\t\tname: \"NoWildcardDependencies\",\n\t\tdescription:\n\t\t\t\"Prevents wildcard or overly permissive dependency version specifications in Cargo.toml.\",\n\t\thandler: async (toml: CargoToml, { config }) => {\n\t\t\t// Skip workspace root\n\t\t\tif (toml.workspace !== undefined && toml.package === undefined) {\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tconst denyPatterns = config?.deny ?? DEFAULT_DENY;\n\t\t\tconst warnPatterns = config?.warn ?? DEFAULT_WARN;\n\t\t\tconst allowDev = config?.allowDev ?? true;\n\t\t\tconst errors: string[] = [];\n\n\t\t\tconst sections = [\n\t\t\t\t{ name: \"dependencies\", isDev: false },\n\t\t\t\t{ name: \"build-dependencies\", isDev: false },\n\t\t\t\t{ name: \"dev-dependencies\", isDev: true },\n\t\t\t];\n\n\t\t\tfor (const { name, isDev } of sections) {\n\t\t\t\tconst deps = toml[name] as Record<string, unknown> | undefined;\n\t\t\t\tif (!deps) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tcheckSectionDeps(\n\t\t\t\t\tdeps,\n\t\t\t\t\tname,\n\t\t\t\t\tisDev,\n\t\t\t\t\tallowDev,\n\t\t\t\t\tdenyPatterns,\n\t\t\t\t\twarnPatterns,\n\t\t\t\t\terrors,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tif (errors.length > 0) {\n\t\t\t\treturn {\n\t\t\t\t\terror: errors.join(\"; \"),\n\t\t\t\t\tmanualFix:\n\t\t\t\t\t\t'Use specific version ranges (e.g., \"1.0\", \"^1.0\", \"~1.0\") instead of wildcards.',\n\t\t\t\t};\n\t\t\t}\n\n\t\t\treturn true;\n\t\t},\n\t});\n"]}
@@ -84,5 +84,5 @@ export interface PackageAllowedScopesConfig {
84
84
  *
85
85
  * @alpha
86
86
  */
87
- export declare const PackageAllowedScopes: import("../policy.js").PolicyDefinition<PackageAllowedScopesConfig | undefined>;
87
+ export declare const PackageAllowedScopes: import("../policy.js").PolicyShape<PackageAllowedScopesConfig | undefined>;
88
88
  //# sourceMappingURL=PackageAllowedScopes.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"PackageAllowedScopes.d.ts","sourceRoot":"","sources":["../../src/policies/PackageAllowedScopes.ts"],"names":[],"mappings":"AAKA;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,WAAW,0BAA0B;IAC1C;;;;;;;;;;;;;;;OAeG;IACH,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IAEzB;;;;;;;;;;;;;OAaG;IACH,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC5B;AAiDD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,eAAO,MAAM,oBAAoB,iFAuD/B,CAAC"}
1
+ {"version":3,"file":"PackageAllowedScopes.d.ts","sourceRoot":"","sources":["../../src/policies/PackageAllowedScopes.ts"],"names":[],"mappings":"AAKA;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,WAAW,0BAA0B;IAC1C;;;;;;;;;;;;;;;OAeG;IACH,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IAEzB;;;;;;;;;;;;;OAaG;IACH,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC5B;AAiDD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,eAAO,MAAM,oBAAoB,4EAuD/B,CAAC"}
@@ -96,5 +96,5 @@ export interface PackageEsmTypeConfig {
96
96
  *
97
97
  * @alpha
98
98
  */
99
- export declare const PackageEsmType: import("../policy.js").PolicyDefinition<PackageEsmTypeConfig | undefined>;
99
+ export declare const PackageEsmType: import("../policy.js").PolicyShape<PackageEsmTypeConfig | undefined>;
100
100
  //# sourceMappingURL=PackageEsmType.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"PackageEsmType.d.ts","sourceRoot":"","sources":["../../src/policies/PackageEsmType.ts"],"names":[],"mappings":"AAQA;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,WAAW,oBAAoB;IACpC;;;;;;;;;;;OAWG;IACH,YAAY,CAAC,EAAE,QAAQ,GAAG,UAAU,CAAC;IAErC;;;;;;;;;;OAUG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAE5B;;;;;;;OAOG;IACH,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAE3B;;;;;;;;;;OAUG;IACH,kBAAkB,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;CAC9C;AAyPD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,eAAO,MAAM,cAAc,2EAgEzB,CAAC"}
1
+ {"version":3,"file":"PackageEsmType.d.ts","sourceRoot":"","sources":["../../src/policies/PackageEsmType.ts"],"names":[],"mappings":"AASA;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,WAAW,oBAAoB;IACpC;;;;;;;;;;;OAWG;IACH,YAAY,CAAC,EAAE,QAAQ,GAAG,UAAU,CAAC;IAErC;;;;;;;;;;OAUG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAE5B;;;;;;;OAOG;IACH,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAE3B;;;;;;;;;;OAUG;IACH,kBAAkB,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;CAC9C;AA2PD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,eAAO,MAAM,cAAc,sEAgEzB,CAAC"}
@@ -1,4 +1,5 @@
1
1
  import jsonfile from "jsonfile";
2
+ import { resolve as resolvePath } from "pathe";
2
3
  import { definePackagePolicy } from "../policyDefiners/definePackagePolicy.js";
3
4
  import { detectIndentation } from "../utils/indentation.js";
4
5
  const { writeFile: writeJson } = jsonfile;
@@ -159,15 +160,16 @@ function buildErrorMessage(packageName, currentType, expectedType) {
159
160
  /**
160
161
  * Apply the auto-fix for the type field.
161
162
  */
162
- async function applyFix(json, file, expectedType, failResult) {
163
+ async function applyFix(json, file, root, expectedType, failResult) {
163
164
  const fixResult = {
164
165
  ...failResult,
165
166
  resolved: false,
166
167
  };
167
168
  try {
168
169
  json.type = expectedType;
169
- const indent = await detectIndentation(file);
170
- await writeJson(file, json, { spaces: indent });
170
+ const filePath = resolvePath(root, file);
171
+ const indent = await detectIndentation(filePath);
172
+ await writeJson(filePath, json, { spaces: indent });
171
173
  fixResult.resolved = true;
172
174
  }
173
175
  catch {
@@ -211,7 +213,7 @@ async function applyFix(json, file, expectedType, failResult) {
211
213
  export const PackageEsmType = definePackagePolicy({
212
214
  name: "PackageEsmType",
213
215
  description: "Ensures the type field in package.json correctly indicates ESM or CommonJS module format.",
214
- handler: async (json, { file, config, resolve }) => {
216
+ handler: async (json, { file, root, config, resolve }) => {
215
217
  // If no config provided, skip validation
216
218
  if (config === undefined) {
217
219
  return true;
@@ -253,7 +255,7 @@ export const PackageEsmType = definePackagePolicy({
253
255
  ],
254
256
  };
255
257
  if (resolve) {
256
- return applyFix(json, file, expectedType, failResult);
258
+ return applyFix(json, file, root, expectedType, failResult);
257
259
  }
258
260
  return failResult;
259
261
  },
@@ -1 +1 @@
1
- {"version":3,"file":"PackageEsmType.js","sourceRoot":"","sources":["../../src/policies/PackageEsmType.ts"],"names":[],"mappings":"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","sourcesContent":["import jsonfile from \"jsonfile\";\nimport type { PackageJson } from \"type-fest\";\nimport type { PolicyFailure, PolicyFixResult } from \"../policy.js\";\nimport { definePackagePolicy } from \"../policyDefiners/definePackagePolicy.js\";\nimport { detectIndentation } from \"../utils/indentation.js\";\n\nconst { writeFile: writeJson } = jsonfile;\n\n/**\n * Configuration for the PackageEsmType policy.\n *\n * This policy ensures the `type` field in package.json is correctly set\n * to indicate whether the package is an ESM or CommonJS module.\n *\n * @example\n * ```typescript\n * const config: PackageEsmTypeConfig = {\n * // Require ESM type field for all packages\n * requiredType: \"module\",\n * // Or detect based on exports/main field\n * detectFromExports: true,\n * };\n * ```\n *\n * @alpha\n */\nexport interface PackageEsmTypeConfig {\n\t/**\n\t * The required `type` field value for all packages.\n\t *\n\t * - `\"module\"` - All packages must be ESM (use import/export)\n\t * - `\"commonjs\"` - All packages must be CommonJS (use require)\n\t * - `undefined` - Use detection logic based on exports/main\n\t *\n\t * @remarks\n\t *\n\t * If set, this overrides automatic detection. Use `detectFromExports`\n\t * for more flexible per-package type detection.\n\t */\n\trequiredType?: \"module\" | \"commonjs\";\n\n\t/**\n\t * Detect the expected `type` field from the exports or main field.\n\t *\n\t * When enabled, the policy analyzes the package's entry points:\n\t * - If exports use `.mjs` or have `import` conditions → `\"module\"`\n\t * - If exports use `.cjs` or have `require` conditions → `\"commonjs\"`\n\t * - If main field uses `.mjs` → `\"module\"`\n\t * - If main field uses `.cjs` → `\"commonjs\"`\n\t *\n\t * @defaultValue false\n\t */\n\tdetectFromExports?: boolean;\n\n\t/**\n\t * Package names or scopes to exclude from validation.\n\t *\n\t * @example\n\t * ```typescript\n\t * excludePackages: [\"@myorg/legacy-package\", \"old-cjs-lib\"]\n\t * ```\n\t */\n\texcludePackages?: string[];\n\n\t/**\n\t * Behavior when module type cannot be detected from exports/main.\n\t *\n\t * - `\"skip\"` (default) - Skip validation for packages where type cannot be detected\n\t * - `\"warn\"` - Pass validation but include a warning in the output\n\t * - `\"fail\"` - Fail validation when type cannot be detected\n\t *\n\t * This only applies when `detectFromExports` is true and no `requiredType` is set.\n\t *\n\t * @defaultValue \"skip\"\n\t */\n\tonDetectionFailure?: \"skip\" | \"warn\" | \"fail\";\n}\n\n/**\n * Result of detecting the module type from a package.\n */\ninterface DetectionResult {\n\t/**\n\t * The detected type, if any.\n\t */\n\ttype: \"module\" | \"commonjs\" | undefined;\n\n\t/**\n\t * Whether the package appears to be dual-format (both ESM and CommonJS).\n\t * When true, the `type` field should typically match the package's default format.\n\t */\n\tisDualFormat: boolean;\n\n\t/**\n\t * Human-readable reason for the detection result.\n\t */\n\treason: string;\n}\n\n/**\n * Detect module type from the exports field.\n */\nfunction detectTypeFromExports(\n\texports: PackageJson[\"exports\"],\n): DetectionResult | undefined {\n\tif (exports === undefined) {\n\t\treturn undefined;\n\t}\n\n\tconst exportsStr = JSON.stringify(exports);\n\tconst hasEsm = exportsStr.includes(\".mjs\") || exportsStr.includes('\"import\"');\n\tconst hasCjs =\n\t\texportsStr.includes(\".cjs\") || exportsStr.includes('\"require\"');\n\n\t// Dual format packages have both ESM and CJS exports\n\tif (hasEsm && hasCjs) {\n\t\treturn {\n\t\t\ttype: \"module\",\n\t\t\tisDualFormat: true,\n\t\t\treason:\n\t\t\t\t\"Package has both ESM and CommonJS exports (dual format). Defaulting to ESM.\",\n\t\t};\n\t}\n\n\tif (hasEsm) {\n\t\treturn {\n\t\t\ttype: \"module\",\n\t\t\tisDualFormat: false,\n\t\t\treason: \"Detected ESM from exports field (.mjs or import condition)\",\n\t\t};\n\t}\n\n\tif (hasCjs) {\n\t\treturn {\n\t\t\ttype: \"commonjs\",\n\t\t\tisDualFormat: false,\n\t\t\treason:\n\t\t\t\t\"Detected CommonJS from exports field (.cjs or require condition)\",\n\t\t};\n\t}\n\n\treturn undefined;\n}\n\n/**\n * Detect module type from the main field.\n */\nfunction detectTypeFromMain(main: unknown): DetectionResult | undefined {\n\tif (typeof main !== \"string\") {\n\t\treturn undefined;\n\t}\n\n\tif (main.endsWith(\".mjs\")) {\n\t\treturn {\n\t\t\ttype: \"module\",\n\t\t\tisDualFormat: false,\n\t\t\treason: \"Detected ESM from main field (.mjs extension)\",\n\t\t};\n\t}\n\n\tif (main.endsWith(\".cjs\")) {\n\t\treturn {\n\t\t\ttype: \"commonjs\",\n\t\t\tisDualFormat: false,\n\t\t\treason: \"Detected CommonJS from main field (.cjs extension)\",\n\t\t};\n\t}\n\n\treturn undefined;\n}\n\n/**\n * Detect the expected type field from the package's exports or main field.\n *\n * @param json - The package.json content\n * @returns Detection result with type, dual-format indicator, and reason\n */\nfunction detectTypeFromPackage(json: PackageJson): DetectionResult {\n\t// Check exports field for type hints\n\tconst exportsResult = detectTypeFromExports(json.exports);\n\tif (exportsResult !== undefined) {\n\t\treturn exportsResult;\n\t}\n\n\t// Check main field\n\tconst mainResult = detectTypeFromMain(json.main);\n\tif (mainResult !== undefined) {\n\t\treturn mainResult;\n\t}\n\n\t// Check module field (ESM entry point)\n\tif (json.module !== undefined) {\n\t\treturn {\n\t\t\ttype: \"module\",\n\t\t\tisDualFormat: false,\n\t\t\treason: \"Detected ESM from module field presence\",\n\t\t};\n\t}\n\n\treturn {\n\t\ttype: undefined,\n\t\tisDualFormat: false,\n\t\treason: \"Could not detect module type from exports, main, or module fields\",\n\t};\n}\n\n/**\n * Check if a package matches an exclusion pattern.\n *\n * @param packageName - The package name\n * @param excludePackages - List of patterns to exclude\n * @returns true if the package should be excluded\n */\nfunction isExcluded(\n\tpackageName: string,\n\texcludePackages: string[] | undefined,\n): boolean {\n\tif (excludePackages === undefined || excludePackages.length === 0) {\n\t\treturn false;\n\t}\n\n\tfor (const pattern of excludePackages) {\n\t\t// Scope matching\n\t\tif (pattern.startsWith(\"@\") && packageName.startsWith(`${pattern}/`)) {\n\t\t\treturn true;\n\t\t}\n\t\t// Exact match\n\t\tif (packageName === pattern) {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n/**\n * Check if the package should be skipped from validation.\n */\nfunction shouldSkipPackage(\n\tpackageName: string | undefined,\n\tconfig: PackageEsmTypeConfig,\n): boolean {\n\t// Skip packages without a name\n\tif (packageName === undefined) {\n\t\treturn true;\n\t}\n\n\t// Skip the root package\n\tif (packageName === \"root\") {\n\t\treturn true;\n\t}\n\n\t// Check exclusions\n\treturn isExcluded(packageName, config.excludePackages);\n}\n\n/**\n * Determine the expected type and reason from the config and package.\n */\nfunction determineExpectedType(\n\tjson: PackageJson,\n\tconfig: PackageEsmTypeConfig,\n): {\n\texpectedType: \"module\" | \"commonjs\" | undefined;\n\tdetectionReason: string | undefined;\n} {\n\tif (config.requiredType !== undefined) {\n\t\treturn {\n\t\t\texpectedType: config.requiredType,\n\t\t\tdetectionReason: `Required type: ${config.requiredType}`,\n\t\t};\n\t}\n\n\tif (config.detectFromExports === true) {\n\t\tconst detection = detectTypeFromPackage(json);\n\t\treturn {\n\t\t\texpectedType: detection.type,\n\t\t\tdetectionReason: detection.reason,\n\t\t};\n\t}\n\n\treturn { expectedType: undefined, detectionReason: undefined };\n}\n\n/**\n * Build the error message for a type mismatch.\n */\nfunction buildErrorMessage(\n\tpackageName: string,\n\tcurrentType: string | undefined,\n\texpectedType: string,\n): string {\n\tif (currentType === undefined) {\n\t\treturn `Package \"${packageName}\" is missing the \"type\" field. Expected: \"type\": \"${expectedType}\"`;\n\t}\n\treturn `Package \"${packageName}\" has \"type\": \"${currentType}\" but expected \"type\": \"${expectedType}\"`;\n}\n\n/**\n * Apply the auto-fix for the type field.\n */\nasync function applyFix(\n\tjson: PackageJson,\n\tfile: string,\n\texpectedType: \"module\" | \"commonjs\",\n\tfailResult: PolicyFailure,\n): Promise<PolicyFixResult> {\n\tconst fixResult: PolicyFixResult = {\n\t\t...failResult,\n\t\tresolved: false,\n\t};\n\n\ttry {\n\t\tjson.type = expectedType;\n\t\tconst indent = await detectIndentation(file);\n\t\tawait writeJson(file, json, { spaces: indent });\n\t\tfixResult.resolved = true;\n\t} catch {\n\t\tfixResult.resolved = false;\n\t\tfixResult.errorMessages = [`Failed to update ${file}`];\n\t}\n\n\treturn fixResult;\n}\n\n/**\n * A policy that ensures the `type` field in package.json is correctly set.\n *\n * This policy helps maintain consistency in how packages declare their module format,\n * which is essential for proper module resolution in Node.js and bundlers.\n *\n * @remarks\n *\n * The policy can operate in two modes:\n *\n * 1. **Required Type**: Set `requiredType` to enforce a specific type for all packages\n * 2. **Detection**: Set `detectFromExports: true` to detect the expected type from\n * the package's exports or main field\n *\n * When `detectFromExports` is enabled, the policy looks for:\n * - `.mjs` extensions or `\"import\"` conditions → expects `\"module\"`\n * - `.cjs` extensions or `\"require\"` conditions → expects `\"commonjs\"`\n *\n * @example\n * ```typescript\n * import { makePolicy } from \"repopo\";\n * import { PackageEsmType } from \"repopo/policies\";\n *\n * // Require all packages to be ESM\n * makePolicy(PackageEsmType, { requiredType: \"module\" })\n *\n * // Or detect based on exports\n * makePolicy(PackageEsmType, { detectFromExports: true })\n * ```\n *\n * @alpha\n */\nexport const PackageEsmType = definePackagePolicy<\n\tPackageJson,\n\tPackageEsmTypeConfig | undefined\n>({\n\tname: \"PackageEsmType\",\n\tdescription:\n\t\t\"Ensures the type field in package.json correctly indicates ESM or CommonJS module format.\",\n\thandler: async (json, { file, config, resolve }) => {\n\t\t// If no config provided, skip validation\n\t\tif (config === undefined) {\n\t\t\treturn true;\n\t\t}\n\n\t\tconst packageName = json.name;\n\n\t\t// Check if package should be skipped\n\t\tif (shouldSkipPackage(packageName, config)) {\n\t\t\treturn true;\n\t\t}\n\n\t\t// Determine the expected type\n\t\tconst { expectedType, detectionReason } = determineExpectedType(\n\t\t\tjson,\n\t\t\tconfig,\n\t\t);\n\n\t\t// Handle case where type cannot be detected\n\t\tif (expectedType === undefined) {\n\t\t\tif (config.onDetectionFailure === \"fail\") {\n\t\t\t\treturn {\n\t\t\t\t\tname: \"PackageEsmType\",\n\t\t\t\t\tfile,\n\t\t\t\t\tautoFixable: false,\n\t\t\t\t\terrorMessages: [\n\t\t\t\t\t\t`Package \"${packageName}\": ${detectionReason ?? \"Could not detect module type\"}. Set \"type\" field explicitly or use \"requiredType\" config.`,\n\t\t\t\t\t],\n\t\t\t\t};\n\t\t\t}\n\t\t\t// \"skip\" and \"warn\" both pass validation\n\t\t\treturn true;\n\t\t}\n\n\t\t// Check if current type matches expected\n\t\tif (json.type === expectedType) {\n\t\t\treturn true;\n\t\t}\n\n\t\t// Build failure result\n\t\tconst failResult: PolicyFailure = {\n\t\t\tname: PackageEsmType.name,\n\t\t\tfile,\n\t\t\tautoFixable: true,\n\t\t\t// packageName is guaranteed to be defined here due to shouldSkipPackage check\n\t\t\terrorMessages: [\n\t\t\t\tbuildErrorMessage(packageName as string, json.type, expectedType),\n\t\t\t],\n\t\t};\n\n\t\tif (resolve) {\n\t\t\treturn applyFix(json, file, expectedType, failResult);\n\t\t}\n\n\t\treturn failResult;\n\t},\n});\n"]}
1
+ {"version":3,"file":"PackageEsmType.js","sourceRoot":"","sources":["../../src/policies/PackageEsmType.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 type { PolicyFailure, PolicyFixResult } from \"../policy.js\";\nimport { definePackagePolicy } from \"../policyDefiners/definePackagePolicy.js\";\nimport { detectIndentation } from \"../utils/indentation.js\";\n\nconst { writeFile: writeJson } = jsonfile;\n\n/**\n * Configuration for the PackageEsmType policy.\n *\n * This policy ensures the `type` field in package.json is correctly set\n * to indicate whether the package is an ESM or CommonJS module.\n *\n * @example\n * ```typescript\n * const config: PackageEsmTypeConfig = {\n * // Require ESM type field for all packages\n * requiredType: \"module\",\n * // Or detect based on exports/main field\n * detectFromExports: true,\n * };\n * ```\n *\n * @alpha\n */\nexport interface PackageEsmTypeConfig {\n\t/**\n\t * The required `type` field value for all packages.\n\t *\n\t * - `\"module\"` - All packages must be ESM (use import/export)\n\t * - `\"commonjs\"` - All packages must be CommonJS (use require)\n\t * - `undefined` - Use detection logic based on exports/main\n\t *\n\t * @remarks\n\t *\n\t * If set, this overrides automatic detection. Use `detectFromExports`\n\t * for more flexible per-package type detection.\n\t */\n\trequiredType?: \"module\" | \"commonjs\";\n\n\t/**\n\t * Detect the expected `type` field from the exports or main field.\n\t *\n\t * When enabled, the policy analyzes the package's entry points:\n\t * - If exports use `.mjs` or have `import` conditions → `\"module\"`\n\t * - If exports use `.cjs` or have `require` conditions → `\"commonjs\"`\n\t * - If main field uses `.mjs` → `\"module\"`\n\t * - If main field uses `.cjs` → `\"commonjs\"`\n\t *\n\t * @defaultValue false\n\t */\n\tdetectFromExports?: boolean;\n\n\t/**\n\t * Package names or scopes to exclude from validation.\n\t *\n\t * @example\n\t * ```typescript\n\t * excludePackages: [\"@myorg/legacy-package\", \"old-cjs-lib\"]\n\t * ```\n\t */\n\texcludePackages?: string[];\n\n\t/**\n\t * Behavior when module type cannot be detected from exports/main.\n\t *\n\t * - `\"skip\"` (default) - Skip validation for packages where type cannot be detected\n\t * - `\"warn\"` - Pass validation but include a warning in the output\n\t * - `\"fail\"` - Fail validation when type cannot be detected\n\t *\n\t * This only applies when `detectFromExports` is true and no `requiredType` is set.\n\t *\n\t * @defaultValue \"skip\"\n\t */\n\tonDetectionFailure?: \"skip\" | \"warn\" | \"fail\";\n}\n\n/**\n * Result of detecting the module type from a package.\n */\ninterface DetectionResult {\n\t/**\n\t * The detected type, if any.\n\t */\n\ttype: \"module\" | \"commonjs\" | undefined;\n\n\t/**\n\t * Whether the package appears to be dual-format (both ESM and CommonJS).\n\t * When true, the `type` field should typically match the package's default format.\n\t */\n\tisDualFormat: boolean;\n\n\t/**\n\t * Human-readable reason for the detection result.\n\t */\n\treason: string;\n}\n\n/**\n * Detect module type from the exports field.\n */\nfunction detectTypeFromExports(\n\texports: PackageJson[\"exports\"],\n): DetectionResult | undefined {\n\tif (exports === undefined) {\n\t\treturn undefined;\n\t}\n\n\tconst exportsStr = JSON.stringify(exports);\n\tconst hasEsm = exportsStr.includes(\".mjs\") || exportsStr.includes('\"import\"');\n\tconst hasCjs =\n\t\texportsStr.includes(\".cjs\") || exportsStr.includes('\"require\"');\n\n\t// Dual format packages have both ESM and CJS exports\n\tif (hasEsm && hasCjs) {\n\t\treturn {\n\t\t\ttype: \"module\",\n\t\t\tisDualFormat: true,\n\t\t\treason:\n\t\t\t\t\"Package has both ESM and CommonJS exports (dual format). Defaulting to ESM.\",\n\t\t};\n\t}\n\n\tif (hasEsm) {\n\t\treturn {\n\t\t\ttype: \"module\",\n\t\t\tisDualFormat: false,\n\t\t\treason: \"Detected ESM from exports field (.mjs or import condition)\",\n\t\t};\n\t}\n\n\tif (hasCjs) {\n\t\treturn {\n\t\t\ttype: \"commonjs\",\n\t\t\tisDualFormat: false,\n\t\t\treason:\n\t\t\t\t\"Detected CommonJS from exports field (.cjs or require condition)\",\n\t\t};\n\t}\n\n\treturn undefined;\n}\n\n/**\n * Detect module type from the main field.\n */\nfunction detectTypeFromMain(main: unknown): DetectionResult | undefined {\n\tif (typeof main !== \"string\") {\n\t\treturn undefined;\n\t}\n\n\tif (main.endsWith(\".mjs\")) {\n\t\treturn {\n\t\t\ttype: \"module\",\n\t\t\tisDualFormat: false,\n\t\t\treason: \"Detected ESM from main field (.mjs extension)\",\n\t\t};\n\t}\n\n\tif (main.endsWith(\".cjs\")) {\n\t\treturn {\n\t\t\ttype: \"commonjs\",\n\t\t\tisDualFormat: false,\n\t\t\treason: \"Detected CommonJS from main field (.cjs extension)\",\n\t\t};\n\t}\n\n\treturn undefined;\n}\n\n/**\n * Detect the expected type field from the package's exports or main field.\n *\n * @param json - The package.json content\n * @returns Detection result with type, dual-format indicator, and reason\n */\nfunction detectTypeFromPackage(json: PackageJson): DetectionResult {\n\t// Check exports field for type hints\n\tconst exportsResult = detectTypeFromExports(json.exports);\n\tif (exportsResult !== undefined) {\n\t\treturn exportsResult;\n\t}\n\n\t// Check main field\n\tconst mainResult = detectTypeFromMain(json.main);\n\tif (mainResult !== undefined) {\n\t\treturn mainResult;\n\t}\n\n\t// Check module field (ESM entry point)\n\tif (json.module !== undefined) {\n\t\treturn {\n\t\t\ttype: \"module\",\n\t\t\tisDualFormat: false,\n\t\t\treason: \"Detected ESM from module field presence\",\n\t\t};\n\t}\n\n\treturn {\n\t\ttype: undefined,\n\t\tisDualFormat: false,\n\t\treason: \"Could not detect module type from exports, main, or module fields\",\n\t};\n}\n\n/**\n * Check if a package matches an exclusion pattern.\n *\n * @param packageName - The package name\n * @param excludePackages - List of patterns to exclude\n * @returns true if the package should be excluded\n */\nfunction isExcluded(\n\tpackageName: string,\n\texcludePackages: string[] | undefined,\n): boolean {\n\tif (excludePackages === undefined || excludePackages.length === 0) {\n\t\treturn false;\n\t}\n\n\tfor (const pattern of excludePackages) {\n\t\t// Scope matching\n\t\tif (pattern.startsWith(\"@\") && packageName.startsWith(`${pattern}/`)) {\n\t\t\treturn true;\n\t\t}\n\t\t// Exact match\n\t\tif (packageName === pattern) {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n/**\n * Check if the package should be skipped from validation.\n */\nfunction shouldSkipPackage(\n\tpackageName: string | undefined,\n\tconfig: PackageEsmTypeConfig,\n): boolean {\n\t// Skip packages without a name\n\tif (packageName === undefined) {\n\t\treturn true;\n\t}\n\n\t// Skip the root package\n\tif (packageName === \"root\") {\n\t\treturn true;\n\t}\n\n\t// Check exclusions\n\treturn isExcluded(packageName, config.excludePackages);\n}\n\n/**\n * Determine the expected type and reason from the config and package.\n */\nfunction determineExpectedType(\n\tjson: PackageJson,\n\tconfig: PackageEsmTypeConfig,\n): {\n\texpectedType: \"module\" | \"commonjs\" | undefined;\n\tdetectionReason: string | undefined;\n} {\n\tif (config.requiredType !== undefined) {\n\t\treturn {\n\t\t\texpectedType: config.requiredType,\n\t\t\tdetectionReason: `Required type: ${config.requiredType}`,\n\t\t};\n\t}\n\n\tif (config.detectFromExports === true) {\n\t\tconst detection = detectTypeFromPackage(json);\n\t\treturn {\n\t\t\texpectedType: detection.type,\n\t\t\tdetectionReason: detection.reason,\n\t\t};\n\t}\n\n\treturn { expectedType: undefined, detectionReason: undefined };\n}\n\n/**\n * Build the error message for a type mismatch.\n */\nfunction buildErrorMessage(\n\tpackageName: string,\n\tcurrentType: string | undefined,\n\texpectedType: string,\n): string {\n\tif (currentType === undefined) {\n\t\treturn `Package \"${packageName}\" is missing the \"type\" field. Expected: \"type\": \"${expectedType}\"`;\n\t}\n\treturn `Package \"${packageName}\" has \"type\": \"${currentType}\" but expected \"type\": \"${expectedType}\"`;\n}\n\n/**\n * Apply the auto-fix for the type field.\n */\nasync function applyFix(\n\tjson: PackageJson,\n\tfile: string,\n\troot: string,\n\texpectedType: \"module\" | \"commonjs\",\n\tfailResult: PolicyFailure,\n): Promise<PolicyFixResult> {\n\tconst fixResult: PolicyFixResult = {\n\t\t...failResult,\n\t\tresolved: false,\n\t};\n\n\ttry {\n\t\tjson.type = expectedType;\n\t\tconst filePath = resolvePath(root, file);\n\t\tconst indent = await detectIndentation(filePath);\n\t\tawait writeJson(filePath, json, { spaces: indent });\n\t\tfixResult.resolved = true;\n\t} catch {\n\t\tfixResult.resolved = false;\n\t\tfixResult.errorMessages = [`Failed to update ${file}`];\n\t}\n\n\treturn fixResult;\n}\n\n/**\n * A policy that ensures the `type` field in package.json is correctly set.\n *\n * This policy helps maintain consistency in how packages declare their module format,\n * which is essential for proper module resolution in Node.js and bundlers.\n *\n * @remarks\n *\n * The policy can operate in two modes:\n *\n * 1. **Required Type**: Set `requiredType` to enforce a specific type for all packages\n * 2. **Detection**: Set `detectFromExports: true` to detect the expected type from\n * the package's exports or main field\n *\n * When `detectFromExports` is enabled, the policy looks for:\n * - `.mjs` extensions or `\"import\"` conditions → expects `\"module\"`\n * - `.cjs` extensions or `\"require\"` conditions → expects `\"commonjs\"`\n *\n * @example\n * ```typescript\n * import { makePolicy } from \"repopo\";\n * import { PackageEsmType } from \"repopo/policies\";\n *\n * // Require all packages to be ESM\n * makePolicy(PackageEsmType, { requiredType: \"module\" })\n *\n * // Or detect based on exports\n * makePolicy(PackageEsmType, { detectFromExports: true })\n * ```\n *\n * @alpha\n */\nexport const PackageEsmType = definePackagePolicy<\n\tPackageJson,\n\tPackageEsmTypeConfig | undefined\n>({\n\tname: \"PackageEsmType\",\n\tdescription:\n\t\t\"Ensures the type field in package.json correctly indicates ESM or CommonJS module format.\",\n\thandler: async (json, { file, root, config, resolve }) => {\n\t\t// If no config provided, skip validation\n\t\tif (config === undefined) {\n\t\t\treturn true;\n\t\t}\n\n\t\tconst packageName = json.name;\n\n\t\t// Check if package should be skipped\n\t\tif (shouldSkipPackage(packageName, config)) {\n\t\t\treturn true;\n\t\t}\n\n\t\t// Determine the expected type\n\t\tconst { expectedType, detectionReason } = determineExpectedType(\n\t\t\tjson,\n\t\t\tconfig,\n\t\t);\n\n\t\t// Handle case where type cannot be detected\n\t\tif (expectedType === undefined) {\n\t\t\tif (config.onDetectionFailure === \"fail\") {\n\t\t\t\treturn {\n\t\t\t\t\tname: \"PackageEsmType\",\n\t\t\t\t\tfile,\n\t\t\t\t\tautoFixable: false,\n\t\t\t\t\terrorMessages: [\n\t\t\t\t\t\t`Package \"${packageName}\": ${detectionReason ?? \"Could not detect module type\"}. Set \"type\" field explicitly or use \"requiredType\" config.`,\n\t\t\t\t\t],\n\t\t\t\t};\n\t\t\t}\n\t\t\t// \"skip\" and \"warn\" both pass validation\n\t\t\treturn true;\n\t\t}\n\n\t\t// Check if current type matches expected\n\t\tif (json.type === expectedType) {\n\t\t\treturn true;\n\t\t}\n\n\t\t// Build failure result\n\t\tconst failResult: PolicyFailure = {\n\t\t\tname: PackageEsmType.name,\n\t\t\tfile,\n\t\t\tautoFixable: true,\n\t\t\t// packageName is guaranteed to be defined here due to shouldSkipPackage check\n\t\t\terrorMessages: [\n\t\t\t\tbuildErrorMessage(packageName as string, json.type, expectedType),\n\t\t\t],\n\t\t};\n\n\t\tif (resolve) {\n\t\t\treturn applyFix(json, file, root, expectedType, failResult);\n\t\t}\n\n\t\treturn failResult;\n\t},\n});\n"]}
@@ -93,5 +93,5 @@ export interface PackageFolderNameConfig {
93
93
  *
94
94
  * @alpha
95
95
  */
96
- export declare const PackageFolderName: import("../policy.js").PolicyDefinition<PackageFolderNameConfig | undefined>;
96
+ export declare const PackageFolderName: import("../policy.js").PolicyShape<PackageFolderNameConfig | undefined>;
97
97
  //# sourceMappingURL=PackageFolderName.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"PackageFolderName.d.ts","sourceRoot":"","sources":["../../src/policies/PackageFolderName.ts"],"names":[],"mappings":"AAOA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,WAAW,uBAAuB;IACvC;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;IACH,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IAEvB;;;;;;;;;;OAUG;IACH,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;CAC3B;AAuCD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,eAAO,MAAM,iBAAiB,8EAqD5B,CAAC"}
1
+ {"version":3,"file":"PackageFolderName.d.ts","sourceRoot":"","sources":["../../src/policies/PackageFolderName.ts"],"names":[],"mappings":"AAOA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,WAAW,uBAAuB;IACvC;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;IACH,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IAEvB;;;;;;;;;;OAUG;IACH,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;CAC3B;AAuCD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,eAAO,MAAM,iBAAiB,yEAqD5B,CAAC"}
@@ -15,5 +15,5 @@ export interface PackageJsonPropertiesSettings {
15
15
  *
16
16
  * @alpha
17
17
  */
18
- export declare const PackageJsonProperties: import("../policy.js").PolicyDefinition<PackageJsonPropertiesSettings | undefined>;
18
+ export declare const PackageJsonProperties: import("../policy.js").PolicyShape<PackageJsonPropertiesSettings | undefined>;
19
19
  //# sourceMappingURL=PackageJsonProperties.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"PackageJsonProperties.d.ts","sourceRoot":"","sources":["../../src/policies/PackageJsonProperties.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAM7C;;;;GAIG;AACH,MAAM,WAAW,6BAA6B;IAC7C;;OAEG;IACH,QAAQ,EAAE,WAAW,CAAC;CACtB;AAED;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,oFAkDhC,CAAC"}
1
+ {"version":3,"file":"PackageJsonProperties.d.ts","sourceRoot":"","sources":["../../src/policies/PackageJsonProperties.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAc7C;;;;GAIG;AACH,MAAM,WAAW,6BAA6B;IAC7C;;OAEG;IACH,QAAQ,EAAE,WAAW,CAAC;CACtB;AAED;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,+EAoDhC,CAAC"}
@@ -1,9 +1,17 @@
1
- import { defu } from "defu";
1
+ import { createDefu } from "defu";
2
2
  import { call } from "effection";
3
3
  import jsonfile from "jsonfile";
4
4
  import diff from "microdiff";
5
+ import { resolve as resolvePath } from "pathe";
5
6
  import { definePackagePolicy } from "../policyDefiners/definePackagePolicy.js";
6
7
  const { writeFile: writeJson } = jsonfile;
8
+ const mergeProperties = createDefu((object, key, value) => {
9
+ if (Array.isArray(value)) {
10
+ object[key] = value;
11
+ return true;
12
+ }
13
+ return false;
14
+ });
7
15
  /**
8
16
  * A RepoPolicy that checks that package.json properties in packages match expected values.
9
17
  *
@@ -12,7 +20,7 @@ const { writeFile: writeJson } = jsonfile;
12
20
  export const PackageJsonProperties = definePackagePolicy({
13
21
  name: "PackageJsonProperties",
14
22
  description: "Ensures package.json files contain required properties with correct values.",
15
- handler: function* (json, { file, config, resolve }) {
23
+ handler: function* (json, { file, root, config, resolve }) {
16
24
  if (config === undefined) {
17
25
  return true;
18
26
  }
@@ -23,7 +31,7 @@ export const PackageJsonProperties = definePackagePolicy({
23
31
  autoFixable: true,
24
32
  errorMessages: [],
25
33
  };
26
- const merged = defu(verbatim, json);
34
+ const merged = mergeProperties(verbatim, json);
27
35
  const result = diff(merged, json);
28
36
  const messages = [];
29
37
  for (const diffResult of result) {
@@ -35,7 +43,7 @@ export const PackageJsonProperties = definePackagePolicy({
35
43
  ...failResult,
36
44
  resolved: false,
37
45
  };
38
- yield* call(() => writeJson(file, merged, { spaces: "\t" }));
46
+ yield* call(() => writeJson(resolvePath(root, file), merged, { spaces: "\t" }));
39
47
  fixResult.resolved = true;
40
48
  return fixResult;
41
49
  }
@@ -1 +1 @@
1
- {"version":3,"file":"PackageJsonProperties.js","sourceRoot":"","sources":["../../src/policies/PackageJsonProperties.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,QAAQ,MAAM,UAAU,CAAC;AAChC,OAAO,IAAI,MAAM,WAAW,CAAC;AAG7B,OAAO,EAAE,mBAAmB,EAAE,MAAM,0CAA0C,CAAC;AAE/E,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,GAAG,QAAQ,CAAC;AAc1C;;;;GAIG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,mBAAmB,CAGtD;IACD,IAAI,EAAE,uBAAuB;IAC7B,WAAW,EACV,6EAA6E;IAC9E,OAAO,EAAE,QAAQ,CAAC,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE;QAClD,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YAC1B,OAAO,IAAI,CAAC;QACb,CAAC;QACD,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,CAAC;QAE5B,MAAM,UAAU,GAAkB;YACjC,IAAI,EAAE,qBAAqB,CAAC,IAAI;YAChC,IAAI;YACJ,WAAW,EAAE,IAAI;YACjB,aAAa,EAAE,EAAE;SACjB,CAAC;QAEF,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QACpC,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QAElC,MAAM,QAAQ,GAAa,EAAE,CAAC;QAC9B,KAAK,MAAM,UAAU,IAAI,MAAM,EAAE,CAAC;YACjC,QAAQ,CAAC,IAAI,CACZ,2CAA2C,UAAU,CAAC,IAAI,IAAI,CAC9D,CAAC;QACH,CAAC;QAED,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACzB,IAAI,OAAO,EAAE,CAAC;gBACb,MAAM,SAAS,GAAoB;oBAClC,GAAG,UAAU;oBACb,QAAQ,EAAE,KAAK;iBACf,CAAC;gBAEF,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;gBAE7D,SAAS,CAAC,QAAQ,GAAG,IAAI,CAAC;gBAC1B,OAAO,SAAS,CAAC;YAClB,CAAC;YAED,2EAA2E;YAC3E,UAAU,CAAC,aAAa,GAAG,QAAQ,CAAC;YACpC,OAAO,UAAU,CAAC;QACnB,CAAC;QAED,OAAO,IAAI,CAAC;IACb,CAAC;CACD,CAAC,CAAC","sourcesContent":["import { defu } from \"defu\";\nimport { call } from \"effection\";\nimport jsonfile from \"jsonfile\";\nimport diff from \"microdiff\";\nimport type { PackageJson } from \"type-fest\";\nimport type { PolicyFailure, PolicyFixResult } from \"../policy.js\";\nimport { definePackagePolicy } from \"../policyDefiners/definePackagePolicy.js\";\n\nconst { writeFile: writeJson } = jsonfile;\n\n/**\n * Policy settings for the PackageJsonProperties repo policy.\n *\n * @alpha\n */\nexport interface PackageJsonPropertiesSettings {\n\t/**\n\t * Sets a package.json property to the value provided. The value will be used verbatim.\n\t */\n\tverbatim: PackageJson;\n}\n\n/**\n * A RepoPolicy that checks that package.json properties in packages match expected values.\n *\n * @alpha\n */\nexport const PackageJsonProperties = definePackagePolicy<\n\tPackageJson,\n\tPackageJsonPropertiesSettings | undefined\n>({\n\tname: \"PackageJsonProperties\",\n\tdescription:\n\t\t\"Ensures package.json files contain required properties with correct values.\",\n\thandler: function* (json, { file, config, resolve }) {\n\t\tif (config === undefined) {\n\t\t\treturn true;\n\t\t}\n\t\tconst { verbatim } = config;\n\n\t\tconst failResult: PolicyFailure = {\n\t\t\tname: PackageJsonProperties.name,\n\t\t\tfile,\n\t\t\tautoFixable: true,\n\t\t\terrorMessages: [],\n\t\t};\n\n\t\tconst merged = defu(verbatim, json);\n\t\tconst result = diff(merged, json);\n\n\t\tconst messages: string[] = [];\n\t\tfor (const diffResult of result) {\n\t\t\tmessages.push(\n\t\t\t\t`Incorrect package.json field value for '${diffResult.path}'.`,\n\t\t\t);\n\t\t}\n\n\t\tif (messages.length > 0) {\n\t\t\tif (resolve) {\n\t\t\t\tconst fixResult: PolicyFixResult = {\n\t\t\t\t\t...failResult,\n\t\t\t\t\tresolved: false,\n\t\t\t\t};\n\n\t\t\t\tyield* call(() => writeJson(file, merged, { spaces: \"\\t\" }));\n\n\t\t\t\tfixResult.resolved = true;\n\t\t\t\treturn fixResult;\n\t\t\t}\n\n\t\t\t// There were errors, and we're not resolving them, so return a fail result\n\t\t\tfailResult.errorMessages = messages;\n\t\t\treturn failResult;\n\t\t}\n\n\t\treturn true;\n\t},\n});\n"]}
1
+ {"version":3,"file":"PackageJsonProperties.js","sourceRoot":"","sources":["../../src/policies/PackageJsonProperties.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,QAAQ,MAAM,UAAU,CAAC;AAChC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,OAAO,CAAC;AAG/C,OAAO,EAAE,mBAAmB,EAAE,MAAM,0CAA0C,CAAC;AAE/E,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,GAAG,QAAQ,CAAC;AAE1C,MAAM,eAAe,GAAG,UAAU,CAAC,CAAC,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE;IACzD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QAC1B,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;QACpB,OAAO,IAAI,CAAC;IACb,CAAC;IACD,OAAO,KAAK,CAAC;AACd,CAAC,CAAC,CAAC;AAcH;;;;GAIG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,mBAAmB,CAGtD;IACD,IAAI,EAAE,uBAAuB;IAC7B,WAAW,EACV,6EAA6E;IAC9E,OAAO,EAAE,QAAQ,CAAC,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE;QACxD,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YAC1B,OAAO,IAAI,CAAC;QACb,CAAC;QACD,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,CAAC;QAE5B,MAAM,UAAU,GAAkB;YACjC,IAAI,EAAE,qBAAqB,CAAC,IAAI;YAChC,IAAI;YACJ,WAAW,EAAE,IAAI;YACjB,aAAa,EAAE,EAAE;SACjB,CAAC;QAEF,MAAM,MAAM,GAAG,eAAe,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QAC/C,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QAElC,MAAM,QAAQ,GAAa,EAAE,CAAC;QAC9B,KAAK,MAAM,UAAU,IAAI,MAAM,EAAE,CAAC;YACjC,QAAQ,CAAC,IAAI,CACZ,2CAA2C,UAAU,CAAC,IAAI,IAAI,CAC9D,CAAC;QACH,CAAC;QAED,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACzB,IAAI,OAAO,EAAE,CAAC;gBACb,MAAM,SAAS,GAAoB;oBAClC,GAAG,UAAU;oBACb,QAAQ,EAAE,KAAK;iBACf,CAAC;gBAEF,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,CAChB,SAAS,CAAC,WAAW,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAC5D,CAAC;gBAEF,SAAS,CAAC,QAAQ,GAAG,IAAI,CAAC;gBAC1B,OAAO,SAAS,CAAC;YAClB,CAAC;YAED,2EAA2E;YAC3E,UAAU,CAAC,aAAa,GAAG,QAAQ,CAAC;YACpC,OAAO,UAAU,CAAC;QACnB,CAAC;QAED,OAAO,IAAI,CAAC;IACb,CAAC;CACD,CAAC,CAAC","sourcesContent":["import { createDefu } from \"defu\";\nimport { call } from \"effection\";\nimport jsonfile from \"jsonfile\";\nimport diff from \"microdiff\";\nimport { resolve as resolvePath } from \"pathe\";\nimport type { PackageJson } from \"type-fest\";\nimport type { PolicyFailure, PolicyFixResult } from \"../policy.js\";\nimport { definePackagePolicy } from \"../policyDefiners/definePackagePolicy.js\";\n\nconst { writeFile: writeJson } = jsonfile;\n\nconst mergeProperties = createDefu((object, key, value) => {\n\tif (Array.isArray(value)) {\n\t\tobject[key] = value;\n\t\treturn true;\n\t}\n\treturn false;\n});\n\n/**\n * Policy settings for the PackageJsonProperties repo policy.\n *\n * @alpha\n */\nexport interface PackageJsonPropertiesSettings {\n\t/**\n\t * Sets a package.json property to the value provided. The value will be used verbatim.\n\t */\n\tverbatim: PackageJson;\n}\n\n/**\n * A RepoPolicy that checks that package.json properties in packages match expected values.\n *\n * @alpha\n */\nexport const PackageJsonProperties = definePackagePolicy<\n\tPackageJson,\n\tPackageJsonPropertiesSettings | undefined\n>({\n\tname: \"PackageJsonProperties\",\n\tdescription:\n\t\t\"Ensures package.json files contain required properties with correct values.\",\n\thandler: function* (json, { file, root, config, resolve }) {\n\t\tif (config === undefined) {\n\t\t\treturn true;\n\t\t}\n\t\tconst { verbatim } = config;\n\n\t\tconst failResult: PolicyFailure = {\n\t\t\tname: PackageJsonProperties.name,\n\t\t\tfile,\n\t\t\tautoFixable: true,\n\t\t\terrorMessages: [],\n\t\t};\n\n\t\tconst merged = mergeProperties(verbatim, json);\n\t\tconst result = diff(merged, json);\n\n\t\tconst messages: string[] = [];\n\t\tfor (const diffResult of result) {\n\t\t\tmessages.push(\n\t\t\t\t`Incorrect package.json field value for '${diffResult.path}'.`,\n\t\t\t);\n\t\t}\n\n\t\tif (messages.length > 0) {\n\t\t\tif (resolve) {\n\t\t\t\tconst fixResult: PolicyFixResult = {\n\t\t\t\t\t...failResult,\n\t\t\t\t\tresolved: false,\n\t\t\t\t};\n\n\t\t\t\tyield* call(() =>\n\t\t\t\t\twriteJson(resolvePath(root, file), merged, { spaces: \"\\t\" }),\n\t\t\t\t);\n\n\t\t\t\tfixResult.resolved = true;\n\t\t\t\treturn fixResult;\n\t\t\t}\n\n\t\t\t// There were errors, and we're not resolving them, so return a fail result\n\t\t\tfailResult.errorMessages = messages;\n\t\t\treturn failResult;\n\t\t}\n\n\t\treturn true;\n\t},\n});\n"]}
@@ -4,5 +4,5 @@
4
4
  *
5
5
  * @alpha
6
6
  */
7
- export declare const PackageJsonRepoDirectoryProperty: import("../policy.js").PolicyDefinition<undefined>;
7
+ export declare const PackageJsonRepoDirectoryProperty: import("../policy.js").PolicyShape<undefined>;
8
8
  //# sourceMappingURL=PackageJsonRepoDirectoryProperty.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"PackageJsonRepoDirectoryProperty.d.ts","sourceRoot":"","sources":["../../src/policies/PackageJsonRepoDirectoryProperty.ts"],"names":[],"mappings":"AAOA;;;;;GAKG;AACH,eAAO,MAAM,gCAAgC,oDAuD3C,CAAC"}
1
+ {"version":3,"file":"PackageJsonRepoDirectoryProperty.d.ts","sourceRoot":"","sources":["../../src/policies/PackageJsonRepoDirectoryProperty.ts"],"names":[],"mappings":"AAOA;;;;;GAKG;AACH,eAAO,MAAM,gCAAgC,+CAwD3C,CAAC"}
@@ -22,14 +22,15 @@ export const PackageJsonRepoDirectoryProperty = definePackagePolicy({
22
22
  ...failResult,
23
23
  resolved: false,
24
24
  };
25
- const pkgDir = path.dirname(file);
25
+ const packageJsonPath = path.resolve(root, file);
26
+ const pkgDir = path.dirname(packageJsonPath);
26
27
  const maybeDir = path.relative(root, pkgDir);
27
28
  const relativePkgDir = maybeDir === "" ? undefined : maybeDir;
28
29
  if (typeof json.repository === "object" &&
29
30
  json.repository.directory !== relativePkgDir) {
30
31
  if (resolve) {
31
32
  try {
32
- updatePackageJsonFile(file, (pkgJson) => {
33
+ updatePackageJsonFile(packageJsonPath, (pkgJson) => {
33
34
  assert(typeof pkgJson.repository === "object");
34
35
  if (relativePkgDir === undefined) {
35
36
  delete pkgJson.repository.directory;
@@ -1 +1 @@
1
- {"version":3,"file":"PackageJsonRepoDirectoryProperty.js","sourceRoot":"","sources":["../../src/policies/PackageJsonRepoDirectoryProperty.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,qBAAqB,EAAE,MAAM,mCAAmC,CAAC;AAC1E,OAAO,IAAI,MAAM,OAAO,CAAC;AAGzB,OAAO,EAAE,mBAAmB,EAAE,MAAM,0CAA0C,CAAC;AAE/E;;;;;GAKG;AACH,MAAM,CAAC,MAAM,gCAAgC,GAAG,mBAAmB,CAGjE;IACD,IAAI,EAAE,kCAAkC;IACxC,WAAW,EACV,mGAAmG;IACpG,OAAO,EAAE,QAAQ,CAAC,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE;QAChD,MAAM,UAAU,GAAkB;YACjC,IAAI,EAAE,gCAAgC,CAAC,IAAI;YAC3C,IAAI;YACJ,WAAW,EAAE,IAAI;YACjB,aAAa,EAAE,EAAE;SACjB,CAAC;QAEF,MAAM,SAAS,GAAoB;YAClC,GAAG,UAAU;YACb,QAAQ,EAAE,KAAK;SACf,CAAC;QAEF,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAClC,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QAC7C,MAAM,cAAc,GAAG,QAAQ,KAAK,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC;QAE9D,IACC,OAAO,IAAI,CAAC,UAAU,KAAK,QAAQ;YACnC,IAAI,CAAC,UAAU,CAAC,SAAS,KAAK,cAAc,EAC3C,CAAC;YACF,IAAI,OAAO,EAAE,CAAC;gBACb,IAAI,CAAC;oBACJ,qBAAqB,CAAC,IAAI,EAAE,CAAC,OAAO,EAAE,EAAE;wBACvC,MAAM,CAAC,OAAO,OAAO,CAAC,UAAU,KAAK,QAAQ,CAAC,CAAC;wBAC/C,IAAI,cAAc,KAAK,SAAS,EAAE,CAAC;4BAClC,OAAO,OAAO,CAAC,UAAU,CAAC,SAAS,CAAC;wBACrC,CAAC;6BAAM,CAAC;4BACP,OAAO,CAAC,UAAU,CAAC,SAAS,GAAG,cAAc,CAAC;wBAC/C,CAAC;oBACF,CAAC,CAAC,CAAC;oBACH,SAAS,CAAC,QAAQ,GAAG,IAAI,CAAC;gBAC3B,CAAC;gBAAC,OAAO,KAAc,EAAE,CAAC;oBACzB,SAAS,CAAC,QAAQ,GAAG,KAAK,CAAC;oBAC3B,SAAS,CAAC,aAAa,CAAC,IAAI,CAC3B,GAAI,KAAe,CAAC,OAAO,KAAM,KAAe,CAAC,KAAK,EAAE,CACxD,CAAC;gBACH,CAAC;gBACD,OAAO,SAAS,CAAC;YAClB,CAAC;YACD,UAAU,CAAC,aAAa,CAAC,IAAI,CAC5B,kDAAkD,cAAc,WAAW,IAAI,CAAC,UAAU,CAAC,SAAS,GAAG,CACvG,CAAC;YACF,OAAO,UAAU,CAAC;QACnB,CAAC;QAED,OAAO,IAAI,CAAC;IACb,CAAC;CACD,CAAC,CAAC","sourcesContent":["import assert from \"node:assert/strict\";\nimport { updatePackageJsonFile } from \"@fluid-tools/build-infrastructure\";\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/**\n * A RepoPolicy that checks that the repository.directory property in package.json is set correctly. If the repository\n * field is a string instead of an object the package will be ignored.\n *\n * @alpha\n */\nexport const PackageJsonRepoDirectoryProperty = definePackagePolicy<\n\tPackageJson,\n\tundefined\n>({\n\tname: \"PackageJsonRepoDirectoryProperty\",\n\tdescription:\n\t\t\"Ensures the repository.directory property in package.json is set correctly for monorepo packages.\",\n\thandler: function* (json, { file, root, resolve }) {\n\t\tconst failResult: PolicyFailure = {\n\t\t\tname: PackageJsonRepoDirectoryProperty.name,\n\t\t\tfile,\n\t\t\tautoFixable: true,\n\t\t\terrorMessages: [],\n\t\t};\n\n\t\tconst fixResult: PolicyFixResult = {\n\t\t\t...failResult,\n\t\t\tresolved: false,\n\t\t};\n\n\t\tconst pkgDir = path.dirname(file);\n\t\tconst maybeDir = path.relative(root, pkgDir);\n\t\tconst relativePkgDir = maybeDir === \"\" ? undefined : maybeDir;\n\n\t\tif (\n\t\t\ttypeof json.repository === \"object\" &&\n\t\t\tjson.repository.directory !== relativePkgDir\n\t\t) {\n\t\t\tif (resolve) {\n\t\t\t\ttry {\n\t\t\t\t\tupdatePackageJsonFile(file, (pkgJson) => {\n\t\t\t\t\t\tassert(typeof pkgJson.repository === \"object\");\n\t\t\t\t\t\tif (relativePkgDir === undefined) {\n\t\t\t\t\t\t\tdelete pkgJson.repository.directory;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tpkgJson.repository.directory = relativePkgDir;\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t\tfixResult.resolved = true;\n\t\t\t\t} catch (error: unknown) {\n\t\t\t\t\tfixResult.resolved = false;\n\t\t\t\t\tfixResult.errorMessages.push(\n\t\t\t\t\t\t`${(error as Error).message}\\n${(error as Error).stack}`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\treturn fixResult;\n\t\t\t}\n\t\t\tfailResult.errorMessages.push(\n\t\t\t\t`repository.directory value is wrong. Expected '${relativePkgDir}', got '${json.repository.directory}'`,\n\t\t\t);\n\t\t\treturn failResult;\n\t\t}\n\n\t\treturn true;\n\t},\n});\n"]}
1
+ {"version":3,"file":"PackageJsonRepoDirectoryProperty.js","sourceRoot":"","sources":["../../src/policies/PackageJsonRepoDirectoryProperty.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,qBAAqB,EAAE,MAAM,mCAAmC,CAAC;AAC1E,OAAO,IAAI,MAAM,OAAO,CAAC;AAGzB,OAAO,EAAE,mBAAmB,EAAE,MAAM,0CAA0C,CAAC;AAE/E;;;;;GAKG;AACH,MAAM,CAAC,MAAM,gCAAgC,GAAG,mBAAmB,CAGjE;IACD,IAAI,EAAE,kCAAkC;IACxC,WAAW,EACV,mGAAmG;IACpG,OAAO,EAAE,QAAQ,CAAC,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE;QAChD,MAAM,UAAU,GAAkB;YACjC,IAAI,EAAE,gCAAgC,CAAC,IAAI;YAC3C,IAAI;YACJ,WAAW,EAAE,IAAI;YACjB,aAAa,EAAE,EAAE;SACjB,CAAC;QAEF,MAAM,SAAS,GAAoB;YAClC,GAAG,UAAU;YACb,QAAQ,EAAE,KAAK;SACf,CAAC;QAEF,MAAM,eAAe,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QACjD,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;QAC7C,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QAC7C,MAAM,cAAc,GAAG,QAAQ,KAAK,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC;QAE9D,IACC,OAAO,IAAI,CAAC,UAAU,KAAK,QAAQ;YACnC,IAAI,CAAC,UAAU,CAAC,SAAS,KAAK,cAAc,EAC3C,CAAC;YACF,IAAI,OAAO,EAAE,CAAC;gBACb,IAAI,CAAC;oBACJ,qBAAqB,CAAC,eAAe,EAAE,CAAC,OAAO,EAAE,EAAE;wBAClD,MAAM,CAAC,OAAO,OAAO,CAAC,UAAU,KAAK,QAAQ,CAAC,CAAC;wBAC/C,IAAI,cAAc,KAAK,SAAS,EAAE,CAAC;4BAClC,OAAO,OAAO,CAAC,UAAU,CAAC,SAAS,CAAC;wBACrC,CAAC;6BAAM,CAAC;4BACP,OAAO,CAAC,UAAU,CAAC,SAAS,GAAG,cAAc,CAAC;wBAC/C,CAAC;oBACF,CAAC,CAAC,CAAC;oBACH,SAAS,CAAC,QAAQ,GAAG,IAAI,CAAC;gBAC3B,CAAC;gBAAC,OAAO,KAAc,EAAE,CAAC;oBACzB,SAAS,CAAC,QAAQ,GAAG,KAAK,CAAC;oBAC3B,SAAS,CAAC,aAAa,CAAC,IAAI,CAC3B,GAAI,KAAe,CAAC,OAAO,KAAM,KAAe,CAAC,KAAK,EAAE,CACxD,CAAC;gBACH,CAAC;gBACD,OAAO,SAAS,CAAC;YAClB,CAAC;YACD,UAAU,CAAC,aAAa,CAAC,IAAI,CAC5B,kDAAkD,cAAc,WAAW,IAAI,CAAC,UAAU,CAAC,SAAS,GAAG,CACvG,CAAC;YACF,OAAO,UAAU,CAAC;QACnB,CAAC;QAED,OAAO,IAAI,CAAC;IACb,CAAC;CACD,CAAC,CAAC","sourcesContent":["import assert from \"node:assert/strict\";\nimport { updatePackageJsonFile } from \"@fluid-tools/build-infrastructure\";\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/**\n * A RepoPolicy that checks that the repository.directory property in package.json is set correctly. If the repository\n * field is a string instead of an object the package will be ignored.\n *\n * @alpha\n */\nexport const PackageJsonRepoDirectoryProperty = definePackagePolicy<\n\tPackageJson,\n\tundefined\n>({\n\tname: \"PackageJsonRepoDirectoryProperty\",\n\tdescription:\n\t\t\"Ensures the repository.directory property in package.json is set correctly for monorepo packages.\",\n\thandler: function* (json, { file, root, resolve }) {\n\t\tconst failResult: PolicyFailure = {\n\t\t\tname: PackageJsonRepoDirectoryProperty.name,\n\t\t\tfile,\n\t\t\tautoFixable: true,\n\t\t\terrorMessages: [],\n\t\t};\n\n\t\tconst fixResult: PolicyFixResult = {\n\t\t\t...failResult,\n\t\t\tresolved: false,\n\t\t};\n\n\t\tconst packageJsonPath = path.resolve(root, file);\n\t\tconst pkgDir = path.dirname(packageJsonPath);\n\t\tconst maybeDir = path.relative(root, pkgDir);\n\t\tconst relativePkgDir = maybeDir === \"\" ? undefined : maybeDir;\n\n\t\tif (\n\t\t\ttypeof json.repository === \"object\" &&\n\t\t\tjson.repository.directory !== relativePkgDir\n\t\t) {\n\t\t\tif (resolve) {\n\t\t\t\ttry {\n\t\t\t\t\tupdatePackageJsonFile(packageJsonPath, (pkgJson) => {\n\t\t\t\t\t\tassert(typeof pkgJson.repository === \"object\");\n\t\t\t\t\t\tif (relativePkgDir === undefined) {\n\t\t\t\t\t\t\tdelete pkgJson.repository.directory;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tpkgJson.repository.directory = relativePkgDir;\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t\tfixResult.resolved = true;\n\t\t\t\t} catch (error: unknown) {\n\t\t\t\t\tfixResult.resolved = false;\n\t\t\t\t\tfixResult.errorMessages.push(\n\t\t\t\t\t\t`${(error as Error).message}\\n${(error as Error).stack}`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\treturn fixResult;\n\t\t\t}\n\t\t\tfailResult.errorMessages.push(\n\t\t\t\t`repository.directory value is wrong. Expected '${relativePkgDir}', got '${json.repository.directory}'`,\n\t\t\t);\n\t\t\treturn failResult;\n\t\t}\n\n\t\treturn true;\n\t},\n});\n"]}
@@ -3,5 +3,5 @@
3
3
  *
4
4
  * @alpha
5
5
  */
6
- export declare const PackageJsonSorted: import("../policy.js").PolicyDefinition<undefined>;
6
+ export declare const PackageJsonSorted: import("../policy.js").PolicyShape<undefined>;
7
7
  //# sourceMappingURL=PackageJsonSorted.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"PackageJsonSorted.d.ts","sourceRoot":"","sources":["../../src/policies/PackageJsonSorted.ts"],"names":[],"mappings":"AAOA;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,oDAiD5B,CAAC"}
1
+ {"version":3,"file":"PackageJsonSorted.d.ts","sourceRoot":"","sources":["../../src/policies/PackageJsonSorted.ts"],"names":[],"mappings":"AAwCA;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,+CA0D5B,CAAC"}
@@ -1,8 +1,33 @@
1
- import { updatePackageJsonFile } from "@tylerbu/cli-api";
2
- import { call } from "effection";
1
+ import jsonfile from "jsonfile";
3
2
  import { resolve as resolvePath } from "pathe";
4
- import { sortPackageJson } from "sort-package-json";
5
3
  import { definePackagePolicy } from "../policyDefiners/definePackagePolicy.js";
4
+ import { detectIndentation } from "../utils/indentation.js";
5
+ const { writeFile: writeJson } = jsonfile;
6
+ const missingDependencyMessage = "PackageJsonSorted requires the optional peer dependency sort-package-json. Install it to enable this policy.";
7
+ function isMissingSortPackageJsonError(error) {
8
+ if (error instanceof Error &&
9
+ "code" in error &&
10
+ error.code === "ERR_MODULE_NOT_FOUND" &&
11
+ error.message.includes("sort-package-json")) {
12
+ return true;
13
+ }
14
+ return (typeof error === "object" &&
15
+ error !== null &&
16
+ "cause" in error &&
17
+ isMissingSortPackageJsonError(error.cause));
18
+ }
19
+ async function loadSortPackageJson() {
20
+ try {
21
+ const { sortPackageJson } = await import("sort-package-json");
22
+ return sortPackageJson;
23
+ }
24
+ catch (error) {
25
+ if (isMissingSortPackageJsonError(error)) {
26
+ return undefined;
27
+ }
28
+ throw error;
29
+ }
30
+ }
6
31
  /**
7
32
  * A repo policy that checks if package.json files in the repo are sorted using sort-package-json.
8
33
  *
@@ -11,7 +36,17 @@ import { definePackagePolicy } from "../policyDefiners/definePackagePolicy.js";
11
36
  export const PackageJsonSorted = definePackagePolicy({
12
37
  name: "PackageJsonSorted",
13
38
  description: "Ensures package.json files are sorted consistently using sort-package-json.",
14
- handler: function* (json, { file, root, resolve }) {
39
+ handler: async (json, { file, root, resolve }) => {
40
+ const sortPackageJson = await loadSortPackageJson();
41
+ if (sortPackageJson === undefined) {
42
+ const result = {
43
+ name: PackageJsonSorted.name,
44
+ file,
45
+ autoFixable: false,
46
+ errorMessages: [missingDependencyMessage],
47
+ };
48
+ return result;
49
+ }
15
50
  const sortedJson = sortPackageJson(json);
16
51
  const isSorted = JSON.stringify(sortedJson) === JSON.stringify(json);
17
52
  if (isSorted) {
@@ -19,9 +54,9 @@ export const PackageJsonSorted = definePackagePolicy({
19
54
  }
20
55
  if (resolve) {
21
56
  try {
22
- yield* call(() => updatePackageJsonFile(resolvePath(root, file), (pkgJson) => pkgJson, {
23
- sort: true,
24
- }));
57
+ const packageJsonPath = resolvePath(root, file);
58
+ const indent = await detectIndentation(packageJsonPath);
59
+ await writeJson(packageJsonPath, sortedJson, { spaces: indent });
25
60
  const result = {
26
61
  name: PackageJsonSorted.name,
27
62
  file,