repopo 0.7.1 → 0.9.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 (122) hide show
  1. package/CHANGELOG.md +217 -6
  2. package/THIRD-PARTY-LICENSES.txt +152 -5
  3. package/esm/adapters/fluidFramework.d.ts +103 -0
  4. package/esm/adapters/fluidFramework.d.ts.map +1 -0
  5. package/esm/adapters/fluidFramework.js +124 -0
  6. package/esm/adapters/fluidFramework.js.map +1 -0
  7. package/esm/baseCommand.d.ts +1 -0
  8. package/esm/baseCommand.d.ts.map +1 -1
  9. package/esm/baseCommand.js +2 -0
  10. package/esm/baseCommand.js.map +1 -1
  11. package/esm/commands/check.d.ts.map +1 -1
  12. package/esm/commands/check.js +76 -50
  13. package/esm/commands/check.js.map +1 -1
  14. package/esm/commands/list.d.ts.map +1 -1
  15. package/esm/commands/list.js +3 -1
  16. package/esm/commands/list.js.map +1 -1
  17. package/esm/generators.d.ts +30 -2
  18. package/esm/generators.d.ts.map +1 -1
  19. package/esm/generators.js +31 -1
  20. package/esm/generators.js.map +1 -1
  21. package/esm/index.d.ts +14 -3
  22. package/esm/index.d.ts.map +1 -1
  23. package/esm/index.js +13 -2
  24. package/esm/index.js.map +1 -1
  25. package/esm/makePolicy.d.ts +31 -2
  26. package/esm/makePolicy.d.ts.map +1 -1
  27. package/esm/makePolicy.js +27 -13
  28. package/esm/makePolicy.js.map +1 -1
  29. package/esm/perf.d.ts +6 -2
  30. package/esm/perf.d.ts.map +1 -1
  31. package/esm/perf.js +5 -4
  32. package/esm/perf.js.map +1 -1
  33. package/esm/policies/HtmlFileHeaders.d.ts.map +1 -1
  34. package/esm/policies/HtmlFileHeaders.js +11 -7
  35. package/esm/policies/HtmlFileHeaders.js.map +1 -1
  36. package/esm/policies/JsTsFileHeaders.d.ts.map +1 -1
  37. package/esm/policies/JsTsFileHeaders.js +11 -7
  38. package/esm/policies/JsTsFileHeaders.js.map +1 -1
  39. package/esm/policies/LicenseFileExists.d.ts.map +1 -1
  40. package/esm/policies/LicenseFileExists.js +22 -16
  41. package/esm/policies/LicenseFileExists.js.map +1 -1
  42. package/esm/policies/NoJsFileExtensions.d.ts.map +1 -1
  43. package/esm/policies/NoJsFileExtensions.js +16 -9
  44. package/esm/policies/NoJsFileExtensions.js.map +1 -1
  45. package/esm/policies/NoLargeBinaryFiles.d.ts.map +1 -1
  46. package/esm/policies/NoLargeBinaryFiles.js +35 -29
  47. package/esm/policies/NoLargeBinaryFiles.js.map +1 -1
  48. package/esm/policies/NoPrivateWorkspaceDependencies.d.ts +37 -0
  49. package/esm/policies/NoPrivateWorkspaceDependencies.d.ts.map +1 -0
  50. package/esm/policies/NoPrivateWorkspaceDependencies.js +145 -0
  51. package/esm/policies/NoPrivateWorkspaceDependencies.js.map +1 -0
  52. package/esm/policies/PackageAllowedScopes.d.ts +88 -0
  53. package/esm/policies/PackageAllowedScopes.d.ts.map +1 -0
  54. package/esm/policies/PackageAllowedScopes.js +118 -0
  55. package/esm/policies/PackageAllowedScopes.js.map +1 -0
  56. package/esm/policies/PackageEsmType.d.ts +100 -0
  57. package/esm/policies/PackageEsmType.d.ts.map +1 -0
  58. package/esm/policies/PackageEsmType.js +261 -0
  59. package/esm/policies/PackageEsmType.js.map +1 -0
  60. package/esm/policies/PackageFolderName.d.ts +97 -0
  61. package/esm/policies/PackageFolderName.d.ts.map +1 -0
  62. package/esm/policies/PackageFolderName.js +108 -0
  63. package/esm/policies/PackageFolderName.js.map +1 -0
  64. package/esm/policies/PackageJsonProperties.d.ts.map +1 -1
  65. package/esm/policies/PackageJsonProperties.js +36 -30
  66. package/esm/policies/PackageJsonProperties.js.map +1 -1
  67. package/esm/policies/PackageJsonRepoDirectoryProperty.d.ts.map +1 -1
  68. package/esm/policies/PackageJsonRepoDirectoryProperty.js +41 -37
  69. package/esm/policies/PackageJsonRepoDirectoryProperty.js.map +1 -1
  70. package/esm/policies/PackageJsonSorted.d.ts.map +1 -1
  71. package/esm/policies/PackageJsonSorted.js +40 -28
  72. package/esm/policies/PackageJsonSorted.js.map +1 -1
  73. package/esm/policies/PackageLicense.d.ts +61 -0
  74. package/esm/policies/PackageLicense.d.ts.map +1 -0
  75. package/esm/policies/PackageLicense.js +135 -0
  76. package/esm/policies/PackageLicense.js.map +1 -0
  77. package/esm/policies/PackagePrivateField.d.ts +124 -0
  78. package/esm/policies/PackagePrivateField.d.ts.map +1 -0
  79. package/esm/policies/PackagePrivateField.js +159 -0
  80. package/esm/policies/PackagePrivateField.js.map +1 -0
  81. package/esm/policies/PackageReadme.d.ts +77 -0
  82. package/esm/policies/PackageReadme.d.ts.map +1 -0
  83. package/esm/policies/PackageReadme.js +213 -0
  84. package/esm/policies/PackageReadme.js.map +1 -0
  85. package/esm/policies/PackageScripts.d.ts +180 -2
  86. package/esm/policies/PackageScripts.d.ts.map +1 -1
  87. package/esm/policies/PackageScripts.js +358 -34
  88. package/esm/policies/PackageScripts.js.map +1 -1
  89. package/esm/policies/PackageTestScripts.d.ts +100 -0
  90. package/esm/policies/PackageTestScripts.d.ts.map +1 -0
  91. package/esm/policies/PackageTestScripts.js +155 -0
  92. package/esm/policies/PackageTestScripts.js.map +1 -0
  93. package/esm/policies/RequiredGitignorePatterns.d.ts.map +1 -1
  94. package/esm/policies/RequiredGitignorePatterns.js +30 -19
  95. package/esm/policies/RequiredGitignorePatterns.js.map +1 -1
  96. package/esm/policies.d.ts +9 -1
  97. package/esm/policies.d.ts.map +1 -1
  98. package/esm/policies.js +10 -1
  99. package/esm/policies.js.map +1 -1
  100. package/esm/policy.d.ts +37 -8
  101. package/esm/policy.d.ts.map +1 -1
  102. package/esm/policy.js +18 -9
  103. package/esm/policy.js.map +1 -1
  104. package/esm/policyDefiners/defineFileHeaderPolicy.d.ts +43 -1
  105. package/esm/policyDefiners/defineFileHeaderPolicy.d.ts.map +1 -1
  106. package/esm/policyDefiners/defineFileHeaderPolicy.js +18 -5
  107. package/esm/policyDefiners/defineFileHeaderPolicy.js.map +1 -1
  108. package/esm/policyDefiners/definePackagePolicy.d.ts +43 -2
  109. package/esm/policyDefiners/definePackagePolicy.d.ts.map +1 -1
  110. package/esm/policyDefiners/definePackagePolicy.js +41 -5
  111. package/esm/policyDefiners/definePackagePolicy.js.map +1 -1
  112. package/esm/tsdoc-metadata.json +1 -1
  113. package/esm/utils/indentation.d.ts +73 -0
  114. package/esm/utils/indentation.d.ts.map +1 -0
  115. package/esm/utils/indentation.js +97 -0
  116. package/esm/utils/indentation.js.map +1 -0
  117. package/esm/utils/packageName.d.ts +71 -0
  118. package/esm/utils/packageName.d.ts.map +1 -0
  119. package/esm/utils/packageName.js +79 -0
  120. package/esm/utils/packageName.js.map +1 -0
  121. package/oclif.manifest.json +1 -1
  122. package/package.json +33 -16
