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":"NoLargeBinaryFiles.js","sourceRoot":"","sources":["../../src/policies/NoLargeBinaryFiles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,IAAI,MAAM,OAAO,CAAC;AACzB,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AA2BxD;;GAEG;AACH,MAAM,gBAAgB,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;AAE1C;;GAEG;AACH,MAAM,0BAA0B,GAAG;IAClC,KAAK;IACL,MAAM;IACN,OAAO;IACP,KAAK;IACL,KAAK;IACL,MAAM;IACN,MAAM;IACN,MAAM;IACN,OAAO;IACP,OAAO;IACP,OAAO;IACP,OAAO;IACP,MAAM;IACN,MAAM;IACN,OAAO;IACP,OAAO;IACP,MAAM;IACN,OAAO;IACP,SAAS;CACT,CAAC;AAEF;;;GAGG;AACH,SAAS,WAAW,CAAC,OAAe;IACnC,+CAA+C;IAC/C,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,oBAAoB,EAAE,MAAM,CAAC,CAAC;IAC9D,wCAAwC;IACxC,MAAM,YAAY,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IAClD,OAAO,IAAI,MAAM,CAAC,IAAI,YAAY,GAAG,CAAC,CAAC;AACxC,CAAC;AAED;;GAEG;AACH,SAAS,UAAU,CAClB,QAAgB,EAChB,eAAyB,EACzB,iBAA2B;IAE3B,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,WAAW,EAAE,CAAC;IAEjD,mBAAmB;IACnB,IAAI,iBAAiB,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QACrC,OAAO,IAAI,CAAC;IACb,CAAC;IAED,6CAA6C;IAC7C,KAAK,MAAM,OAAO,IAAI,eAAe,EAAE,CAAC;QACvC,IAAI,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YAC3B,MAAM,KAAK,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;YACnC,IAAI,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAC1B,OAAO,IAAI,CAAC;YACb,CAAC;QACF,CAAC;aAAM,IAAI,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;YACvC,OAAO,IAAI,CAAC;QACb,CAAC;IACF,CAAC;IAED,OAAO,KAAK,CAAC;AACd,CAAC;AAED;;GAEG;AACH,SAAS,WAAW,CAAC,KAAa;IACjC,IAAI,KAAK,KAAK,CAAC,EAAE,CAAC;QACjB,OAAO,KAAK,CAAC;IACd,CAAC;IACD,MAAM,CAAC,GAAG,IAAI,CAAC;IACf,MAAM,KAAK,GAAG,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IAC5C,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CACjB,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EACzC,KAAK,CAAC,MAAM,GAAG,CAAC,CAChB,CAAC;IACF,MAAM,SAAS,GAAG,CAAC,KAAK,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IAC9C,OAAO,GAAG,SAAS,IAAI,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;AACnC,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAC9B,oBAAoB,CACnB,oBAAoB;AACpB,kBAAkB;AAClB,IAAI,EACJ,KAAK,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE;IAChC,MAAM,YAAY,GAAG,MAAM,EAAE,YAAY,IAAI,gBAAgB,CAAC;IAC9D,MAAM,iBAAiB,GACtB,MAAM,EAAE,iBAAiB,IAAI,0BAA0B,CAAC;IACzD,MAAM,eAAe,GAAG,MAAM,EAAE,eAAe,IAAI,EAAE,CAAC;IAEtD,sBAAsB;IACtB,IAAI,UAAU,CAAC,IAAI,EAAE,eAAe,EAAE,iBAAiB,CAAC,EAAE,CAAC;QAC1D,OAAO,IAAI,CAAC;IACb,CAAC;IAED,IAAI,CAAC;QACJ,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAC1C,MAAM,KAAK,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAEjC,mBAAmB;QACnB,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;YACzB,OAAO,IAAI,CAAC;QACb,CAAC;QAED,IAAI,KAAK,CAAC,IAAI,GAAG,YAAY,EAAE,CAAC;YAC/B,MAAM,MAAM,GAAkB;gBAC7B,IAAI,EAAE,kBAAkB,CAAC,IAAI;gBAC7B,IAAI;gBACJ,WAAW,EAAE,KAAK;gBAClB,YAAY,EAAE,sBAAsB,WAAW,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,WAAW,CAAC,YAAY,CAAC,iDAAiD;aAC/I,CAAC;YAEF,OAAO,MAAM,CAAC;QACf,CAAC;QAED,OAAO,IAAI,CAAC;IACb,CAAC;IAAC,OAAO,MAAe,EAAE,CAAC;QAC1B,mDAAmD;QACnD,OAAO,IAAI,CAAC;IACb,CAAC;AACF,CAAC,CACD,CAAC","sourcesContent":["import { statSync } from \"node:fs\";\nimport path from \"pathe\";\nimport { makePolicyDefinition } from \"../makePolicy.js\";\nimport type { PolicyDefinition, PolicyFailure } from \"../policy.js\";\n\n/**\n * Policy settings for the NoLargeBinaryFiles repo policy.\n *\n * @alpha\n */\nexport interface NoLargeBinaryFilesSettings {\n\t/**\n\t * Maximum file size in bytes. Defaults to 10MB.\n\t */\n\tmaxSizeBytes?: number;\n\n\t/**\n\t * File extensions to exclude from size checking.\n\t * Useful for known large files that should be allowed.\n\t */\n\texcludeExtensions?: string[];\n\n\t/**\n\t * File patterns to exclude from size checking.\n\t * Uses glob-style patterns.\n\t */\n\texcludePatterns?: string[];\n}\n\n/**\n * Default maximum file size (10MB in bytes).\n */\nconst DEFAULT_MAX_SIZE = 10 * 1024 * 1024;\n\n/**\n * Default file extensions that are typically safe to exclude from size limits.\n */\nconst DEFAULT_EXCLUDE_EXTENSIONS = [\n\t\".md\",\n\t\".txt\",\n\t\".json\",\n\t\".js\",\n\t\".ts\",\n\t\".jsx\",\n\t\".tsx\",\n\t\".css\",\n\t\".scss\",\n\t\".sass\",\n\t\".less\",\n\t\".html\",\n\t\".xml\",\n\t\".yml\",\n\t\".yaml\",\n\t\".toml\",\n\t\".ini\",\n\t\".conf\",\n\t\".config\",\n];\n\n/**\n * Convert a glob pattern to a regex pattern by escaping special characters\n * and converting * to .* for wildcard matching.\n */\nfunction globToRegex(pattern: string): RegExp {\n\t// Escape all regex special characters except *\n\tconst escaped = pattern.replace(/[.+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n\t// Convert * to .* for wildcard matching\n\tconst regexPattern = escaped.replace(/\\*/g, \".*\");\n\treturn new RegExp(`^${regexPattern}$`);\n}\n\n/**\n * Check if a file matches any of the exclude patterns.\n */\nfunction isExcluded(\n\tfilePath: string,\n\texcludePatterns: string[],\n\texcludeExtensions: string[],\n): boolean {\n\tconst ext = path.extname(filePath).toLowerCase();\n\n\t// Check extensions\n\tif (excludeExtensions.includes(ext)) {\n\t\treturn true;\n\t}\n\n\t// Check patterns (simple glob-like matching)\n\tfor (const pattern of excludePatterns) {\n\t\tif (pattern.includes(\"*\")) {\n\t\t\tconst regex = globToRegex(pattern);\n\t\t\tif (regex.test(filePath)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t} else if (filePath.includes(pattern)) {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n/**\n * Format bytes in a human-readable format.\n */\nfunction formatBytes(bytes: number): string {\n\tif (bytes === 0) {\n\t\treturn \"0 B\";\n\t}\n\tconst k = 1024;\n\tconst sizes = [\"B\", \"KB\", \"MB\", \"GB\", \"TB\"];\n\tconst i = Math.min(\n\t\tMath.floor(Math.log(bytes) / Math.log(k)),\n\t\tsizes.length - 1,\n\t);\n\tconst formatted = (bytes / k ** i).toFixed(1);\n\treturn `${formatted} ${sizes[i]}`;\n}\n\n/**\n * A repo policy that prevents large binary files from being committed to the repository.\n * Large files can bloat the repository and slow down clones. Consider using Git LFS for large assets.\n *\n * @alpha\n */\nexport const NoLargeBinaryFiles: PolicyDefinition<NoLargeBinaryFilesSettings> =\n\tmakePolicyDefinition(\n\t\t\"NoLargeBinaryFiles\",\n\t\t// Match all files\n\t\t/.*/,\n\t\tasync ({ file, root, config }) => {\n\t\t\tconst maxSizeBytes = config?.maxSizeBytes ?? DEFAULT_MAX_SIZE;\n\t\t\tconst excludeExtensions =\n\t\t\t\tconfig?.excludeExtensions ?? DEFAULT_EXCLUDE_EXTENSIONS;\n\t\t\tconst excludePatterns = config?.excludePatterns ?? [];\n\n\t\t\t// Skip excluded files\n\t\t\tif (isExcluded(file, excludePatterns, excludeExtensions)) {\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst filePath = path.resolve(root, file);\n\t\t\t\tconst stats = statSync(filePath);\n\n\t\t\t\t// Skip directories\n\t\t\t\tif (stats.isDirectory()) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\n\t\t\t\tif (stats.size > maxSizeBytes) {\n\t\t\t\t\tconst result: PolicyFailure = {\n\t\t\t\t\t\tname: NoLargeBinaryFiles.name,\n\t\t\t\t\t\tfile,\n\t\t\t\t\t\tautoFixable: false,\n\t\t\t\t\t\terrorMessage: `File is too large: ${formatBytes(stats.size)} (max: ${formatBytes(maxSizeBytes)}). Consider using Git LFS or removing the file.`,\n\t\t\t\t\t};\n\n\t\t\t\t\treturn result;\n\t\t\t\t}\n\n\t\t\t\treturn true;\n\t\t\t} catch (_error: unknown) {\n\t\t\t\t// File doesn't exist or can't be accessed, skip it\n\t\t\t\treturn true;\n\t\t\t}\n\t\t},\n\t);\n"]}
1
+ {"version":3,"file":"NoLargeBinaryFiles.js","sourceRoot":"","sources":["../../src/policies/NoLargeBinaryFiles.ts"],"names":[],"mappings":"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","sourcesContent":["import { statSync } from \"node:fs\";\nimport path from \"pathe\";\nimport { makePolicyDefinition } from \"../makePolicy.js\";\nimport type { PolicyDefinition, PolicyFailure } from \"../policy.js\";\n\n/**\n * Policy settings for the NoLargeBinaryFiles repo policy.\n *\n * @alpha\n */\nexport interface NoLargeBinaryFilesSettings {\n\t/**\n\t * Maximum file size in bytes. Defaults to 10MB.\n\t */\n\tmaxSizeBytes?: number;\n\n\t/**\n\t * File extensions to exclude from size checking.\n\t * Useful for known large files that should be allowed.\n\t */\n\texcludeExtensions?: string[];\n\n\t/**\n\t * File patterns to exclude from size checking.\n\t * Uses glob-style patterns.\n\t */\n\texcludePatterns?: string[];\n}\n\n/**\n * Default maximum file size (10MB in bytes).\n */\nconst DEFAULT_MAX_SIZE = 10 * 1024 * 1024;\n\n/**\n * Default file extensions that are typically safe to exclude from size limits.\n */\nconst DEFAULT_EXCLUDE_EXTENSIONS = [\n\t\".md\",\n\t\".txt\",\n\t\".json\",\n\t\".js\",\n\t\".ts\",\n\t\".jsx\",\n\t\".tsx\",\n\t\".css\",\n\t\".scss\",\n\t\".sass\",\n\t\".less\",\n\t\".html\",\n\t\".xml\",\n\t\".yml\",\n\t\".yaml\",\n\t\".toml\",\n\t\".ini\",\n\t\".conf\",\n\t\".config\",\n];\n\n/**\n * Convert a glob pattern to a regex pattern by escaping special characters\n * and converting * to .* for wildcard matching.\n */\nfunction globToRegex(pattern: string): RegExp {\n\t// Escape all regex special characters except *\n\tconst escaped = pattern.replace(/[.+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n\t// Convert * to .* for wildcard matching\n\tconst regexPattern = escaped.replace(/\\*/g, \".*\");\n\treturn new RegExp(`^${regexPattern}$`);\n}\n\n/**\n * Check if a file matches any of the exclude patterns.\n */\nfunction isExcluded(\n\tfilePath: string,\n\texcludePatterns: string[],\n\texcludeExtensions: string[],\n): boolean {\n\tconst ext = path.extname(filePath).toLowerCase();\n\n\t// Check extensions\n\tif (excludeExtensions.includes(ext)) {\n\t\treturn true;\n\t}\n\n\t// Check patterns (simple glob-like matching)\n\tfor (const pattern of excludePatterns) {\n\t\tif (pattern.includes(\"*\")) {\n\t\t\tconst regex = globToRegex(pattern);\n\t\t\tif (regex.test(filePath)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t} else if (filePath.includes(pattern)) {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n/**\n * Format bytes in a human-readable format.\n */\nfunction formatBytes(bytes: number): string {\n\tif (bytes === 0) {\n\t\treturn \"0 B\";\n\t}\n\tconst k = 1024;\n\tconst sizes = [\"B\", \"KB\", \"MB\", \"GB\", \"TB\"];\n\tconst i = Math.min(\n\t\tMath.floor(Math.log(bytes) / Math.log(k)),\n\t\tsizes.length - 1,\n\t);\n\tconst formatted = (bytes / k ** i).toFixed(1);\n\treturn `${formatted} ${sizes[i]}`;\n}\n\n/**\n * A repo policy that prevents large binary files from being committed to the repository.\n * Large files can bloat the repository and slow down clones. Consider using Git LFS for large assets.\n *\n * @alpha\n */\nexport const NoLargeBinaryFiles: PolicyDefinition<NoLargeBinaryFilesSettings> =\n\tmakePolicyDefinition({\n\t\tname: \"NoLargeBinaryFiles\",\n\t\tdescription:\n\t\t\t\"Prevents large binary files from being committed to avoid repository bloat; consider using Git LFS for large assets.\",\n\t\t// Match all files\n\t\tmatch: /.*/,\n\t\thandler: async ({ file, root, config }) => {\n\t\t\tconst maxSizeBytes = config?.maxSizeBytes ?? DEFAULT_MAX_SIZE;\n\t\t\tconst excludeExtensions =\n\t\t\t\tconfig?.excludeExtensions ?? DEFAULT_EXCLUDE_EXTENSIONS;\n\t\t\tconst excludePatterns = config?.excludePatterns ?? [];\n\n\t\t\t// Skip excluded files\n\t\t\tif (isExcluded(file, excludePatterns, excludeExtensions)) {\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst filePath = path.resolve(root, file);\n\t\t\t\tconst stats = statSync(filePath);\n\n\t\t\t\t// Skip directories\n\t\t\t\tif (stats.isDirectory()) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\n\t\t\t\tif (stats.size > maxSizeBytes) {\n\t\t\t\t\tconst result: PolicyFailure = {\n\t\t\t\t\t\tname: NoLargeBinaryFiles.name,\n\t\t\t\t\t\tfile,\n\t\t\t\t\t\tautoFixable: false,\n\t\t\t\t\t\terrorMessages: [\n\t\t\t\t\t\t\t`File is too large: ${formatBytes(stats.size)} (max: ${formatBytes(maxSizeBytes)}). Consider using Git LFS or removing the file.`,\n\t\t\t\t\t\t],\n\t\t\t\t\t};\n\n\t\t\t\t\treturn result;\n\t\t\t\t}\n\n\t\t\t\treturn true;\n\t\t\t} catch (_error: unknown) {\n\t\t\t\t// File doesn't exist or can't be accessed, skip it\n\t\t\t\treturn true;\n\t\t\t}\n\t\t},\n\t});\n"]}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Configuration for the NoPrivateWorkspaceDependencies policy.
3
+ */
4
+ export interface NoPrivateWorkspaceDependenciesSettings {
5
+ /**
6
+ * Whether to check devDependencies in addition to dependencies.
7
+ * @default false
8
+ */
9
+ checkDevDependencies?: boolean;
10
+ }
11
+ /**
12
+ * Policy that prevents publishable packages from depending on private workspace packages.
13
+ *
14
+ * A package is considered "publishable" if it does not have `"private": true` in its package.json.
15
+ * Such packages cannot have `workspace:` dependencies on packages that are marked as private,
16
+ * because those dependencies won't be resolvable when the package is published to npm.
17
+ *
18
+ * This policy uses the workspace configuration (pnpm-workspace.yaml or package.json workspaces field)
19
+ * to locate workspace packages, rather than hardcoding directory conventions.
20
+ *
21
+ * @example
22
+ * ```typescript
23
+ * // In repopo.config.ts
24
+ * import { NoPrivateWorkspaceDependencies } from "repopo/policies";
25
+ *
26
+ * makePolicy(NoPrivateWorkspaceDependencies, {
27
+ * checkDevDependencies: false, // Only check production dependencies (default)
28
+ * });
29
+ *
30
+ * // Or check both dependencies and devDependencies
31
+ * makePolicy(NoPrivateWorkspaceDependencies, {
32
+ * checkDevDependencies: true,
33
+ * });
34
+ * ```
35
+ */
36
+ export declare const NoPrivateWorkspaceDependencies: import("../policy.js").PolicyDefinition<NoPrivateWorkspaceDependenciesSettings | undefined>;
37
+ //# sourceMappingURL=NoPrivateWorkspaceDependencies.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NoPrivateWorkspaceDependencies.d.ts","sourceRoot":"","sources":["../../src/policies/NoPrivateWorkspaceDependencies.ts"],"names":[],"mappings":"AAYA;;GAEG;AACH,MAAM,WAAW,sCAAsC;IACtD;;;OAGG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAC/B;AA6ED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,eAAO,MAAM,8BAA8B,6FAyEzC,CAAC"}
@@ -0,0 +1,145 @@
1
+ import { readFileSync } from "node:fs";
2
+ // Use default import + destructure for CJS/ESM interop
3
+ // (resolve-workspace-root is a bundled CJS module)
4
+ import resolveWorkspaceRoot from "resolve-workspace-root";
5
+ const { getWorkspaceGlobs } = resolveWorkspaceRoot;
6
+ import { globSync } from "tinyglobby";
7
+ import { definePackagePolicy } from "../policyDefiners/definePackagePolicy.js";
8
+ /**
9
+ * Cache for workspace package lookups to avoid repeated filesystem operations.
10
+ */
11
+ const workspacePackageCache = new Map();
12
+ /**
13
+ * Builds a map of package name to package.json for all packages in the workspace.
14
+ * Uses the workspace configuration (pnpm-workspace.yaml or package.json workspaces) to find packages.
15
+ */
16
+ function buildWorkspacePackageMap(root) {
17
+ // Check cache first
18
+ const cached = workspacePackageCache.get(root);
19
+ if (cached !== undefined) {
20
+ return cached;
21
+ }
22
+ const packageMap = new Map();
23
+ // Get workspace globs from pnpm-workspace.yaml or package.json
24
+ const workspaceGlobs = getWorkspaceGlobs(root);
25
+ if (workspaceGlobs === null) {
26
+ // Not a workspace - return empty map
27
+ workspacePackageCache.set(root, packageMap);
28
+ return packageMap;
29
+ }
30
+ // Find all package.json files matching the workspace globs
31
+ const packageGlobs = workspaceGlobs.map((g) => `${g}/package.json`);
32
+ const packageJsonPaths = globSync(packageGlobs, {
33
+ cwd: root,
34
+ ignore: ["**/node_modules/**"],
35
+ onlyFiles: true,
36
+ absolute: true,
37
+ });
38
+ // Load each package.json and map by name
39
+ for (const pkgPath of packageJsonPaths) {
40
+ try {
41
+ const content = readFileSync(pkgPath, "utf-8");
42
+ const pkg = JSON.parse(content);
43
+ if (pkg.name) {
44
+ packageMap.set(pkg.name, pkg);
45
+ }
46
+ }
47
+ catch {
48
+ // Ignore parse errors
49
+ }
50
+ }
51
+ workspacePackageCache.set(root, packageMap);
52
+ return packageMap;
53
+ }
54
+ /**
55
+ * Looks up a workspace package by name using the workspace configuration.
56
+ */
57
+ function findWorkspacePackage(depName, root) {
58
+ const packageMap = buildWorkspacePackageMap(root);
59
+ return packageMap.get(depName);
60
+ }
61
+ /**
62
+ * Checks if a dependency version uses the workspace protocol.
63
+ */
64
+ function isWorkspaceDependency(version) {
65
+ return version?.startsWith("workspace:") ?? false;
66
+ }
67
+ /**
68
+ * Policy that prevents publishable packages from depending on private workspace packages.
69
+ *
70
+ * A package is considered "publishable" if it does not have `"private": true` in its package.json.
71
+ * Such packages cannot have `workspace:` dependencies on packages that are marked as private,
72
+ * because those dependencies won't be resolvable when the package is published to npm.
73
+ *
74
+ * This policy uses the workspace configuration (pnpm-workspace.yaml or package.json workspaces field)
75
+ * to locate workspace packages, rather than hardcoding directory conventions.
76
+ *
77
+ * @example
78
+ * ```typescript
79
+ * // In repopo.config.ts
80
+ * import { NoPrivateWorkspaceDependencies } from "repopo/policies";
81
+ *
82
+ * makePolicy(NoPrivateWorkspaceDependencies, {
83
+ * checkDevDependencies: false, // Only check production dependencies (default)
84
+ * });
85
+ *
86
+ * // Or check both dependencies and devDependencies
87
+ * makePolicy(NoPrivateWorkspaceDependencies, {
88
+ * checkDevDependencies: true,
89
+ * });
90
+ * ```
91
+ */
92
+ export const NoPrivateWorkspaceDependencies = definePackagePolicy({
93
+ name: "NoPrivateWorkspaceDependencies",
94
+ description: "Prevents publishable packages from depending on private workspace packages that won't be available on npm.",
95
+ // biome-ignore lint/correctness/useYield: no yield needed
96
+ handler: function* (json, { file, root, config }) {
97
+ // Private packages can depend on anything - they won't be published
98
+ if (json.private === true) {
99
+ return true;
100
+ }
101
+ const checkDevDeps = config?.checkDevDependencies ?? false;
102
+ // Collect dependencies to check
103
+ const depsToCheck = {
104
+ ...(json.dependencies ?? {}),
105
+ };
106
+ if (checkDevDeps) {
107
+ Object.assign(depsToCheck, json.devDependencies ?? {});
108
+ }
109
+ const privateWorkspaceDeps = [];
110
+ const unresolvableDeps = [];
111
+ for (const [depName, version] of Object.entries(depsToCheck)) {
112
+ if (!isWorkspaceDependency(version)) {
113
+ continue;
114
+ }
115
+ const depPackageJson = findWorkspacePackage(depName, root);
116
+ if (depPackageJson === undefined) {
117
+ // Could not find the package - might be a configuration issue
118
+ // We'll warn but not fail, as this could be a valid setup we don't understand
119
+ unresolvableDeps.push(depName);
120
+ continue;
121
+ }
122
+ if (depPackageJson.private === true) {
123
+ privateWorkspaceDeps.push(depName);
124
+ }
125
+ }
126
+ const errorMessages = [];
127
+ if (privateWorkspaceDeps.length > 0) {
128
+ errorMessages.push(`Publishable package has workspace dependencies on private packages that won't be available on npm:\n\t${privateWorkspaceDeps.join("\n\t")}`);
129
+ }
130
+ if (unresolvableDeps.length > 0) {
131
+ errorMessages.push(`Could not resolve workspace dependencies (verify they exist):\n\t${unresolvableDeps.join("\n\t")}`);
132
+ }
133
+ if (errorMessages.length > 0) {
134
+ const failResult = {
135
+ name: NoPrivateWorkspaceDependencies.name,
136
+ file,
137
+ autoFixable: false,
138
+ errorMessages,
139
+ };
140
+ return failResult;
141
+ }
142
+ return true;
143
+ },
144
+ });
145
+ //# sourceMappingURL=NoPrivateWorkspaceDependencies.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NoPrivateWorkspaceDependencies.js","sourceRoot":"","sources":["../../src/policies/NoPrivateWorkspaceDependencies.ts"],"names":[],"mappings":"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","sourcesContent":["import { readFileSync } from \"node:fs\";\n// Use default import + destructure for CJS/ESM interop\n// (resolve-workspace-root is a bundled CJS module)\nimport resolveWorkspaceRoot from \"resolve-workspace-root\";\n\nconst { getWorkspaceGlobs } = resolveWorkspaceRoot;\n\nimport { globSync } from \"tinyglobby\";\nimport type { PackageJson } from \"type-fest\";\nimport type { PolicyFailure } from \"../policy.js\";\nimport { definePackagePolicy } from \"../policyDefiners/definePackagePolicy.js\";\n\n/**\n * Configuration for the NoPrivateWorkspaceDependencies policy.\n */\nexport interface NoPrivateWorkspaceDependenciesSettings {\n\t/**\n\t * Whether to check devDependencies in addition to dependencies.\n\t * @default false\n\t */\n\tcheckDevDependencies?: boolean;\n}\n\n/**\n * Cache for workspace package lookups to avoid repeated filesystem operations.\n */\nconst workspacePackageCache = new Map<\n\tstring,\n\tMap<string, PackageJson | undefined>\n>();\n\n/**\n * Builds a map of package name to package.json for all packages in the workspace.\n * Uses the workspace configuration (pnpm-workspace.yaml or package.json workspaces) to find packages.\n */\nfunction buildWorkspacePackageMap(\n\troot: string,\n): Map<string, PackageJson | undefined> {\n\t// Check cache first\n\tconst cached = workspacePackageCache.get(root);\n\tif (cached !== undefined) {\n\t\treturn cached;\n\t}\n\n\tconst packageMap = new Map<string, PackageJson | undefined>();\n\n\t// Get workspace globs from pnpm-workspace.yaml or package.json\n\tconst workspaceGlobs = getWorkspaceGlobs(root);\n\tif (workspaceGlobs === null) {\n\t\t// Not a workspace - return empty map\n\t\tworkspacePackageCache.set(root, packageMap);\n\t\treturn packageMap;\n\t}\n\n\t// Find all package.json files matching the workspace globs\n\tconst packageGlobs = workspaceGlobs.map((g) => `${g}/package.json`);\n\tconst packageJsonPaths = globSync(packageGlobs, {\n\t\tcwd: root,\n\t\tignore: [\"**/node_modules/**\"],\n\t\tonlyFiles: true,\n\t\tabsolute: true,\n\t});\n\n\t// Load each package.json and map by name\n\tfor (const pkgPath of packageJsonPaths) {\n\t\ttry {\n\t\t\tconst content = readFileSync(pkgPath, \"utf-8\");\n\t\t\tconst pkg = JSON.parse(content) as PackageJson;\n\t\t\tif (pkg.name) {\n\t\t\t\tpackageMap.set(pkg.name, pkg);\n\t\t\t}\n\t\t} catch {\n\t\t\t// Ignore parse errors\n\t\t}\n\t}\n\n\tworkspacePackageCache.set(root, packageMap);\n\treturn packageMap;\n}\n\n/**\n * Looks up a workspace package by name using the workspace configuration.\n */\nfunction findWorkspacePackage(\n\tdepName: string,\n\troot: string,\n): PackageJson | undefined {\n\tconst packageMap = buildWorkspacePackageMap(root);\n\treturn packageMap.get(depName);\n}\n\n/**\n * Checks if a dependency version uses the workspace protocol.\n */\nfunction isWorkspaceDependency(version: string | undefined): boolean {\n\treturn version?.startsWith(\"workspace:\") ?? false;\n}\n\n/**\n * Policy that prevents publishable packages from depending on private workspace packages.\n *\n * A package is considered \"publishable\" if it does not have `\"private\": true` in its package.json.\n * Such packages cannot have `workspace:` dependencies on packages that are marked as private,\n * because those dependencies won't be resolvable when the package is published to npm.\n *\n * This policy uses the workspace configuration (pnpm-workspace.yaml or package.json workspaces field)\n * to locate workspace packages, rather than hardcoding directory conventions.\n *\n * @example\n * ```typescript\n * // In repopo.config.ts\n * import { NoPrivateWorkspaceDependencies } from \"repopo/policies\";\n *\n * makePolicy(NoPrivateWorkspaceDependencies, {\n * checkDevDependencies: false, // Only check production dependencies (default)\n * });\n *\n * // Or check both dependencies and devDependencies\n * makePolicy(NoPrivateWorkspaceDependencies, {\n * checkDevDependencies: true,\n * });\n * ```\n */\nexport const NoPrivateWorkspaceDependencies = definePackagePolicy<\n\tPackageJson,\n\tNoPrivateWorkspaceDependenciesSettings | undefined\n>({\n\tname: \"NoPrivateWorkspaceDependencies\",\n\tdescription:\n\t\t\"Prevents publishable packages from depending on private workspace packages that won't be available on npm.\",\n\t// biome-ignore lint/correctness/useYield: no yield needed\n\thandler: function* (json, { file, root, config }) {\n\t\t// Private packages can depend on anything - they won't be published\n\t\tif (json.private === true) {\n\t\t\treturn true;\n\t\t}\n\n\t\tconst checkDevDeps = config?.checkDevDependencies ?? false;\n\n\t\t// Collect dependencies to check\n\t\tconst depsToCheck: Record<string, string | undefined> = {\n\t\t\t...(json.dependencies ?? {}),\n\t\t};\n\n\t\tif (checkDevDeps) {\n\t\t\tObject.assign(depsToCheck, json.devDependencies ?? {});\n\t\t}\n\n\t\tconst privateWorkspaceDeps: string[] = [];\n\t\tconst unresolvableDeps: string[] = [];\n\n\t\tfor (const [depName, version] of Object.entries(depsToCheck)) {\n\t\t\tif (!isWorkspaceDependency(version)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst depPackageJson = findWorkspacePackage(depName, root);\n\n\t\t\tif (depPackageJson === undefined) {\n\t\t\t\t// Could not find the package - might be a configuration issue\n\t\t\t\t// We'll warn but not fail, as this could be a valid setup we don't understand\n\t\t\t\tunresolvableDeps.push(depName);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (depPackageJson.private === true) {\n\t\t\t\tprivateWorkspaceDeps.push(depName);\n\t\t\t}\n\t\t}\n\n\t\tconst errorMessages: string[] = [];\n\n\t\tif (privateWorkspaceDeps.length > 0) {\n\t\t\terrorMessages.push(\n\t\t\t\t`Publishable package has workspace dependencies on private packages that won't be available on npm:\\n\\t${privateWorkspaceDeps.join(\"\\n\\t\")}`,\n\t\t\t);\n\t\t}\n\n\t\tif (unresolvableDeps.length > 0) {\n\t\t\terrorMessages.push(\n\t\t\t\t`Could not resolve workspace dependencies (verify they exist):\\n\\t${unresolvableDeps.join(\"\\n\\t\")}`,\n\t\t\t);\n\t\t}\n\n\t\tif (errorMessages.length > 0) {\n\t\t\tconst failResult: PolicyFailure = {\n\t\t\t\tname: NoPrivateWorkspaceDependencies.name,\n\t\t\t\tfile,\n\t\t\t\tautoFixable: false,\n\t\t\t\terrorMessages,\n\t\t\t};\n\t\t\treturn failResult;\n\t\t}\n\n\t\treturn true;\n\t},\n});\n"]}
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Configuration for the PackageAllowedScopes policy.
3
+ *
4
+ * This policy ensures packages in a monorepo use only approved scopes or package names.
5
+ * This prevents accidental introduction of new scopes and ensures naming consistency.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * const config: PackageAllowedScopesConfig = {
10
+ * // Only packages in these scopes are allowed
11
+ * allowedScopes: ["@myorg", "@internal", "@experimental"],
12
+ * // These specific unscoped packages are also allowed
13
+ * unscopedPackages: ["my-legacy-tool", "special-package"],
14
+ * };
15
+ * ```
16
+ *
17
+ * @alpha
18
+ */
19
+ export interface PackageAllowedScopesConfig {
20
+ /**
21
+ * A list of npm scopes that are allowed for packages in this repository.
22
+ *
23
+ * Scopes should start with `@` (e.g., `@myorg`). A package like `@myorg/foo`
24
+ * would match the scope `@myorg`.
25
+ *
26
+ * @remarks
27
+ *
28
+ * If a package's scope is not in this list and the package is not in
29
+ * `unscopedPackages`, the policy will fail.
30
+ *
31
+ * @example
32
+ * ```typescript
33
+ * allowedScopes: ["@myorg", "@myorg-tools", "@myorg-internal"]
34
+ * ```
35
+ */
36
+ allowedScopes?: string[];
37
+ /**
38
+ * A list of unscoped package names that are explicitly allowed.
39
+ *
40
+ * Use this for legacy packages or special cases where an unscoped name is needed.
41
+ *
42
+ * @remarks
43
+ *
44
+ * Unscoped packages not in this list will cause the policy to fail.
45
+ *
46
+ * @example
47
+ * ```typescript
48
+ * unscopedPackages: ["my-legacy-tool", "special-package"]
49
+ * ```
50
+ */
51
+ unscopedPackages?: string[];
52
+ }
53
+ /**
54
+ * A policy that ensures packages use only allowed scopes and package names.
55
+ *
56
+ * This policy prevents accidental introduction of new npm scopes or unscoped packages
57
+ * in a monorepo. It helps maintain naming consistency and prevents typos or mistakes
58
+ * when creating new packages.
59
+ *
60
+ * @remarks
61
+ *
62
+ * The policy checks packages in the following order:
63
+ *
64
+ * 1. If the package is scoped (starts with `@`), check if its scope is in `allowedScopes`
65
+ * 2. If the package is unscoped, check if it's in `unscopedPackages`
66
+ * 3. If neither check passes, the policy fails
67
+ *
68
+ * The "root" package (common in monorepos) is automatically skipped.
69
+ *
70
+ * @example
71
+ * ```typescript
72
+ * import { makePolicy } from "repopo";
73
+ * import { PackageAllowedScopes } from "repopo/policies";
74
+ *
75
+ * const config: RepopoConfig = {
76
+ * policies: [
77
+ * makePolicy(PackageAllowedScopes, {
78
+ * allowedScopes: ["@myorg", "@internal"],
79
+ * unscopedPackages: ["legacy-package"],
80
+ * }),
81
+ * ],
82
+ * };
83
+ * ```
84
+ *
85
+ * @alpha
86
+ */
87
+ export declare const PackageAllowedScopes: import("../policy.js").PolicyDefinition<PackageAllowedScopesConfig | undefined>;
88
+ //# sourceMappingURL=PackageAllowedScopes.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,118 @@
1
+ import { definePackagePolicy } from "../policyDefiners/definePackagePolicy.js";
2
+ import { getPackageScope, isScoped } from "../utils/packageName.js";
3
+ /**
4
+ * Check if a package's scope is in the allowed list.
5
+ *
6
+ * @param packageName - The package name to check
7
+ * @param allowedScopes - List of allowed scopes
8
+ * @returns `true` if the package's scope is allowed
9
+ */
10
+ function isScopeAllowed(packageName, allowedScopes) {
11
+ if (allowedScopes === undefined || allowedScopes.length === 0) {
12
+ return false;
13
+ }
14
+ const scope = getPackageScope(packageName);
15
+ if (scope === undefined) {
16
+ // Package is unscoped, so scope check doesn't apply
17
+ return false;
18
+ }
19
+ return allowedScopes.includes(scope);
20
+ }
21
+ /**
22
+ * Check if an unscoped package name is in the allowed list.
23
+ *
24
+ * @param packageName - The package name to check
25
+ * @param unscopedPackages - List of allowed unscoped package names
26
+ * @returns `true` if the unscoped package is allowed
27
+ */
28
+ function isUnscopedPackageAllowed(packageName, unscopedPackages) {
29
+ if (unscopedPackages === undefined || unscopedPackages.length === 0) {
30
+ return false;
31
+ }
32
+ // Only applies to unscoped packages
33
+ if (isScoped(packageName)) {
34
+ return false;
35
+ }
36
+ return unscopedPackages.includes(packageName);
37
+ }
38
+ /**
39
+ * A policy that ensures packages use only allowed scopes and package names.
40
+ *
41
+ * This policy prevents accidental introduction of new npm scopes or unscoped packages
42
+ * in a monorepo. It helps maintain naming consistency and prevents typos or mistakes
43
+ * when creating new packages.
44
+ *
45
+ * @remarks
46
+ *
47
+ * The policy checks packages in the following order:
48
+ *
49
+ * 1. If the package is scoped (starts with `@`), check if its scope is in `allowedScopes`
50
+ * 2. If the package is unscoped, check if it's in `unscopedPackages`
51
+ * 3. If neither check passes, the policy fails
52
+ *
53
+ * The "root" package (common in monorepos) is automatically skipped.
54
+ *
55
+ * @example
56
+ * ```typescript
57
+ * import { makePolicy } from "repopo";
58
+ * import { PackageAllowedScopes } from "repopo/policies";
59
+ *
60
+ * const config: RepopoConfig = {
61
+ * policies: [
62
+ * makePolicy(PackageAllowedScopes, {
63
+ * allowedScopes: ["@myorg", "@internal"],
64
+ * unscopedPackages: ["legacy-package"],
65
+ * }),
66
+ * ],
67
+ * };
68
+ * ```
69
+ *
70
+ * @alpha
71
+ */
72
+ export const PackageAllowedScopes = definePackagePolicy({
73
+ name: "PackageAllowedScopes",
74
+ description: "Ensures packages use only allowed npm scopes and package names.",
75
+ // biome-ignore lint/correctness/useYield: no yield needed
76
+ handler: function* (json, { file, config }) {
77
+ // If no config provided, skip validation
78
+ if (config === undefined) {
79
+ return true;
80
+ }
81
+ const packageName = json.name;
82
+ // Skip packages without a name
83
+ if (packageName === undefined) {
84
+ return true;
85
+ }
86
+ // Skip the root package (common in monorepos)
87
+ if (packageName === "root") {
88
+ return true;
89
+ }
90
+ // Check if the package uses an allowed scope
91
+ if (isScopeAllowed(packageName, config.allowedScopes)) {
92
+ return true;
93
+ }
94
+ // Check if the package is an allowed unscoped package
95
+ if (isUnscopedPackageAllowed(packageName, config.unscopedPackages)) {
96
+ return true;
97
+ }
98
+ // Package doesn't match any allowed pattern - build error message
99
+ const scope = getPackageScope(packageName);
100
+ let errorMessage;
101
+ if (scope !== undefined) {
102
+ // Package has an unexpected scope
103
+ errorMessage = `Package "${packageName}" uses scope "${scope}" which is not in the allowed scopes list. Allowed scopes: ${config.allowedScopes?.join(", ") ?? "(none)"}`;
104
+ }
105
+ else {
106
+ // Package is unscoped but not in the allowed list
107
+ errorMessage = `Package "${packageName}" is an unscoped package that is not in the allowed unscoped packages list. Allowed unscoped packages: ${config.unscopedPackages?.join(", ") ?? "(none)"}`;
108
+ }
109
+ const failResult = {
110
+ name: PackageAllowedScopes.name,
111
+ file,
112
+ autoFixable: false, // Can't auto-fix package names
113
+ errorMessages: [errorMessage],
114
+ };
115
+ return failResult;
116
+ },
117
+ });
118
+ //# sourceMappingURL=PackageAllowedScopes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PackageAllowedScopes.js","sourceRoot":"","sources":["../../src/policies/PackageAllowedScopes.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,mBAAmB,EAAE,MAAM,0CAA0C,CAAC;AAC/E,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AAwDpE;;;;;;GAMG;AACH,SAAS,cAAc,CACtB,WAAmB,EACnB,aAAmC;IAEnC,IAAI,aAAa,KAAK,SAAS,IAAI,aAAa,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC/D,OAAO,KAAK,CAAC;IACd,CAAC;IAED,MAAM,KAAK,GAAG,eAAe,CAAC,WAAW,CAAC,CAAC;IAC3C,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACzB,oDAAoD;QACpD,OAAO,KAAK,CAAC;IACd,CAAC;IAED,OAAO,aAAa,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AACtC,CAAC;AAED;;;;;;GAMG;AACH,SAAS,wBAAwB,CAChC,WAAmB,EACnB,gBAAsC;IAEtC,IAAI,gBAAgB,KAAK,SAAS,IAAI,gBAAgB,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACrE,OAAO,KAAK,CAAC;IACd,CAAC;IAED,oCAAoC;IACpC,IAAI,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;QAC3B,OAAO,KAAK,CAAC;IACd,CAAC;IAED,OAAO,gBAAgB,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;AAC/C,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,mBAAmB,CAGrD;IACD,IAAI,EAAE,sBAAsB;IAC5B,WAAW,EACV,iEAAiE;IAClE,0DAA0D;IAC1D,OAAO,EAAE,QAAQ,CAAC,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE;QACzC,yCAAyC;QACzC,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YAC1B,OAAO,IAAI,CAAC;QACb,CAAC;QAED,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC;QAE9B,+BAA+B;QAC/B,IAAI,WAAW,KAAK,SAAS,EAAE,CAAC;YAC/B,OAAO,IAAI,CAAC;QACb,CAAC;QAED,8CAA8C;QAC9C,IAAI,WAAW,KAAK,MAAM,EAAE,CAAC;YAC5B,OAAO,IAAI,CAAC;QACb,CAAC;QAED,6CAA6C;QAC7C,IAAI,cAAc,CAAC,WAAW,EAAE,MAAM,CAAC,aAAa,CAAC,EAAE,CAAC;YACvD,OAAO,IAAI,CAAC;QACb,CAAC;QAED,sDAAsD;QACtD,IAAI,wBAAwB,CAAC,WAAW,EAAE,MAAM,CAAC,gBAAgB,CAAC,EAAE,CAAC;YACpE,OAAO,IAAI,CAAC;QACb,CAAC;QAED,kEAAkE;QAClE,MAAM,KAAK,GAAG,eAAe,CAAC,WAAW,CAAC,CAAC;QAC3C,IAAI,YAAoB,CAAC;QACzB,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACzB,kCAAkC;YAClC,YAAY,GAAG,YAAY,WAAW,iBAAiB,KAAK,8DAA8D,MAAM,CAAC,aAAa,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,QAAQ,EAAE,CAAC;QAC1K,CAAC;aAAM,CAAC;YACP,kDAAkD;YAClD,YAAY,GAAG,YAAY,WAAW,0GAA0G,MAAM,CAAC,gBAAgB,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,QAAQ,EAAE,CAAC;QACnM,CAAC;QAED,MAAM,UAAU,GAAkB;YACjC,IAAI,EAAE,oBAAoB,CAAC,IAAI;YAC/B,IAAI;YACJ,WAAW,EAAE,KAAK,EAAE,+BAA+B;YACnD,aAAa,EAAE,CAAC,YAAY,CAAC;SAC7B,CAAC;QACF,OAAO,UAAU,CAAC;IACnB,CAAC;CACD,CAAC,CAAC","sourcesContent":["import type { PackageJson } from \"type-fest\";\nimport type { PolicyFailure } from \"../policy.js\";\nimport { definePackagePolicy } from \"../policyDefiners/definePackagePolicy.js\";\nimport { getPackageScope, isScoped } from \"../utils/packageName.js\";\n\n/**\n * Configuration for the PackageAllowedScopes policy.\n *\n * This policy ensures packages in a monorepo use only approved scopes or package names.\n * This prevents accidental introduction of new scopes and ensures naming consistency.\n *\n * @example\n * ```typescript\n * const config: PackageAllowedScopesConfig = {\n * // Only packages in these scopes are allowed\n * allowedScopes: [\"@myorg\", \"@internal\", \"@experimental\"],\n * // These specific unscoped packages are also allowed\n * unscopedPackages: [\"my-legacy-tool\", \"special-package\"],\n * };\n * ```\n *\n * @alpha\n */\nexport interface PackageAllowedScopesConfig {\n\t/**\n\t * A list of npm scopes that are allowed for packages in this repository.\n\t *\n\t * Scopes should start with `@` (e.g., `@myorg`). A package like `@myorg/foo`\n\t * would match the scope `@myorg`.\n\t *\n\t * @remarks\n\t *\n\t * If a package's scope is not in this list and the package is not in\n\t * `unscopedPackages`, the policy will fail.\n\t *\n\t * @example\n\t * ```typescript\n\t * allowedScopes: [\"@myorg\", \"@myorg-tools\", \"@myorg-internal\"]\n\t * ```\n\t */\n\tallowedScopes?: string[];\n\n\t/**\n\t * A list of unscoped package names that are explicitly allowed.\n\t *\n\t * Use this for legacy packages or special cases where an unscoped name is needed.\n\t *\n\t * @remarks\n\t *\n\t * Unscoped packages not in this list will cause the policy to fail.\n\t *\n\t * @example\n\t * ```typescript\n\t * unscopedPackages: [\"my-legacy-tool\", \"special-package\"]\n\t * ```\n\t */\n\tunscopedPackages?: string[];\n}\n\n/**\n * Check if a package's scope is in the allowed list.\n *\n * @param packageName - The package name to check\n * @param allowedScopes - List of allowed scopes\n * @returns `true` if the package's scope is allowed\n */\nfunction isScopeAllowed(\n\tpackageName: string,\n\tallowedScopes: string[] | undefined,\n): boolean {\n\tif (allowedScopes === undefined || allowedScopes.length === 0) {\n\t\treturn false;\n\t}\n\n\tconst scope = getPackageScope(packageName);\n\tif (scope === undefined) {\n\t\t// Package is unscoped, so scope check doesn't apply\n\t\treturn false;\n\t}\n\n\treturn allowedScopes.includes(scope);\n}\n\n/**\n * Check if an unscoped package name is in the allowed list.\n *\n * @param packageName - The package name to check\n * @param unscopedPackages - List of allowed unscoped package names\n * @returns `true` if the unscoped package is allowed\n */\nfunction isUnscopedPackageAllowed(\n\tpackageName: string,\n\tunscopedPackages: string[] | undefined,\n): boolean {\n\tif (unscopedPackages === undefined || unscopedPackages.length === 0) {\n\t\treturn false;\n\t}\n\n\t// Only applies to unscoped packages\n\tif (isScoped(packageName)) {\n\t\treturn false;\n\t}\n\n\treturn unscopedPackages.includes(packageName);\n}\n\n/**\n * A policy that ensures packages use only allowed scopes and package names.\n *\n * This policy prevents accidental introduction of new npm scopes or unscoped packages\n * in a monorepo. It helps maintain naming consistency and prevents typos or mistakes\n * when creating new packages.\n *\n * @remarks\n *\n * The policy checks packages in the following order:\n *\n * 1. If the package is scoped (starts with `@`), check if its scope is in `allowedScopes`\n * 2. If the package is unscoped, check if it's in `unscopedPackages`\n * 3. If neither check passes, the policy fails\n *\n * The \"root\" package (common in monorepos) is automatically skipped.\n *\n * @example\n * ```typescript\n * import { makePolicy } from \"repopo\";\n * import { PackageAllowedScopes } from \"repopo/policies\";\n *\n * const config: RepopoConfig = {\n * policies: [\n * makePolicy(PackageAllowedScopes, {\n * allowedScopes: [\"@myorg\", \"@internal\"],\n * unscopedPackages: [\"legacy-package\"],\n * }),\n * ],\n * };\n * ```\n *\n * @alpha\n */\nexport const PackageAllowedScopes = definePackagePolicy<\n\tPackageJson,\n\tPackageAllowedScopesConfig | undefined\n>({\n\tname: \"PackageAllowedScopes\",\n\tdescription:\n\t\t\"Ensures packages use only allowed npm scopes and package names.\",\n\t// biome-ignore lint/correctness/useYield: no yield needed\n\thandler: function* (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\tconst packageName = json.name;\n\n\t\t// Skip packages without a name\n\t\tif (packageName === undefined) {\n\t\t\treturn true;\n\t\t}\n\n\t\t// Skip the root package (common in monorepos)\n\t\tif (packageName === \"root\") {\n\t\t\treturn true;\n\t\t}\n\n\t\t// Check if the package uses an allowed scope\n\t\tif (isScopeAllowed(packageName, config.allowedScopes)) {\n\t\t\treturn true;\n\t\t}\n\n\t\t// Check if the package is an allowed unscoped package\n\t\tif (isUnscopedPackageAllowed(packageName, config.unscopedPackages)) {\n\t\t\treturn true;\n\t\t}\n\n\t\t// Package doesn't match any allowed pattern - build error message\n\t\tconst scope = getPackageScope(packageName);\n\t\tlet errorMessage: string;\n\t\tif (scope !== undefined) {\n\t\t\t// Package has an unexpected scope\n\t\t\terrorMessage = `Package \"${packageName}\" uses scope \"${scope}\" which is not in the allowed scopes list. Allowed scopes: ${config.allowedScopes?.join(\", \") ?? \"(none)\"}`;\n\t\t} else {\n\t\t\t// Package is unscoped but not in the allowed list\n\t\t\terrorMessage = `Package \"${packageName}\" is an unscoped package that is not in the allowed unscoped packages list. Allowed unscoped packages: ${config.unscopedPackages?.join(\", \") ?? \"(none)\"}`;\n\t\t}\n\n\t\tconst failResult: PolicyFailure = {\n\t\t\tname: PackageAllowedScopes.name,\n\t\t\tfile,\n\t\t\tautoFixable: false, // Can't auto-fix package names\n\t\t\terrorMessages: [errorMessage],\n\t\t};\n\t\treturn failResult;\n\t},\n});\n"]}
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Configuration for the PackageEsmType policy.
3
+ *
4
+ * This policy ensures the `type` field in package.json is correctly set
5
+ * to indicate whether the package is an ESM or CommonJS module.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * const config: PackageEsmTypeConfig = {
10
+ * // Require ESM type field for all packages
11
+ * requiredType: "module",
12
+ * // Or detect based on exports/main field
13
+ * detectFromExports: true,
14
+ * };
15
+ * ```
16
+ *
17
+ * @alpha
18
+ */
19
+ export interface PackageEsmTypeConfig {
20
+ /**
21
+ * The required `type` field value for all packages.
22
+ *
23
+ * - `"module"` - All packages must be ESM (use import/export)
24
+ * - `"commonjs"` - All packages must be CommonJS (use require)
25
+ * - `undefined` - Use detection logic based on exports/main
26
+ *
27
+ * @remarks
28
+ *
29
+ * If set, this overrides automatic detection. Use `detectFromExports`
30
+ * for more flexible per-package type detection.
31
+ */
32
+ requiredType?: "module" | "commonjs";
33
+ /**
34
+ * Detect the expected `type` field from the exports or main field.
35
+ *
36
+ * When enabled, the policy analyzes the package's entry points:
37
+ * - If exports use `.mjs` or have `import` conditions → `"module"`
38
+ * - If exports use `.cjs` or have `require` conditions → `"commonjs"`
39
+ * - If main field uses `.mjs` → `"module"`
40
+ * - If main field uses `.cjs` → `"commonjs"`
41
+ *
42
+ * @defaultValue false
43
+ */
44
+ detectFromExports?: boolean;
45
+ /**
46
+ * Package names or scopes to exclude from validation.
47
+ *
48
+ * @example
49
+ * ```typescript
50
+ * excludePackages: ["@myorg/legacy-package", "old-cjs-lib"]
51
+ * ```
52
+ */
53
+ excludePackages?: string[];
54
+ /**
55
+ * Behavior when module type cannot be detected from exports/main.
56
+ *
57
+ * - `"skip"` (default) - Skip validation for packages where type cannot be detected
58
+ * - `"warn"` - Pass validation but include a warning in the output
59
+ * - `"fail"` - Fail validation when type cannot be detected
60
+ *
61
+ * This only applies when `detectFromExports` is true and no `requiredType` is set.
62
+ *
63
+ * @defaultValue "skip"
64
+ */
65
+ onDetectionFailure?: "skip" | "warn" | "fail";
66
+ }
67
+ /**
68
+ * A policy that ensures the `type` field in package.json is correctly set.
69
+ *
70
+ * This policy helps maintain consistency in how packages declare their module format,
71
+ * which is essential for proper module resolution in Node.js and bundlers.
72
+ *
73
+ * @remarks
74
+ *
75
+ * The policy can operate in two modes:
76
+ *
77
+ * 1. **Required Type**: Set `requiredType` to enforce a specific type for all packages
78
+ * 2. **Detection**: Set `detectFromExports: true` to detect the expected type from
79
+ * the package's exports or main field
80
+ *
81
+ * When `detectFromExports` is enabled, the policy looks for:
82
+ * - `.mjs` extensions or `"import"` conditions → expects `"module"`
83
+ * - `.cjs` extensions or `"require"` conditions → expects `"commonjs"`
84
+ *
85
+ * @example
86
+ * ```typescript
87
+ * import { makePolicy } from "repopo";
88
+ * import { PackageEsmType } from "repopo/policies";
89
+ *
90
+ * // Require all packages to be ESM
91
+ * makePolicy(PackageEsmType, { requiredType: "module" })
92
+ *
93
+ * // Or detect based on exports
94
+ * makePolicy(PackageEsmType, { detectFromExports: true })
95
+ * ```
96
+ *
97
+ * @alpha
98
+ */
99
+ export declare const PackageEsmType: import("../policy.js").PolicyDefinition<PackageEsmTypeConfig | undefined>;
100
+ //# sourceMappingURL=PackageEsmType.d.ts.map
@@ -0,0 +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"}