@@ -1 +1 @@
1
- {"version":3,"file":"PackageScripts.js","sourceRoot":"","sources":["../../src/policies/PackageScripts.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,mBAAmB,EAAE,MAAM,0CAA0C,CAAC;AA2B/E;;GAEG;AACH,SAAS,uBAAuB,CAC/B,IAAc,EACd,OAA+B;IAE/B,MAAM,cAAc,GAAa,EAAE,CAAC;IACpC,KAAK,MAAM,UAAU,IAAI,IAAI,EAAE,CAAC;QAC/B,IAAI,CAAC,CAAC,OAAO,IAAI,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC,EAAE,CAAC;YACtD,cAAc,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACjC,CAAC;IACF,CAAC;IAED,IAAI,cAAc,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC/B,OAAO,CAAC,gCAAgC,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACxE,CAAC;IAED,OAAO,EAAE,CAAC;AACX,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;;;;GAIG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,mBAAmB,CAG/C,gBAAgB,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE;IACpD,MAAM,UAAU,GAAkB;QACjC,IAAI,EAAE,cAAc,CAAC,IAAI;QACzB,IAAI;QACJ,WAAW,EAAE,KAAK;KAClB,CAAC;IAEF,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QAC1B,OAAO,IAAI,CAAC;IACb,CAAC;IAED,MAAM,eAAe,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IACvD,MAAM,OAAO,GAAG,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;IACpD,MAAM,aAAa,GAAa,EAAE,CAAC;IAEnC,4BAA4B;IAC5B,IAAI,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC3C,aAAa,CAAC,IAAI,CAAC,GAAG,uBAAuB,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;IACtE,CAAC;IAED,4CAA4C;IAC5C,IAAI,MAAM,CAAC,iBAAiB,IAAI,MAAM,CAAC,iBAAiB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACrE,aAAa,CAAC,IAAI,CACjB,GAAG,gCAAgC,CAAC,MAAM,CAAC,iBAAiB,EAAE,OAAO,CAAC,CACtE,CAAC;IACH,CAAC;IAED,IAAI,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC9B,UAAU,CAAC,YAAY,GAAG,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACrD,OAAO,UAAU,CAAC;IACnB,CAAC;IAED,OAAO,IAAI,CAAC;AACb,CAAC,CAAC,CAAC","sourcesContent":["import type { PackageJson } from \"type-fest\";\nimport type { PolicyFailure } from \"../policy.js\";\nimport { definePackagePolicy } from \"../policyDefiners/definePackagePolicy.js\";\n\n/**\n * Policy settings for the PackageScripts repo policy.\n *\n * @alpha\n */\nexport interface PackageScriptsSettings {\n\t/**\n\t * List of script names that must be present in package.json scripts.\n\t */\n\tmust?: 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 * // Allow \"test:unit\" OR \"test:vitest\" OR neither, but not both\n\t * mutuallyExclusive: [[\"test:unit\", \"test:vitest\"]]\n\t */\n\tmutuallyExclusive?: string[][];\n}\n\n/**\n * Validates required scripts and returns error messages for missing scripts.\n */\nfunction validateRequiredScripts(\n\tmust: string[],\n\tscripts: PackageJson[\"scripts\"],\n): string[] {\n\tconst missingScripts: string[] = [];\n\tfor (const scriptName of must) {\n\t\tif (!(scripts && Object.hasOwn(scripts, scriptName))) {\n\t\t\tmissingScripts.push(scriptName);\n\t\t}\n\t}\n\n\tif (missingScripts.length > 0) {\n\t\treturn [`Missing required scripts:\\n\\t${missingScripts.join(\"\\n\\t\")}`];\n\t}\n\n\treturn [];\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 * A RepoPolicy that validates package.json scripts based on configurable rules.\n *\n * @alpha\n */\nexport const PackageScripts = definePackagePolicy<\n\tPackageJson,\n\tPackageScriptsSettings | undefined\n>(\"PackageScripts\", async (json, { file, config }) => {\n\tconst failResult: PolicyFailure = {\n\t\tname: PackageScripts.name,\n\t\tfile,\n\t\tautoFixable: false,\n\t};\n\n\tif (config === undefined) {\n\t\treturn true;\n\t}\n\n\tconst hasScriptsField = Object.hasOwn(json, \"scripts\");\n\tconst scripts = hasScriptsField ? json.scripts : {};\n\tconst errorMessages: string[] = [];\n\n\t// Validate required scripts\n\tif (config.must && config.must.length > 0) {\n\t\terrorMessages.push(...validateRequiredScripts(config.must, scripts));\n\t}\n\n\t// Validate mutually exclusive script groups\n\tif (config.mutuallyExclusive && config.mutuallyExclusive.length > 0) {\n\t\terrorMessages.push(\n\t\t\t...validateMutuallyExclusiveScripts(config.mutuallyExclusive, scripts),\n\t\t);\n\t}\n\n\tif (errorMessages.length > 0) {\n\t\tfailResult.errorMessage = errorMessages.join(\"\\n\\n\");\n\t\treturn failResult;\n\t}\n\n\treturn true;\n});\n"]}
1
+ {"version":3,"file":"PackageScripts.js","sourceRoot":"","sources":["../../src/policies/PackageScripts.ts"],"names":[],"mappings":"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","sourcesContent":["import jsonfile from \"jsonfile\";\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, 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\tfile,\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"]}
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Configuration for the PackageTestScripts policy.
3
+ *
4
+ * This policy ensures that packages have appropriate test scripts when they
5
+ * contain test files or have test-related dependencies.
6
+ *
7
+ * The policy automatically determines what to check based on what's configured:
8
+ * - If `testDirectories` is set, checks for test directories
9
+ * - If `testDependencies` is set, checks for test dependencies
10
+ * - If both are set, a package triggers validation if either condition is met
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * const config: PackageTestScriptsConfig = {
15
+ * // Check for these test directories
16
+ * testDirectories: ["test", "tests", "__tests__"],
17
+ * // Require test script if these devDependencies exist
18
+ * testDependencies: ["vitest", "jest", "mocha"],
19
+ * // Required test script names
20
+ * requiredScripts: ["test"],
21
+ * };
22
+ * ```
23
+ *
24
+ * @alpha
25
+ */
26
+ export interface PackageTestScriptsConfig {
27
+ /**
28
+ * Directory names that indicate test files are present.
29
+ *
30
+ * If any of these directories exist relative to the package.json,
31
+ * the policy will require test scripts to be present.
32
+ *
33
+ * If not set or empty, directory checking is skipped.
34
+ *
35
+ * @example
36
+ * ```typescript
37
+ * testDirectories: ["test", "tests", "__tests__"]
38
+ * ```
39
+ */
40
+ testDirectories?: string[];
41
+ /**
42
+ * Test framework dependencies that indicate tests are expected.
43
+ *
44
+ * If any of these appear in devDependencies, the policy will require
45
+ * test scripts to be present.
46
+ *
47
+ * If not set or empty, dependency checking is skipped.
48
+ *
49
+ * @example
50
+ * ```typescript
51
+ * testDependencies: ["vitest", "jest", "mocha", "@jest/globals"]
52
+ * ```
53
+ */
54
+ testDependencies?: string[];
55
+ /**
56
+ * Script names that must exist when tests are detected.
57
+ *
58
+ * @defaultValue ["test"]
59
+ */
60
+ requiredScripts?: string[];
61
+ /**
62
+ * Package names or scopes to exclude from validation.
63
+ */
64
+ excludePackages?: string[];
65
+ }
66
+ /**
67
+ * A policy that ensures packages have test scripts when test files exist.
68
+ *
69
+ * This policy helps maintain testing discipline by requiring test scripts
70
+ * for packages that have test directories or test framework dependencies.
71
+ *
72
+ * @remarks
73
+ *
74
+ * The policy detects that tests are expected based on:
75
+ *
76
+ * 1. **Test directories**: If `test/`, `tests/`, or `__tests__/` exists
77
+ * 2. **Test dependencies**: If vitest, jest, mocha, etc. are in devDependencies
78
+ *
79
+ * When tests are detected, the policy requires specified test scripts to exist.
80
+ *
81
+ * @example
82
+ * ```typescript
83
+ * import { makePolicy } from "repopo";
84
+ * import { PackageTestScripts } from "repopo/policies";
85
+ *
86
+ * const config: RepopoConfig = {
87
+ * policies: [
88
+ * makePolicy(PackageTestScripts, {
89
+ * testDirectories: ["test", "tests"],
90
+ * testDependencies: ["vitest", "jest"],
91
+ * requiredScripts: ["test"],
92
+ * }),
93
+ * ],
94
+ * };
95
+ * ```
96
+ *
97
+ * @alpha
98
+ */
99
+ export declare const PackageTestScripts: import("../policy.js").PolicyDefinition<PackageTestScriptsConfig | undefined>;
100
+ //# sourceMappingURL=PackageTestScripts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PackageTestScripts.d.ts","sourceRoot":"","sources":["../../src/policies/PackageTestScripts.ts"],"names":[],"mappings":"AAMA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,WAAW,wBAAwB;IACxC;;;;;;;;;;;;OAYG;IACH,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAE3B;;;;;;;;;;;;OAYG;IACH,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAE5B;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAE3B;;OAEG;IACH,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;CAC3B;AA0GD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,eAAO,MAAM,kBAAkB,+EA4D7B,CAAC"}
@@ -0,0 +1,155 @@
1
+ import { existsSync } from "node:fs";
2
+ import { dirname, join } from "pathe";
3
+ import { definePackagePolicy } from "../policyDefiners/definePackagePolicy.js";
4
+ /**
5
+ * Check if a package should be excluded from validation.
6
+ */
7
+ function isExcluded(packageName, excludePackages) {
8
+ if (excludePackages === undefined || excludePackages.length === 0) {
9
+ return false;
10
+ }
11
+ for (const pattern of excludePackages) {
12
+ if (pattern.startsWith("@") && packageName.startsWith(`${pattern}/`)) {
13
+ return true;
14
+ }
15
+ if (packageName === pattern) {
16
+ return true;
17
+ }
18
+ }
19
+ return false;
20
+ }
21
+ /**
22
+ * Check if any test directories exist.
23
+ */
24
+ function hasTestDirectory(packageDir, testDirectories) {
25
+ for (const testDir of testDirectories) {
26
+ const testPath = join(packageDir, testDir);
27
+ if (existsSync(testPath)) {
28
+ return true;
29
+ }
30
+ }
31
+ return false;
32
+ }
33
+ /**
34
+ * Check if any test dependencies are present.
35
+ */
36
+ function hasTestDependency(devDependencies, testDependencies) {
37
+ if (devDependencies === undefined) {
38
+ return false;
39
+ }
40
+ return testDependencies.some((dep) => devDependencies[dep] !== undefined);
41
+ }
42
+ /**
43
+ * Find missing scripts from the required list.
44
+ */
45
+ function findMissingScripts(scripts, requiredScripts) {
46
+ return requiredScripts.filter((scriptName) => scripts?.[scriptName] === undefined);
47
+ }
48
+ /**
49
+ * Get the list of test dependencies that are present in devDependencies.
50
+ */
51
+ function getPresentTestDependencies(devDependencies, testDependencies) {
52
+ if (devDependencies === undefined) {
53
+ return [];
54
+ }
55
+ return testDependencies.filter((d) => devDependencies[d] !== undefined);
56
+ }
57
+ /**
58
+ * Check if a package should be validated based on config and package properties.
59
+ */
60
+ function shouldSkipValidation(json, config) {
61
+ const packageName = json.name;
62
+ // Skip packages without a name or root package
63
+ if (packageName === undefined || packageName === "root") {
64
+ return true;
65
+ }
66
+ // Check exclusions
67
+ if (isExcluded(packageName, config.excludePackages)) {
68
+ return true;
69
+ }
70
+ // Skip if neither directories nor dependencies are configured
71
+ const testDirectories = config.testDirectories ?? [];
72
+ const testDependencies = config.testDependencies ?? [];
73
+ return testDirectories.length === 0 && testDependencies.length === 0;
74
+ }
75
+ /**
76
+ * A policy that ensures packages have test scripts when test files exist.
77
+ *
78
+ * This policy helps maintain testing discipline by requiring test scripts
79
+ * for packages that have test directories or test framework dependencies.
80
+ *
81
+ * @remarks
82
+ *
83
+ * The policy detects that tests are expected based on:
84
+ *
85
+ * 1. **Test directories**: If `test/`, `tests/`, or `__tests__/` exists
86
+ * 2. **Test dependencies**: If vitest, jest, mocha, etc. are in devDependencies
87
+ *
88
+ * When tests are detected, the policy requires specified test scripts to exist.
89
+ *
90
+ * @example
91
+ * ```typescript
92
+ * import { makePolicy } from "repopo";
93
+ * import { PackageTestScripts } from "repopo/policies";
94
+ *
95
+ * const config: RepopoConfig = {
96
+ * policies: [
97
+ * makePolicy(PackageTestScripts, {
98
+ * testDirectories: ["test", "tests"],
99
+ * testDependencies: ["vitest", "jest"],
100
+ * requiredScripts: ["test"],
101
+ * }),
102
+ * ],
103
+ * };
104
+ * ```
105
+ *
106
+ * @alpha
107
+ */
108
+ export const PackageTestScripts = definePackagePolicy({
109
+ name: "PackageTestScripts",
110
+ description: "Ensures packages have test scripts when test directories or test framework dependencies exist.",
111
+ handler: async (json, { file, config }) => {
112
+ // If no config provided, skip validation
113
+ if (config === undefined) {
114
+ return true;
115
+ }
116
+ // Skip validation based on package properties and config
117
+ if (shouldSkipValidation(json, config)) {
118
+ return true;
119
+ }
120
+ // Get config values
121
+ const testDirectories = config.testDirectories ?? [];
122
+ const testDependencies = config.testDependencies ?? [];
123
+ const requiredScripts = config.requiredScripts ?? ["test"];
124
+ const packageDir = dirname(file);
125
+ const devDeps = json.devDependencies;
126
+ // Check if tests are expected based on what's configured
127
+ const hasTestDir = testDirectories.length > 0 &&
128
+ hasTestDirectory(packageDir, testDirectories);
129
+ const hasTestDep = testDependencies.length > 0 &&
130
+ hasTestDependency(devDeps, testDependencies);
131
+ // If no tests expected based on configured checks, pass
132
+ if (!(hasTestDir || hasTestDep)) {
133
+ return true;
134
+ }
135
+ // Tests are expected - check for required scripts
136
+ const missingScripts = findMissingScripts(json.scripts, requiredScripts);
137
+ if (missingScripts.length === 0) {
138
+ return true;
139
+ }
140
+ // Build error message
141
+ const reason = hasTestDir
142
+ ? "test directory exists"
143
+ : `test dependencies found (${getPresentTestDependencies(devDeps, testDependencies).join(", ")})`;
144
+ const failResult = {
145
+ name: PackageTestScripts.name,
146
+ file,
147
+ autoFixable: false,
148
+ errorMessages: [
149
+ `Package "${json.name}" has ${reason} but is missing test scripts: ${missingScripts.join(", ")}`,
150
+ ],
151
+ };
152
+ return failResult;
153
+ },
154
+ });
155
+ //# sourceMappingURL=PackageTestScripts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PackageTestScripts.js","sourceRoot":"","sources":["../../src/policies/PackageTestScripts.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync } from \"node:fs\";\nimport { dirname, join } from \"pathe\";\nimport type { PackageJson } from \"type-fest\";\nimport type { PolicyFailure } from \"../policy.js\";\nimport { definePackagePolicy } from \"../policyDefiners/definePackagePolicy.js\";\n\n/**\n * Configuration for the PackageTestScripts policy.\n *\n * This policy ensures that packages have appropriate test scripts when they\n * contain test files or have test-related dependencies.\n *\n * The policy automatically determines what to check based on what's configured:\n * - If `testDirectories` is set, checks for test directories\n * - If `testDependencies` is set, checks for test dependencies\n * - If both are set, a package triggers validation if either condition is met\n *\n * @example\n * ```typescript\n * const config: PackageTestScriptsConfig = {\n * // Check for these test directories\n * testDirectories: [\"test\", \"tests\", \"__tests__\"],\n * // Require test script if these devDependencies exist\n * testDependencies: [\"vitest\", \"jest\", \"mocha\"],\n * // Required test script names\n * requiredScripts: [\"test\"],\n * };\n * ```\n *\n * @alpha\n */\nexport interface PackageTestScriptsConfig {\n\t/**\n\t * Directory names that indicate test files are present.\n\t *\n\t * If any of these directories exist relative to the package.json,\n\t * the policy will require test scripts to be present.\n\t *\n\t * If not set or empty, directory checking is skipped.\n\t *\n\t * @example\n\t * ```typescript\n\t * testDirectories: [\"test\", \"tests\", \"__tests__\"]\n\t * ```\n\t */\n\ttestDirectories?: string[];\n\n\t/**\n\t * Test framework dependencies that indicate tests are expected.\n\t *\n\t * If any of these appear in devDependencies, the policy will require\n\t * test scripts to be present.\n\t *\n\t * If not set or empty, dependency checking is skipped.\n\t *\n\t * @example\n\t * ```typescript\n\t * testDependencies: [\"vitest\", \"jest\", \"mocha\", \"@jest/globals\"]\n\t * ```\n\t */\n\ttestDependencies?: string[];\n\n\t/**\n\t * Script names that must exist when tests are detected.\n\t *\n\t * @defaultValue [\"test\"]\n\t */\n\trequiredScripts?: string[];\n\n\t/**\n\t * Package names or scopes to exclude from validation.\n\t */\n\texcludePackages?: string[];\n}\n\n/**\n * Check if a package should be excluded from validation.\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\tif (pattern.startsWith(\"@\") && packageName.startsWith(`${pattern}/`)) {\n\t\t\treturn true;\n\t\t}\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 any test directories exist.\n */\nfunction hasTestDirectory(\n\tpackageDir: string,\n\ttestDirectories: string[],\n): boolean {\n\tfor (const testDir of testDirectories) {\n\t\tconst testPath = join(packageDir, testDir);\n\t\tif (existsSync(testPath)) {\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n\n/**\n * Check if any test dependencies are present.\n */\nfunction hasTestDependency(\n\tdevDependencies: Record<string, string> | undefined,\n\ttestDependencies: string[],\n): boolean {\n\tif (devDependencies === undefined) {\n\t\treturn false;\n\t}\n\n\treturn testDependencies.some((dep) => devDependencies[dep] !== undefined);\n}\n\n/**\n * Find missing scripts from the required list.\n */\nfunction findMissingScripts(\n\tscripts: PackageJson[\"scripts\"],\n\trequiredScripts: string[],\n): string[] {\n\treturn requiredScripts.filter(\n\t\t(scriptName) => scripts?.[scriptName] === undefined,\n\t);\n}\n\n/**\n * Get the list of test dependencies that are present in devDependencies.\n */\nfunction getPresentTestDependencies(\n\tdevDependencies: Record<string, string> | undefined,\n\ttestDependencies: string[],\n): string[] {\n\tif (devDependencies === undefined) {\n\t\treturn [];\n\t}\n\treturn testDependencies.filter((d) => devDependencies[d] !== undefined);\n}\n\n/**\n * Check if a package should be validated based on config and package properties.\n */\nfunction shouldSkipValidation(\n\tjson: PackageJson,\n\tconfig: PackageTestScriptsConfig,\n): boolean {\n\tconst packageName = json.name;\n\n\t// Skip packages without a name or root package\n\tif (packageName === undefined || packageName === \"root\") {\n\t\treturn true;\n\t}\n\n\t// Check exclusions\n\tif (isExcluded(packageName, config.excludePackages)) {\n\t\treturn true;\n\t}\n\n\t// Skip if neither directories nor dependencies are configured\n\tconst testDirectories = config.testDirectories ?? [];\n\tconst testDependencies = config.testDependencies ?? [];\n\n\treturn testDirectories.length === 0 && testDependencies.length === 0;\n}\n\n/**\n * A policy that ensures packages have test scripts when test files exist.\n *\n * This policy helps maintain testing discipline by requiring test scripts\n * for packages that have test directories or test framework dependencies.\n *\n * @remarks\n *\n * The policy detects that tests are expected based on:\n *\n * 1. **Test directories**: If `test/`, `tests/`, or `__tests__/` exists\n * 2. **Test dependencies**: If vitest, jest, mocha, etc. are in devDependencies\n *\n * When tests are detected, the policy requires specified test scripts to exist.\n *\n * @example\n * ```typescript\n * import { makePolicy } from \"repopo\";\n * import { PackageTestScripts } from \"repopo/policies\";\n *\n * const config: RepopoConfig = {\n * policies: [\n * makePolicy(PackageTestScripts, {\n * testDirectories: [\"test\", \"tests\"],\n * testDependencies: [\"vitest\", \"jest\"],\n * requiredScripts: [\"test\"],\n * }),\n * ],\n * };\n * ```\n *\n * @alpha\n */\nexport const PackageTestScripts = definePackagePolicy<\n\tPackageJson,\n\tPackageTestScriptsConfig | undefined\n>({\n\tname: \"PackageTestScripts\",\n\tdescription:\n\t\t\"Ensures packages have test scripts when test directories or test framework dependencies exist.\",\n\thandler: async (json, { file, config }) => {\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\t// Skip validation based on package properties and config\n\t\tif (shouldSkipValidation(json, config)) {\n\t\t\treturn true;\n\t\t}\n\n\t\t// Get config values\n\t\tconst testDirectories = config.testDirectories ?? [];\n\t\tconst testDependencies = config.testDependencies ?? [];\n\t\tconst requiredScripts = config.requiredScripts ?? [\"test\"];\n\t\tconst packageDir = dirname(file);\n\t\tconst devDeps = json.devDependencies as Record<string, string> | undefined;\n\n\t\t// Check if tests are expected based on what's configured\n\t\tconst hasTestDir =\n\t\t\ttestDirectories.length > 0 &&\n\t\t\thasTestDirectory(packageDir, testDirectories);\n\t\tconst hasTestDep =\n\t\t\ttestDependencies.length > 0 &&\n\t\t\thasTestDependency(devDeps, testDependencies);\n\n\t\t// If no tests expected based on configured checks, pass\n\t\tif (!(hasTestDir || hasTestDep)) {\n\t\t\treturn true;\n\t\t}\n\n\t\t// Tests are expected - check for required scripts\n\t\tconst missingScripts = findMissingScripts(json.scripts, requiredScripts);\n\n\t\tif (missingScripts.length === 0) {\n\t\t\treturn true;\n\t\t}\n\n\t\t// Build error message\n\t\tconst reason = hasTestDir\n\t\t\t? \"test directory exists\"\n\t\t\t: `test dependencies found (${getPresentTestDependencies(devDeps, testDependencies).join(\", \")})`;\n\n\t\tconst failResult: PolicyFailure = {\n\t\t\tname: PackageTestScripts.name,\n\t\t\tfile,\n\t\t\tautoFixable: false,\n\t\t\terrorMessages: [\n\t\t\t\t`Package \"${json.name}\" has ${reason} but is missing test scripts: ${missingScripts.join(\", \")}`,\n\t\t\t],\n\t\t};\n\t\treturn failResult;\n\t},\n});\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"RequiredGitignorePatterns.d.ts","sourceRoot":"","sources":["../../src/policies/RequiredGitignorePatterns.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACX,gBAAgB,EAGhB,MAAM,cAAc,CAAC;AAEtB;;;;GAIG;AACH,MAAM,WAAW,iCAAiC;IACjD;;;OAGG;IACH,QAAQ,EAAE,KAAK,CAAC,MAAM,GAAG;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAChE;AAmMD;;;;;GAKG;AACH,eAAO,MAAM,yBAAyB,EAAE,gBAAgB,CAAC,iCAAiC,CAcxF,CAAC"}
1
+ {"version":3,"file":"RequiredGitignorePatterns.d.ts","sourceRoot":"","sources":["../../src/policies/RequiredGitignorePatterns.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACX,gBAAgB,EAGhB,MAAM,cAAc,CAAC;AAEtB;;;;GAIG;AACH,MAAM,WAAW,iCAAiC;IACjD;;;OAGG;IACH,QAAQ,EAAE,KAAK,CAAC,MAAM,GAAG;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAChE;AAyMD;;;;;GAKG;AACH,eAAO,MAAM,yBAAyB,EAAE,gBAAgB,CAAC,iCAAiC,CAgBvF,CAAC"}
@@ -1,4 +1,4 @@
1
- import { readFileSync, writeFileSync } from "node:fs";
1
+ import { readFile, writeFile } from "node:fs/promises";
2
2
  import path from "pathe";
3
3
  import { makePolicyDefinition } from "../makePolicy.js";
4
4
  /**
@@ -90,8 +90,8 @@ function createGitignoreContent(patterns) {
90
90
  /**
91
91
  * Handle the case when .gitignore exists.
92
92
  */
93
- function handleExistingGitignore(filePath, patterns, file, resolve) {
94
- const gitignoreContent = readFileSync(filePath, "utf-8");
93
+ async function handleExistingGitignore(filePath, patterns, file, resolve) {
94
+ const gitignoreContent = await readFile(filePath, "utf-8");
95
95
  const lines = gitignoreContent.split("\n");
96
96
  const missingPatterns = findMissingPatterns(patterns, lines);
97
97
  if (missingPatterns.length === 0) {
@@ -101,7 +101,9 @@ function handleExistingGitignore(filePath, patterns, file, resolve) {
101
101
  name: RequiredGitignorePatterns.name,
102
102
  file,
103
103
  autoFixable: true,
104
- errorMessage: `Missing required .gitignore patterns: ${missingPatterns.map((p) => (typeof p === "string" ? p : p.pattern)).join(", ")}`,
104
+ errorMessages: [
105
+ `Missing required .gitignore patterns: ${missingPatterns.map((p) => (typeof p === "string" ? p : p.pattern)).join(", ")}`,
106
+ ],
105
107
  };
106
108
  if (resolve) {
107
109
  const fixResult = {
@@ -110,11 +112,13 @@ function handleExistingGitignore(filePath, patterns, file, resolve) {
110
112
  };
111
113
  try {
112
114
  const newContent = addPatternsToContent(gitignoreContent, missingPatterns);
113
- writeFileSync(filePath, newContent);
115
+ await writeFile(filePath, newContent);
114
116
  fixResult.resolved = true;
115
117
  }
116
118
  catch (error) {
117
- fixResult.errorMessage = `Failed to update .gitignore: ${error.message}`;
119
+ fixResult.errorMessages = [
120
+ `Failed to update .gitignore: ${error.message}`,
121
+ ];
118
122
  }
119
123
  return fixResult;
120
124
  }
@@ -123,12 +127,12 @@ function handleExistingGitignore(filePath, patterns, file, resolve) {
123
127
  /**
124
128
  * Handle the case when .gitignore doesn't exist.
125
129
  */
126
- function handleMissingGitignore(filePath, patterns, file, resolve) {
130
+ async function handleMissingGitignore(filePath, patterns, file, resolve) {
127
131
  const result = {
128
132
  name: RequiredGitignorePatterns.name,
129
133
  file,
130
134
  autoFixable: true,
131
- errorMessage: "No .gitignore file found in repository root",
135
+ errorMessages: ["No .gitignore file found in repository root"],
132
136
  };
133
137
  if (resolve) {
134
138
  const fixResult = {
@@ -137,11 +141,13 @@ function handleMissingGitignore(filePath, patterns, file, resolve) {
137
141
  };
138
142
  try {
139
143
  const content = createGitignoreContent(patterns);
140
- writeFileSync(filePath, content);
144
+ await writeFile(filePath, content);
141
145
  fixResult.resolved = true;
142
146
  }
143
147
  catch (writeError) {
144
- fixResult.errorMessage = `Failed to create .gitignore: ${writeError.message}`;
148
+ fixResult.errorMessages = [
149
+ `Failed to create .gitignore: ${writeError.message}`,
150
+ ];
145
151
  }
146
152
  return fixResult;
147
153
  }
@@ -153,14 +159,19 @@ function handleMissingGitignore(filePath, patterns, file, resolve) {
153
159
  *
154
160
  * @alpha
155
161
  */
156
- export const RequiredGitignorePatterns = makePolicyDefinition("RequiredGitignorePatterns", /^\.gitignore$/, async ({ file, root, resolve, config }) => {
157
- const patterns = config?.patterns ?? DEFAULT_PATTERNS;
158
- const filePath = path.join(root, file);
159
- try {
160
- return handleExistingGitignore(filePath, patterns, file, resolve);
161
- }
162
- catch (_error) {
163
- return handleMissingGitignore(filePath, patterns, file, resolve);
164
- }
162
+ export const RequiredGitignorePatterns = makePolicyDefinition({
163
+ name: "RequiredGitignorePatterns",
164
+ description: "Ensures .gitignore contains required patterns to prevent committing sensitive files, dependencies, and build artifacts.",
165
+ match: /^\.gitignore$/,
166
+ handler: async ({ file, root, resolve, config }) => {
167
+ const patterns = config?.patterns ?? DEFAULT_PATTERNS;
168
+ const filePath = path.join(root, file);
169
+ try {
170
+ return await handleExistingGitignore(filePath, patterns, file, resolve);
171
+ }
172
+ catch (_error) {
173
+ return await handleMissingGitignore(filePath, patterns, file, resolve);
174
+ }
175
+ },
165
176
  });
166
177
  //# sourceMappingURL=RequiredGitignorePatterns.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"RequiredGitignorePatterns.js","sourceRoot":"","sources":["../../src/policies/RequiredGitignorePatterns.ts"],"names":[],"mappings":"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","sourcesContent":["import { readFileSync, writeFileSync } from \"node:fs\";\nimport path from \"pathe\";\nimport { makePolicyDefinition } from \"../makePolicy.js\";\nimport type {\n\tPolicyDefinition,\n\tPolicyFailure,\n\tPolicyFixResult,\n} from \"../policy.js\";\n\n/**\n * Policy settings for the RequiredGitignorePatterns repo policy.\n *\n * @alpha\n */\nexport interface RequiredGitignorePatternsSettings {\n\t/**\n\t * Array of patterns that must be present in .gitignore.\n\t * Each pattern can be a string or an object with pattern and comment.\n\t */\n\tpatterns: Array<string | { pattern: string; comment?: string }>;\n}\n\n/**\n * Default patterns that should typically be in .gitignore files.\n */\nconst DEFAULT_PATTERNS = [\n\t{ pattern: \"node_modules/\", comment: \"Dependencies\" },\n\t{ pattern: \".env\", comment: \"Environment files\" },\n\t{ pattern: \".env.*\", comment: \"Environment files\" },\n\t{ pattern: \"!.env.example\", comment: \"Allow example env file\" },\n\t{ pattern: \"dist/\", comment: \"Build output\" },\n\t{ pattern: \"build/\", comment: \"Build output\" },\n\t{ pattern: \".DS_Store\", comment: \"macOS system files\" },\n\t{ pattern: \"Thumbs.db\", comment: \"Windows system files\" },\n];\n\n/**\n * Check if a pattern exists in gitignore content.\n * Handles patterns with inline comments (e.g., \"node_modules/ # comment\").\n */\nfunction patternExists(lines: string[], pattern: string): boolean {\n\treturn lines.some((line) => {\n\t\tconst trimmed = line.trim();\n\t\t// Remove inline comments (everything after # that's not escaped)\n\t\tconst withoutComment = trimmed.split(\"#\")[0]?.trim() ?? \"\";\n\t\treturn (\n\t\t\twithoutComment === pattern ||\n\t\t\ttrimmed === pattern ||\n\t\t\ttrimmed.startsWith(`${pattern} `) ||\n\t\t\ttrimmed.startsWith(`${pattern}\\t`)\n\t\t);\n\t});\n}\n\n/**\n * Find missing patterns in gitignore content.\n */\nfunction findMissingPatterns(\n\tpatterns: Array<string | { pattern: string; comment?: string }>,\n\tlines: string[],\n): Array<string | { pattern: string; comment?: string }> {\n\tconst missingPatterns: Array<string | { pattern: string; comment?: string }> =\n\t\t[];\n\n\tfor (const patternConfig of patterns) {\n\t\tconst pattern =\n\t\t\ttypeof patternConfig === \"string\" ? patternConfig : patternConfig.pattern;\n\n\t\tif (!patternExists(lines, pattern)) {\n\t\t\tmissingPatterns.push(patternConfig);\n\t\t}\n\t}\n\n\treturn missingPatterns;\n}\n\n/**\n * Add patterns to gitignore content.\n */\nfunction addPatternsToContent(\n\tcontent: string,\n\tmissingPatterns: Array<string | { pattern: string; comment?: string }>,\n): string {\n\tlet newContent = content;\n\n\t// Ensure file ends with newline\n\tif (!newContent.endsWith(\"\\n\")) {\n\t\tnewContent += \"\\n\";\n\t}\n\n\t// Add a section header if we're adding patterns\n\tif (missingPatterns.length > 0) {\n\t\tnewContent += \"\\n# Added by repopo RequiredGitignorePatterns policy\\n\";\n\t}\n\n\t// Add each missing pattern\n\tfor (const patternConfig of missingPatterns) {\n\t\tif (typeof patternConfig === \"string\") {\n\t\t\tnewContent += `${patternConfig}\\n`;\n\t\t} else {\n\t\t\tif (patternConfig.comment) {\n\t\t\t\tnewContent += `# ${patternConfig.comment}\\n`;\n\t\t\t}\n\t\t\tnewContent += `${patternConfig.pattern}\\n`;\n\t\t}\n\t}\n\n\treturn newContent;\n}\n\n/**\n * Create gitignore content from scratch.\n */\nfunction createGitignoreContent(\n\tpatterns: Array<string | { pattern: string; comment?: string }>,\n): string {\n\tlet content = \"# Generated by repopo RequiredGitignorePatterns policy\\n\\n\";\n\n\tfor (const patternConfig of patterns) {\n\t\tif (typeof patternConfig === \"string\") {\n\t\t\tcontent += `${patternConfig}\\n`;\n\t\t} else {\n\t\t\tif (patternConfig.comment) {\n\t\t\t\tcontent += `# ${patternConfig.comment}\\n`;\n\t\t\t}\n\t\t\tcontent += `${patternConfig.pattern}\\n`;\n\t\t}\n\t}\n\n\treturn content;\n}\n\n/**\n * Handle the case when .gitignore exists.\n */\nfunction handleExistingGitignore(\n\tfilePath: string,\n\tpatterns: Array<string | { pattern: string; comment?: string }>,\n\tfile: string,\n\tresolve: boolean,\n) {\n\tconst gitignoreContent = readFileSync(filePath, \"utf-8\");\n\tconst lines = gitignoreContent.split(\"\\n\");\n\tconst missingPatterns = findMissingPatterns(patterns, lines);\n\n\tif (missingPatterns.length === 0) {\n\t\treturn true;\n\t}\n\n\tconst result: PolicyFailure = {\n\t\tname: RequiredGitignorePatterns.name,\n\t\tfile,\n\t\tautoFixable: true,\n\t\terrorMessage: `Missing required .gitignore patterns: ${missingPatterns.map((p) => (typeof p === \"string\" ? p : p.pattern)).join(\", \")}`,\n\t};\n\n\tif (resolve) {\n\t\tconst fixResult: PolicyFixResult = {\n\t\t\t...result,\n\t\t\tresolved: false,\n\t\t};\n\n\t\ttry {\n\t\t\tconst newContent = addPatternsToContent(\n\t\t\t\tgitignoreContent,\n\t\t\t\tmissingPatterns,\n\t\t\t);\n\t\t\twriteFileSync(filePath, newContent);\n\t\t\tfixResult.resolved = true;\n\t\t} catch (error: unknown) {\n\t\t\tfixResult.errorMessage = `Failed to update .gitignore: ${(error as Error).message}`;\n\t\t}\n\n\t\treturn fixResult;\n\t}\n\n\treturn result;\n}\n\n/**\n * Handle the case when .gitignore doesn't exist.\n */\nfunction handleMissingGitignore(\n\tfilePath: string,\n\tpatterns: Array<string | { pattern: string; comment?: string }>,\n\tfile: string,\n\tresolve: boolean,\n) {\n\tconst result: PolicyFailure = {\n\t\tname: RequiredGitignorePatterns.name,\n\t\tfile,\n\t\tautoFixable: true,\n\t\terrorMessage: \"No .gitignore file found in repository root\",\n\t};\n\n\tif (resolve) {\n\t\tconst fixResult: PolicyFixResult = {\n\t\t\t...result,\n\t\t\tresolved: false,\n\t\t};\n\n\t\ttry {\n\t\t\tconst content = createGitignoreContent(patterns);\n\t\t\twriteFileSync(filePath, content);\n\t\t\tfixResult.resolved = true;\n\t\t} catch (writeError: unknown) {\n\t\t\tfixResult.errorMessage = `Failed to create .gitignore: ${(writeError as Error).message}`;\n\t\t}\n\n\t\treturn fixResult;\n\t}\n\n\treturn result;\n}\n\n/**\n * A repo policy that ensures .gitignore contains required patterns to prevent\n * committing sensitive files, dependencies, and build artifacts.\n *\n * @alpha\n */\nexport const RequiredGitignorePatterns: PolicyDefinition<RequiredGitignorePatternsSettings> =\n\tmakePolicyDefinition(\n\t\t\"RequiredGitignorePatterns\",\n\t\t/^\\.gitignore$/,\n\t\tasync ({ file, root, resolve, config }) => {\n\t\t\tconst patterns = config?.patterns ?? DEFAULT_PATTERNS;\n\t\t\tconst filePath = path.join(root, file);\n\n\t\t\ttry {\n\t\t\t\treturn handleExistingGitignore(filePath, patterns, file, resolve);\n\t\t\t} catch (_error: unknown) {\n\t\t\t\treturn handleMissingGitignore(filePath, patterns, file, resolve);\n\t\t\t}\n\t\t},\n\t);\n"]}
1
+ {"version":3,"file":"RequiredGitignorePatterns.js","sourceRoot":"","sources":["../../src/policies/RequiredGitignorePatterns.ts"],"names":[],"mappings":"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","sourcesContent":["import { readFile, writeFile } from \"node:fs/promises\";\nimport path from \"pathe\";\nimport { makePolicyDefinition } from \"../makePolicy.js\";\nimport type {\n\tPolicyDefinition,\n\tPolicyFailure,\n\tPolicyFixResult,\n} from \"../policy.js\";\n\n/**\n * Policy settings for the RequiredGitignorePatterns repo policy.\n *\n * @alpha\n */\nexport interface RequiredGitignorePatternsSettings {\n\t/**\n\t * Array of patterns that must be present in .gitignore.\n\t * Each pattern can be a string or an object with pattern and comment.\n\t */\n\tpatterns: Array<string | { pattern: string; comment?: string }>;\n}\n\n/**\n * Default patterns that should typically be in .gitignore files.\n */\nconst DEFAULT_PATTERNS = [\n\t{ pattern: \"node_modules/\", comment: \"Dependencies\" },\n\t{ pattern: \".env\", comment: \"Environment files\" },\n\t{ pattern: \".env.*\", comment: \"Environment files\" },\n\t{ pattern: \"!.env.example\", comment: \"Allow example env file\" },\n\t{ pattern: \"dist/\", comment: \"Build output\" },\n\t{ pattern: \"build/\", comment: \"Build output\" },\n\t{ pattern: \".DS_Store\", comment: \"macOS system files\" },\n\t{ pattern: \"Thumbs.db\", comment: \"Windows system files\" },\n];\n\n/**\n * Check if a pattern exists in gitignore content.\n * Handles patterns with inline comments (e.g., \"node_modules/ # comment\").\n */\nfunction patternExists(lines: string[], pattern: string): boolean {\n\treturn lines.some((line) => {\n\t\tconst trimmed = line.trim();\n\t\t// Remove inline comments (everything after # that's not escaped)\n\t\tconst withoutComment = trimmed.split(\"#\")[0]?.trim() ?? \"\";\n\t\treturn (\n\t\t\twithoutComment === pattern ||\n\t\t\ttrimmed === pattern ||\n\t\t\ttrimmed.startsWith(`${pattern} `) ||\n\t\t\ttrimmed.startsWith(`${pattern}\\t`)\n\t\t);\n\t});\n}\n\n/**\n * Find missing patterns in gitignore content.\n */\nfunction findMissingPatterns(\n\tpatterns: Array<string | { pattern: string; comment?: string }>,\n\tlines: string[],\n): Array<string | { pattern: string; comment?: string }> {\n\tconst missingPatterns: Array<string | { pattern: string; comment?: string }> =\n\t\t[];\n\n\tfor (const patternConfig of patterns) {\n\t\tconst pattern =\n\t\t\ttypeof patternConfig === \"string\" ? patternConfig : patternConfig.pattern;\n\n\t\tif (!patternExists(lines, pattern)) {\n\t\t\tmissingPatterns.push(patternConfig);\n\t\t}\n\t}\n\n\treturn missingPatterns;\n}\n\n/**\n * Add patterns to gitignore content.\n */\nfunction addPatternsToContent(\n\tcontent: string,\n\tmissingPatterns: Array<string | { pattern: string; comment?: string }>,\n): string {\n\tlet newContent = content;\n\n\t// Ensure file ends with newline\n\tif (!newContent.endsWith(\"\\n\")) {\n\t\tnewContent += \"\\n\";\n\t}\n\n\t// Add a section header if we're adding patterns\n\tif (missingPatterns.length > 0) {\n\t\tnewContent += \"\\n# Added by repopo RequiredGitignorePatterns policy\\n\";\n\t}\n\n\t// Add each missing pattern\n\tfor (const patternConfig of missingPatterns) {\n\t\tif (typeof patternConfig === \"string\") {\n\t\t\tnewContent += `${patternConfig}\\n`;\n\t\t} else {\n\t\t\tif (patternConfig.comment) {\n\t\t\t\tnewContent += `# ${patternConfig.comment}\\n`;\n\t\t\t}\n\t\t\tnewContent += `${patternConfig.pattern}\\n`;\n\t\t}\n\t}\n\n\treturn newContent;\n}\n\n/**\n * Create gitignore content from scratch.\n */\nfunction createGitignoreContent(\n\tpatterns: Array<string | { pattern: string; comment?: string }>,\n): string {\n\tlet content = \"# Generated by repopo RequiredGitignorePatterns policy\\n\\n\";\n\n\tfor (const patternConfig of patterns) {\n\t\tif (typeof patternConfig === \"string\") {\n\t\t\tcontent += `${patternConfig}\\n`;\n\t\t} else {\n\t\t\tif (patternConfig.comment) {\n\t\t\t\tcontent += `# ${patternConfig.comment}\\n`;\n\t\t\t}\n\t\t\tcontent += `${patternConfig.pattern}\\n`;\n\t\t}\n\t}\n\n\treturn content;\n}\n\n/**\n * Handle the case when .gitignore exists.\n */\nasync function handleExistingGitignore(\n\tfilePath: string,\n\tpatterns: Array<string | { pattern: string; comment?: string }>,\n\tfile: string,\n\tresolve: boolean,\n): Promise<true | PolicyFailure | PolicyFixResult> {\n\tconst gitignoreContent = await readFile(filePath, \"utf-8\");\n\tconst lines = gitignoreContent.split(\"\\n\");\n\tconst missingPatterns = findMissingPatterns(patterns, lines);\n\n\tif (missingPatterns.length === 0) {\n\t\treturn true;\n\t}\n\n\tconst result: PolicyFailure = {\n\t\tname: RequiredGitignorePatterns.name,\n\t\tfile,\n\t\tautoFixable: true,\n\t\terrorMessages: [\n\t\t\t`Missing required .gitignore patterns: ${missingPatterns.map((p) => (typeof p === \"string\" ? p : p.pattern)).join(\", \")}`,\n\t\t],\n\t};\n\n\tif (resolve) {\n\t\tconst fixResult: PolicyFixResult = {\n\t\t\t...result,\n\t\t\tresolved: false,\n\t\t};\n\n\t\ttry {\n\t\t\tconst newContent = addPatternsToContent(\n\t\t\t\tgitignoreContent,\n\t\t\t\tmissingPatterns,\n\t\t\t);\n\t\t\tawait writeFile(filePath, newContent);\n\t\t\tfixResult.resolved = true;\n\t\t} catch (error: unknown) {\n\t\t\tfixResult.errorMessages = [\n\t\t\t\t`Failed to update .gitignore: ${(error as Error).message}`,\n\t\t\t];\n\t\t}\n\n\t\treturn fixResult;\n\t}\n\n\treturn result;\n}\n\n/**\n * Handle the case when .gitignore doesn't exist.\n */\nasync function handleMissingGitignore(\n\tfilePath: string,\n\tpatterns: Array<string | { pattern: string; comment?: string }>,\n\tfile: string,\n\tresolve: boolean,\n): Promise<PolicyFailure | PolicyFixResult> {\n\tconst result: PolicyFailure = {\n\t\tname: RequiredGitignorePatterns.name,\n\t\tfile,\n\t\tautoFixable: true,\n\t\terrorMessages: [\"No .gitignore file found in repository root\"],\n\t};\n\n\tif (resolve) {\n\t\tconst fixResult: PolicyFixResult = {\n\t\t\t...result,\n\t\t\tresolved: false,\n\t\t};\n\n\t\ttry {\n\t\t\tconst content = createGitignoreContent(patterns);\n\t\t\tawait writeFile(filePath, content);\n\t\t\tfixResult.resolved = true;\n\t\t} catch (writeError: unknown) {\n\t\t\tfixResult.errorMessages = [\n\t\t\t\t`Failed to create .gitignore: ${(writeError as Error).message}`,\n\t\t\t];\n\t\t}\n\n\t\treturn fixResult;\n\t}\n\n\treturn result;\n}\n\n/**\n * A repo policy that ensures .gitignore contains required patterns to prevent\n * committing sensitive files, dependencies, and build artifacts.\n *\n * @alpha\n */\nexport const RequiredGitignorePatterns: PolicyDefinition<RequiredGitignorePatternsSettings> =\n\tmakePolicyDefinition({\n\t\tname: \"RequiredGitignorePatterns\",\n\t\tdescription:\n\t\t\t\"Ensures .gitignore contains required patterns to prevent committing sensitive files, dependencies, and build artifacts.\",\n\t\tmatch: /^\\.gitignore$/,\n\t\thandler: async ({ file, root, resolve, config }) => {\n\t\t\tconst patterns = config?.patterns ?? DEFAULT_PATTERNS;\n\t\t\tconst filePath = path.join(root, file);\n\n\t\t\ttry {\n\t\t\t\treturn await handleExistingGitignore(filePath, patterns, file, resolve);\n\t\t\t} catch (_error: unknown) {\n\t\t\t\treturn await handleMissingGitignore(filePath, patterns, file, resolve);\n\t\t\t}\n\t\t},\n\t});\n"]}
package/esm/policies.d.ts CHANGED
@@ -3,10 +3,18 @@ export { JsTsFileHeaders } from "./policies/JsTsFileHeaders.js";
3
3
  export { LicenseFileExists } from "./policies/LicenseFileExists.js";
4
4
  export { NoJsFileExtensions } from "./policies/NoJsFileExtensions.js";
5
5
  export { NoLargeBinaryFiles } from "./policies/NoLargeBinaryFiles.js";
6
+ export { NoPrivateWorkspaceDependencies } from "./policies/NoPrivateWorkspaceDependencies.js";
7
+ export { PackageAllowedScopes, type PackageAllowedScopesConfig, } from "./policies/PackageAllowedScopes.js";
8
+ export { PackageEsmType, type PackageEsmTypeConfig, } from "./policies/PackageEsmType.js";
9
+ export { PackageFolderName, type PackageFolderNameConfig, } from "./policies/PackageFolderName.js";
6
10
  export { PackageJsonProperties } from "./policies/PackageJsonProperties.js";
7
11
  export { PackageJsonRepoDirectoryProperty } from "./policies/PackageJsonRepoDirectoryProperty.js";
8
12
  export { PackageJsonSorted } from "./policies/PackageJsonSorted.js";
9
- export { PackageScripts } from "./policies/PackageScripts.js";
13
+ export { PackageLicense, type PackageLicenseSettings, } from "./policies/PackageLicense.js";
14
+ export { PackagePrivateField, type PackagePrivateFieldConfig, } from "./policies/PackagePrivateField.js";
15
+ export { PackageReadme, type PackageReadmeSettings, } from "./policies/PackageReadme.js";
16
+ export { type ConditionalScriptRule, PackageScripts, type PackageScriptsSettings, type RequiredScriptEntry, type ScriptMustContainRule, } from "./policies/PackageScripts.js";
17
+ export { PackageTestScripts, type PackageTestScriptsConfig, } from "./policies/PackageTestScripts.js";
10
18
  export { RequiredGitignorePatterns } from "./policies/RequiredGitignorePatterns.js";
11
19
  export { DefaultPolicies } from "./policy.js";
12
20
  //# sourceMappingURL=policies.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"policies.d.ts","sourceRoot":"","sources":["../src/policies.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAChE,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAChE,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AACpE,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AACtE,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AACtE,OAAO,EAAE,qBAAqB,EAAE,MAAM,qCAAqC,CAAC;AAC5E,OAAO,EAAE,gCAAgC,EAAE,MAAM,gDAAgD,CAAC;AAClG,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AACpE,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,EAAE,yBAAyB,EAAE,MAAM,yCAAyC,CAAC;AACpF,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"policies.d.ts","sourceRoot":"","sources":["../src/policies.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAChE,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAChE,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AACpE,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AACtE,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AACtE,OAAO,EAAE,8BAA8B,EAAE,MAAM,8CAA8C,CAAC;AAC9F,OAAO,EACN,oBAAoB,EACpB,KAAK,0BAA0B,GAC/B,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EACN,cAAc,EACd,KAAK,oBAAoB,GACzB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EACN,iBAAiB,EACjB,KAAK,uBAAuB,GAC5B,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,qBAAqB,EAAE,MAAM,qCAAqC,CAAC;AAC5E,OAAO,EAAE,gCAAgC,EAAE,MAAM,gDAAgD,CAAC;AAClG,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AACpE,OAAO,EACN,cAAc,EACd,KAAK,sBAAsB,GAC3B,MAAM,8BAA8B,CAAC;AACtC,OAAO,EACN,mBAAmB,EACnB,KAAK,yBAAyB,GAC9B,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EACN,aAAa,EACb,KAAK,qBAAqB,GAC1B,MAAM,6BAA6B,CAAC;AACrC,OAAO,EACN,KAAK,qBAAqB,EAC1B,cAAc,EACd,KAAK,sBAAsB,EAC3B,KAAK,mBAAmB,EACxB,KAAK,qBAAqB,GAC1B,MAAM,8BAA8B,CAAC;AACtC,OAAO,EACN,kBAAkB,EAClB,KAAK,wBAAwB,GAC7B,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EAAE,yBAAyB,EAAE,MAAM,yCAAyC,CAAC;AACpF,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC"}
package/esm/policies.js CHANGED
@@ -1,12 +1,21 @@
1
+ // biome-ignore lint/performance/noBarrelFile: intentional public API entry point for policies
1
2
  export { HtmlFileHeaders } from "./policies/HtmlFileHeaders.js";
2
3
  export { JsTsFileHeaders } from "./policies/JsTsFileHeaders.js";
3
4
  export { LicenseFileExists } from "./policies/LicenseFileExists.js";
4
5
  export { NoJsFileExtensions } from "./policies/NoJsFileExtensions.js";
5
6
  export { NoLargeBinaryFiles } from "./policies/NoLargeBinaryFiles.js";
7
+ export { NoPrivateWorkspaceDependencies } from "./policies/NoPrivateWorkspaceDependencies.js";
8
+ export { PackageAllowedScopes, } from "./policies/PackageAllowedScopes.js";
9
+ export { PackageEsmType, } from "./policies/PackageEsmType.js";
10
+ export { PackageFolderName, } from "./policies/PackageFolderName.js";
6
11
  export { PackageJsonProperties } from "./policies/PackageJsonProperties.js";
7
12
  export { PackageJsonRepoDirectoryProperty } from "./policies/PackageJsonRepoDirectoryProperty.js";
8
13
  export { PackageJsonSorted } from "./policies/PackageJsonSorted.js";
9
- export { PackageScripts } from "./policies/PackageScripts.js";
14
+ export { PackageLicense, } from "./policies/PackageLicense.js";
15
+ export { PackagePrivateField, } from "./policies/PackagePrivateField.js";
16
+ export { PackageReadme, } from "./policies/PackageReadme.js";
17
+ export { PackageScripts, } from "./policies/PackageScripts.js";
18
+ export { PackageTestScripts, } from "./policies/PackageTestScripts.js";
10
19
  export { RequiredGitignorePatterns } from "./policies/RequiredGitignorePatterns.js";
11
20
  export { DefaultPolicies } from "./policy.js";
12
21
  //# sourceMappingURL=policies.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"policies.js","sourceRoot":"","sources":["../src/policies.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAChE,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAChE,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AACpE,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AACtE,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AACtE,OAAO,EAAE,qBAAqB,EAAE,MAAM,qCAAqC,CAAC;AAC5E,OAAO,EAAE,gCAAgC,EAAE,MAAM,gDAAgD,CAAC;AAClG,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AACpE,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,EAAE,yBAAyB,EAAE,MAAM,yCAAyC,CAAC;AACpF,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC","sourcesContent":["export { HtmlFileHeaders } from \"./policies/HtmlFileHeaders.js\";\nexport { JsTsFileHeaders } from \"./policies/JsTsFileHeaders.js\";\nexport { LicenseFileExists } from \"./policies/LicenseFileExists.js\";\nexport { NoJsFileExtensions } from \"./policies/NoJsFileExtensions.js\";\nexport { NoLargeBinaryFiles } from \"./policies/NoLargeBinaryFiles.js\";\nexport { PackageJsonProperties } from \"./policies/PackageJsonProperties.js\";\nexport { PackageJsonRepoDirectoryProperty } from \"./policies/PackageJsonRepoDirectoryProperty.js\";\nexport { PackageJsonSorted } from \"./policies/PackageJsonSorted.js\";\nexport { PackageScripts } from \"./policies/PackageScripts.js\";\nexport { RequiredGitignorePatterns } from \"./policies/RequiredGitignorePatterns.js\";\nexport { DefaultPolicies } from \"./policy.js\";\n"]}
1
+ {"version":3,"file":"policies.js","sourceRoot":"","sources":["../src/policies.ts"],"names":[],"mappings":"AAAA,8FAA8F;AAC9F,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAChE,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAChE,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AACpE,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AACtE,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AACtE,OAAO,EAAE,8BAA8B,EAAE,MAAM,8CAA8C,CAAC;AAC9F,OAAO,EACN,oBAAoB,GAEpB,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EACN,cAAc,GAEd,MAAM,8BAA8B,CAAC;AACtC,OAAO,EACN,iBAAiB,GAEjB,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,qBAAqB,EAAE,MAAM,qCAAqC,CAAC;AAC5E,OAAO,EAAE,gCAAgC,EAAE,MAAM,gDAAgD,CAAC;AAClG,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AACpE,OAAO,EACN,cAAc,GAEd,MAAM,8BAA8B,CAAC;AACtC,OAAO,EACN,mBAAmB,GAEnB,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EACN,aAAa,GAEb,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAEN,cAAc,GAId,MAAM,8BAA8B,CAAC;AACtC,OAAO,EACN,kBAAkB,GAElB,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EAAE,yBAAyB,EAAE,MAAM,yCAAyC,CAAC;AACpF,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC","sourcesContent":["// biome-ignore lint/performance/noBarrelFile: intentional public API entry point for policies\nexport { HtmlFileHeaders } from \"./policies/HtmlFileHeaders.js\";\nexport { JsTsFileHeaders } from \"./policies/JsTsFileHeaders.js\";\nexport { LicenseFileExists } from \"./policies/LicenseFileExists.js\";\nexport { NoJsFileExtensions } from \"./policies/NoJsFileExtensions.js\";\nexport { NoLargeBinaryFiles } from \"./policies/NoLargeBinaryFiles.js\";\nexport { NoPrivateWorkspaceDependencies } from \"./policies/NoPrivateWorkspaceDependencies.js\";\nexport {\n\tPackageAllowedScopes,\n\ttype PackageAllowedScopesConfig,\n} from \"./policies/PackageAllowedScopes.js\";\nexport {\n\tPackageEsmType,\n\ttype PackageEsmTypeConfig,\n} from \"./policies/PackageEsmType.js\";\nexport {\n\tPackageFolderName,\n\ttype PackageFolderNameConfig,\n} from \"./policies/PackageFolderName.js\";\nexport { PackageJsonProperties } from \"./policies/PackageJsonProperties.js\";\nexport { PackageJsonRepoDirectoryProperty } from \"./policies/PackageJsonRepoDirectoryProperty.js\";\nexport { PackageJsonSorted } from \"./policies/PackageJsonSorted.js\";\nexport {\n\tPackageLicense,\n\ttype PackageLicenseSettings,\n} from \"./policies/PackageLicense.js\";\nexport {\n\tPackagePrivateField,\n\ttype PackagePrivateFieldConfig,\n} from \"./policies/PackagePrivateField.js\";\nexport {\n\tPackageReadme,\n\ttype PackageReadmeSettings,\n} from \"./policies/PackageReadme.js\";\nexport {\n\ttype ConditionalScriptRule,\n\tPackageScripts,\n\ttype PackageScriptsSettings,\n\ttype RequiredScriptEntry,\n\ttype ScriptMustContainRule,\n} from \"./policies/PackageScripts.js\";\nexport {\n\tPackageTestScripts,\n\ttype PackageTestScriptsConfig,\n} from \"./policies/PackageTestScripts.js\";\nexport { RequiredGitignorePatterns } from \"./policies/RequiredGitignorePatterns.js\";\nexport { DefaultPolicies } from \"./policy.js\";\n"]}