repopo 0.12.0 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +70 -0
- package/README.md +57 -90
- package/THIRD-PARTY-LICENSES.txt +169 -11
- package/bin/dev.js +0 -0
- package/esm/adapters/fluidFramework.d.ts +13 -9
- package/esm/adapters/fluidFramework.d.ts.map +1 -1
- package/esm/adapters/fluidFramework.js +20 -43
- package/esm/adapters/fluidFramework.js.map +1 -1
- package/esm/api.d.ts +66 -1
- package/esm/api.d.ts.map +1 -1
- package/esm/api.js +18 -1
- package/esm/api.js.map +1 -1
- package/esm/baseCommand.d.ts.map +1 -1
- package/esm/baseCommand.js +2 -2
- package/esm/baseCommand.js.map +1 -1
- package/esm/commands/check-native.d.ts +5 -3
- package/esm/commands/check-native.d.ts.map +1 -1
- package/esm/commands/check-native.js +35 -7
- package/esm/commands/check-native.js.map +1 -1
- package/esm/commands/check.d.ts +8 -21
- package/esm/commands/check.d.ts.map +1 -1
- package/esm/commands/check.js +98 -154
- package/esm/commands/check.js.map +1 -1
- package/esm/commands/list.d.ts.map +1 -1
- package/esm/commands/list.js +1 -0
- package/esm/commands/list.js.map +1 -1
- package/esm/concurrency.d.ts +3 -0
- package/esm/concurrency.d.ts.map +1 -0
- package/esm/concurrency.js +8 -0
- package/esm/concurrency.js.map +1 -0
- package/esm/config.d.ts +10 -39
- package/esm/config.d.ts.map +1 -1
- package/esm/config.js +3 -7
- package/esm/config.js.map +1 -1
- package/esm/context.d.ts +10 -1
- package/esm/context.d.ts.map +1 -1
- package/esm/context.js +19 -1
- package/esm/context.js.map +1 -1
- package/esm/executionContractError.d.ts +4 -0
- package/esm/executionContractError.d.ts.map +1 -0
- package/esm/executionContractError.js +4 -0
- package/esm/executionContractError.js.map +1 -0
- package/esm/fileCache.d.ts +12 -0
- package/esm/fileCache.d.ts.map +1 -0
- package/esm/fileCache.js +37 -0
- package/esm/fileCache.js.map +1 -0
- package/esm/filePaths.d.ts +2 -0
- package/esm/filePaths.d.ts.map +1 -0
- package/esm/filePaths.js +9 -0
- package/esm/filePaths.js.map +1 -0
- package/esm/generators.d.ts +0 -20
- package/esm/generators.d.ts.map +1 -1
- package/esm/generators.js.map +1 -1
- package/esm/index.d.ts +3 -16
- package/esm/index.d.ts.map +1 -1
- package/esm/index.js +3 -17
- package/esm/index.js.map +1 -1
- package/esm/makePolicy.d.ts +20 -100
- package/esm/makePolicy.d.ts.map +1 -1
- package/esm/makePolicy.js +46 -132
- package/esm/makePolicy.js.map +1 -1
- package/esm/perf.d.ts +5 -2
- package/esm/perf.d.ts.map +1 -1
- package/esm/perf.js +10 -9
- package/esm/perf.js.map +1 -1
- package/esm/policies/CargoLicenceValidated.d.ts +4 -4
- package/esm/policies/CargoLicenceValidated.d.ts.map +1 -1
- package/esm/policies/CargoLicenceValidated.js +9 -7
- package/esm/policies/CargoLicenceValidated.js.map +1 -1
- package/esm/policies/CargoLintsConfigured.d.ts +4 -4
- package/esm/policies/CargoLintsConfigured.d.ts.map +1 -1
- package/esm/policies/CargoLintsConfigured.js +19 -15
- package/esm/policies/CargoLintsConfigured.js.map +1 -1
- package/esm/policies/CargoLockPolicy.d.ts +4 -4
- package/esm/policies/CargoLockPolicy.d.ts.map +1 -1
- package/esm/policies/CargoLockPolicy.js +15 -11
- package/esm/policies/CargoLockPolicy.js.map +1 -1
- package/esm/policies/CargoTomlRequired.d.ts +5 -5
- package/esm/policies/CargoTomlRequired.d.ts.map +1 -1
- package/esm/policies/CargoTomlRequired.js +7 -7
- package/esm/policies/CargoTomlRequired.js.map +1 -1
- package/esm/policies/CargoTomlSorted.d.ts +4 -4
- package/esm/policies/CargoTomlSorted.d.ts.map +1 -1
- package/esm/policies/CargoTomlSorted.js +21 -17
- package/esm/policies/CargoTomlSorted.js.map +1 -1
- package/esm/policies/ConditionalScripts.d.ts +10 -8
- package/esm/policies/ConditionalScripts.d.ts.map +1 -1
- package/esm/policies/ConditionalScripts.js +24 -22
- package/esm/policies/ConditionalScripts.js.map +1 -1
- package/esm/policies/ExactScripts.d.ts +8 -6
- package/esm/policies/ExactScripts.d.ts.map +1 -1
- package/esm/policies/ExactScripts.js +15 -18
- package/esm/policies/ExactScripts.js.map +1 -1
- package/esm/policies/GleamDepVersions.d.ts +5 -5
- package/esm/policies/GleamDepVersions.d.ts.map +1 -1
- package/esm/policies/GleamDepVersions.js +5 -5
- package/esm/policies/GleamDepVersions.js.map +1 -1
- package/esm/policies/GleamDocExists.d.ts +4 -4
- package/esm/policies/GleamDocExists.d.ts.map +1 -1
- package/esm/policies/GleamDocExists.js +6 -6
- package/esm/policies/GleamDocExists.js.map +1 -1
- package/esm/policies/GleamInternalModulesConfigured.d.ts +4 -4
- package/esm/policies/GleamInternalModulesConfigured.d.ts.map +1 -1
- package/esm/policies/GleamInternalModulesConfigured.js +8 -6
- package/esm/policies/GleamInternalModulesConfigured.js.map +1 -1
- package/esm/policies/GleamLicenceConfigured.d.ts +4 -4
- package/esm/policies/GleamLicenceConfigured.d.ts.map +1 -1
- package/esm/policies/GleamLicenceConfigured.js +8 -6
- package/esm/policies/GleamLicenceConfigured.js.map +1 -1
- package/esm/policies/GleamManifestCommitted.d.ts +10 -3
- package/esm/policies/GleamManifestCommitted.d.ts.map +1 -1
- package/esm/policies/GleamManifestCommitted.js +10 -9
- package/esm/policies/GleamManifestCommitted.js.map +1 -1
- package/esm/policies/GleamMonorepoConsistency.d.ts +13 -5
- package/esm/policies/GleamMonorepoConsistency.d.ts.map +1 -1
- package/esm/policies/GleamMonorepoConsistency.js +10 -10
- package/esm/policies/GleamMonorepoConsistency.js.map +1 -1
- package/esm/policies/GleamNameRestrictions.d.ts +4 -4
- package/esm/policies/GleamNameRestrictions.d.ts.map +1 -1
- package/esm/policies/GleamNameRestrictions.js +6 -6
- package/esm/policies/GleamNameRestrictions.js.map +1 -1
- package/esm/policies/GleamNoPathDepsInPublished.d.ts +4 -4
- package/esm/policies/GleamNoPathDepsInPublished.d.ts.map +1 -1
- package/esm/policies/GleamNoPathDepsInPublished.js +6 -6
- package/esm/policies/GleamNoPathDepsInPublished.js.map +1 -1
- package/esm/policies/GleamPathDepsValid.d.ts +3 -3
- package/esm/policies/GleamPathDepsValid.d.ts.map +1 -1
- package/esm/policies/GleamPathDepsValid.js +5 -5
- package/esm/policies/GleamPathDepsValid.js.map +1 -1
- package/esm/policies/GleamProjectStructure.d.ts +11 -4
- package/esm/policies/GleamProjectStructure.d.ts.map +1 -1
- package/esm/policies/GleamProjectStructure.js +7 -8
- package/esm/policies/GleamProjectStructure.js.map +1 -1
- package/esm/policies/GleamSharedDependencyVersions.d.ts +12 -4
- package/esm/policies/GleamSharedDependencyVersions.d.ts.map +1 -1
- package/esm/policies/GleamSharedDependencyVersions.js +10 -10
- package/esm/policies/GleamSharedDependencyVersions.js.map +1 -1
- package/esm/policies/GleamTomlRequired.d.ts +4 -4
- package/esm/policies/GleamTomlRequired.d.ts.map +1 -1
- package/esm/policies/GleamTomlRequired.js +5 -5
- package/esm/policies/GleamTomlRequired.js.map +1 -1
- package/esm/policies/GleamTomlSorted.d.ts +4 -4
- package/esm/policies/GleamTomlSorted.d.ts.map +1 -1
- package/esm/policies/GleamTomlSorted.js +21 -17
- package/esm/policies/GleamTomlSorted.js.map +1 -1
- package/esm/policies/GleamVersionConstraint.d.ts +4 -4
- package/esm/policies/GleamVersionConstraint.d.ts.map +1 -1
- package/esm/policies/GleamVersionConstraint.js +14 -10
- package/esm/policies/GleamVersionConstraint.js.map +1 -1
- package/esm/policies/HtmlFileHeaders.d.ts +4 -2
- package/esm/policies/HtmlFileHeaders.d.ts.map +1 -1
- package/esm/policies/HtmlFileHeaders.js +1 -1
- package/esm/policies/HtmlFileHeaders.js.map +1 -1
- package/esm/policies/JsTsFileHeaders.d.ts +4 -2
- package/esm/policies/JsTsFileHeaders.d.ts.map +1 -1
- package/esm/policies/JsTsFileHeaders.js +1 -1
- package/esm/policies/JsTsFileHeaders.js.map +1 -1
- package/esm/policies/LicenseFileExists.d.ts +10 -4
- package/esm/policies/LicenseFileExists.d.ts.map +1 -1
- package/esm/policies/LicenseFileExists.js +27 -20
- package/esm/policies/LicenseFileExists.js.map +1 -1
- package/esm/policies/MaxChangedFiles.d.ts +17 -0
- package/esm/policies/MaxChangedFiles.d.ts.map +1 -0
- package/esm/policies/MaxChangedFiles.js +22 -0
- package/esm/policies/MaxChangedFiles.js.map +1 -0
- package/esm/policies/MutuallyExclusiveScripts.d.ts +8 -6
- package/esm/policies/MutuallyExclusiveScripts.d.ts.map +1 -1
- package/esm/policies/MutuallyExclusiveScripts.js +9 -13
- package/esm/policies/MutuallyExclusiveScripts.js.map +1 -1
- package/esm/policies/NoBuildDirectory.d.ts +9 -3
- package/esm/policies/NoBuildDirectory.d.ts.map +1 -1
- package/esm/policies/NoBuildDirectory.js +8 -13
- package/esm/policies/NoBuildDirectory.js.map +1 -1
- package/esm/policies/NoJsFileExtensions.d.ts +9 -3
- package/esm/policies/NoJsFileExtensions.d.ts.map +1 -1
- package/esm/policies/NoJsFileExtensions.js +9 -14
- package/esm/policies/NoJsFileExtensions.js.map +1 -1
- package/esm/policies/NoLargeBinaryFiles.d.ts +9 -4
- package/esm/policies/NoLargeBinaryFiles.d.ts.map +1 -1
- package/esm/policies/NoLargeBinaryFiles.js +10 -16
- package/esm/policies/NoLargeBinaryFiles.js.map +1 -1
- package/esm/policies/NoPrivateWorkspaceDependencies.d.ts +10 -6
- package/esm/policies/NoPrivateWorkspaceDependencies.d.ts.map +1 -1
- package/esm/policies/NoPrivateWorkspaceDependencies.js +12 -14
- package/esm/policies/NoPrivateWorkspaceDependencies.js.map +1 -1
- package/esm/policies/NoTargetArtifacts.d.ts +9 -3
- package/esm/policies/NoTargetArtifacts.d.ts.map +1 -1
- package/esm/policies/NoTargetArtifacts.js +9 -14
- package/esm/policies/NoTargetArtifacts.js.map +1 -1
- package/esm/policies/NoUnsafeWithoutJustification.d.ts +11 -4
- package/esm/policies/NoUnsafeWithoutJustification.d.ts.map +1 -1
- package/esm/policies/NoUnsafeWithoutJustification.js +8 -9
- package/esm/policies/NoUnsafeWithoutJustification.js.map +1 -1
- package/esm/policies/NoWildcardDependencies.d.ts +5 -5
- package/esm/policies/NoWildcardDependencies.d.ts.map +1 -1
- package/esm/policies/NoWildcardDependencies.js +6 -6
- package/esm/policies/NoWildcardDependencies.js.map +1 -1
- package/esm/policies/PackageAllowedScopes.d.ts +9 -5
- package/esm/policies/PackageAllowedScopes.d.ts.map +1 -1
- package/esm/policies/PackageAllowedScopes.js +15 -16
- package/esm/policies/PackageAllowedScopes.js.map +1 -1
- package/esm/policies/PackageEsmType.d.ts +7 -5
- package/esm/policies/PackageEsmType.d.ts.map +1 -1
- package/esm/policies/PackageEsmType.js +31 -31
- package/esm/policies/PackageEsmType.js.map +1 -1
- package/esm/policies/PackageFolderName.d.ts +8 -6
- package/esm/policies/PackageFolderName.d.ts.map +1 -1
- package/esm/policies/PackageFolderName.js +13 -16
- package/esm/policies/PackageFolderName.js.map +1 -1
- package/esm/policies/PackageJsonProperties.d.ts +5 -3
- package/esm/policies/PackageJsonProperties.d.ts.map +1 -1
- package/esm/policies/PackageJsonProperties.js +6 -17
- package/esm/policies/PackageJsonProperties.js.map +1 -1
- package/esm/policies/PackageJsonRepoDirectoryProperty.d.ts +2 -7
- package/esm/policies/PackageJsonRepoDirectoryProperty.d.ts.map +1 -1
- package/esm/policies/PackageJsonRepoDirectoryProperty.js +28 -42
- package/esm/policies/PackageJsonRepoDirectoryProperty.js.map +1 -1
- package/esm/policies/PackageJsonSorted.d.ts +2 -2
- package/esm/policies/PackageJsonSorted.d.ts.map +1 -1
- package/esm/policies/PackageJsonSorted.js +15 -30
- package/esm/policies/PackageJsonSorted.js.map +1 -1
- package/esm/policies/PackageLicense.d.ts +4 -4
- package/esm/policies/PackageLicense.d.ts.map +1 -1
- package/esm/policies/PackageLicense.js +31 -52
- package/esm/policies/PackageLicense.js.map +1 -1
- package/esm/policies/PackagePrivateField.d.ts +12 -8
- package/esm/policies/PackagePrivateField.d.ts.map +1 -1
- package/esm/policies/PackagePrivateField.js +31 -23
- package/esm/policies/PackagePrivateField.js.map +1 -1
- package/esm/policies/PackageReadme.d.ts +6 -5
- package/esm/policies/PackageReadme.d.ts.map +1 -1
- package/esm/policies/PackageReadme.js +22 -59
- package/esm/policies/PackageReadme.js.map +1 -1
- package/esm/policies/PackageScripts.d.ts +26 -22
- package/esm/policies/PackageScripts.d.ts.map +1 -1
- package/esm/policies/PackageScripts.js +39 -36
- package/esm/policies/PackageScripts.js.map +1 -1
- package/esm/policies/PackageTestScripts.d.ts +10 -6
- package/esm/policies/PackageTestScripts.d.ts.map +1 -1
- package/esm/policies/PackageTestScripts.js +14 -16
- package/esm/policies/PackageTestScripts.js.map +1 -1
- package/esm/policies/PublicApiDocumented.d.ts +4 -4
- package/esm/policies/PublicApiDocumented.d.ts.map +1 -1
- package/esm/policies/PublicApiDocumented.js +10 -8
- package/esm/policies/PublicApiDocumented.js.map +1 -1
- package/esm/policies/RequireCompanionChanges.d.ts +16 -0
- package/esm/policies/RequireCompanionChanges.d.ts.map +1 -0
- package/esm/policies/RequireCompanionChanges.js +42 -0
- package/esm/policies/RequireCompanionChanges.js.map +1 -0
- package/esm/policies/RequiredGitignorePatterns.d.ts +11 -4
- package/esm/policies/RequiredGitignorePatterns.d.ts.map +1 -1
- package/esm/policies/RequiredGitignorePatterns.js +29 -36
- package/esm/policies/RequiredGitignorePatterns.js.map +1 -1
- package/esm/policies/RequiredScripts.d.ts +9 -7
- package/esm/policies/RequiredScripts.d.ts.map +1 -1
- package/esm/policies/RequiredScripts.js +24 -24
- package/esm/policies/RequiredScripts.js.map +1 -1
- package/esm/policies/RustDocExists.d.ts +4 -4
- package/esm/policies/RustDocExists.d.ts.map +1 -1
- package/esm/policies/RustDocExists.js +6 -6
- package/esm/policies/RustDocExists.js.map +1 -1
- package/esm/policies/RustToolchainExists.d.ts +11 -4
- package/esm/policies/RustToolchainExists.d.ts.map +1 -1
- package/esm/policies/RustToolchainExists.js +10 -9
- package/esm/policies/RustToolchainExists.js.map +1 -1
- package/esm/policies/RustfmtConfigExists.d.ts +11 -4
- package/esm/policies/RustfmtConfigExists.d.ts.map +1 -1
- package/esm/policies/RustfmtConfigExists.js +10 -9
- package/esm/policies/RustfmtConfigExists.js.map +1 -1
- package/esm/policies/ScriptContains.d.ts +9 -7
- package/esm/policies/ScriptContains.d.ts.map +1 -1
- package/esm/policies/ScriptContains.js +9 -13
- package/esm/policies/ScriptContains.js.map +1 -1
- package/esm/policies/SharedDependencyVersions.d.ts +9 -4
- package/esm/policies/SharedDependencyVersions.d.ts.map +1 -1
- package/esm/policies/SharedDependencyVersions.js +28 -20
- package/esm/policies/SharedDependencyVersions.js.map +1 -1
- package/esm/policies/WorkspaceInheritance.d.ts +11 -4
- package/esm/policies/WorkspaceInheritance.d.ts.map +1 -1
- package/esm/policies/WorkspaceInheritance.js +10 -11
- package/esm/policies/WorkspaceInheritance.js.map +1 -1
- package/esm/policies/WorkspaceMembersValid.d.ts +11 -4
- package/esm/policies/WorkspaceMembersValid.d.ts.map +1 -1
- package/esm/policies/WorkspaceMembersValid.js +10 -11
- package/esm/policies/WorkspaceMembersValid.js.map +1 -1
- package/esm/policies/constants.d.ts.map +1 -1
- package/esm/policies/constants.js +1 -1
- package/esm/policies/constants.js.map +1 -1
- package/esm/policies.d.ts +15 -5
- package/esm/policies.d.ts.map +1 -1
- package/esm/policies.js +14 -5
- package/esm/policies.js.map +1 -1
- package/esm/policy.d.ts +67 -289
- package/esm/policy.d.ts.map +1 -1
- package/esm/policy.js +1 -122
- package/esm/policy.js.map +1 -1
- package/esm/policyDefiners/defineCargoPolicy.d.ts +25 -9
- package/esm/policyDefiners/defineCargoPolicy.d.ts.map +1 -1
- package/esm/policyDefiners/defineCargoPolicy.js +10 -28
- package/esm/policyDefiners/defineCargoPolicy.js.map +1 -1
- package/esm/policyDefiners/defineFileHeaderPolicy.d.ts +27 -14
- package/esm/policyDefiners/defineFileHeaderPolicy.d.ts.map +1 -1
- package/esm/policyDefiners/defineFileHeaderPolicy.js +16 -35
- package/esm/policyDefiners/defineFileHeaderPolicy.js.map +1 -1
- package/esm/policyDefiners/defineGleamPolicy.d.ts +25 -9
- package/esm/policyDefiners/defineGleamPolicy.d.ts.map +1 -1
- package/esm/policyDefiners/defineGleamPolicy.js +10 -27
- package/esm/policyDefiners/defineGleamPolicy.js.map +1 -1
- package/esm/policyDefiners/definePackagePolicy.d.ts +24 -13
- package/esm/policyDefiners/definePackagePolicy.d.ts.map +1 -1
- package/esm/policyDefiners/definePackagePolicy.js +9 -32
- package/esm/policyDefiners/definePackagePolicy.js.map +1 -1
- package/esm/policyDefiners/spdxValidator.d.ts +4 -12
- package/esm/policyDefiners/spdxValidator.d.ts.map +1 -1
- package/esm/policyDefiners/spdxValidator.js +26 -14
- package/esm/policyDefiners/spdxValidator.js.map +1 -1
- package/esm/policyExecution.d.ts +20 -0
- package/esm/policyExecution.d.ts.map +1 -0
- package/esm/policyExecution.js +234 -0
- package/esm/policyExecution.js.map +1 -0
- package/esm/repositoryChanges.d.ts +26 -0
- package/esm/repositoryChanges.d.ts.map +1 -0
- package/esm/repositoryChanges.js +172 -0
- package/esm/repositoryChanges.js.map +1 -0
- package/esm/repositoryFiles.d.ts +6 -0
- package/esm/repositoryFiles.d.ts.map +1 -0
- package/esm/repositoryFiles.js +11 -0
- package/esm/repositoryFiles.js.map +1 -0
- package/esm/runner.d.ts +73 -47
- package/esm/runner.d.ts.map +1 -1
- package/esm/runner.js +158 -144
- package/esm/runner.js.map +1 -1
- package/esm/settlePolicyPromise.d.ts +4 -0
- package/esm/settlePolicyPromise.d.ts.map +1 -0
- package/esm/settlePolicyPromise.js +10 -0
- package/esm/settlePolicyPromise.js.map +1 -0
- package/esm/tsdoc-metadata.json +1 -1
- package/esm/utils/safePaths.d.ts +9 -0
- package/esm/utils/safePaths.d.ts.map +1 -0
- package/esm/utils/safePaths.js +40 -0
- package/esm/utils/safePaths.js.map +1 -0
- package/oclif.manifest.json +57 -2
- package/package.json +42 -27
- package/scripts/benchmark.mjs +341 -0
- package/sidecar/sidecar.mjs +388 -417
|
@@ -34,36 +34,20 @@ function createReadmeContent(packageName, requiredContent) {
|
|
|
34
34
|
/**
|
|
35
35
|
* Handle case when README is missing.
|
|
36
36
|
*/
|
|
37
|
-
async function handleMissingReadme(readmePath, packageName,
|
|
37
|
+
async function handleMissingReadme(readmePath, packageName, _file, resolve, requiredContent) {
|
|
38
38
|
if (resolve) {
|
|
39
39
|
try {
|
|
40
40
|
await writeFile(readmePath, createReadmeContent(packageName, requiredContent));
|
|
41
|
-
return {
|
|
42
|
-
name: "PackageReadme",
|
|
43
|
-
file,
|
|
44
|
-
resolved: true,
|
|
45
|
-
autoFixable: true,
|
|
46
|
-
errorMessages: [`Created README.md for package "${packageName}"`],
|
|
47
|
-
};
|
|
41
|
+
return [`Created README.md for package "${packageName}"`].map((message) => ({ message, fixStatus: "fixed" }));
|
|
48
42
|
}
|
|
49
43
|
catch {
|
|
50
|
-
return {
|
|
51
|
-
name: "PackageReadme",
|
|
52
|
-
file,
|
|
53
|
-
resolved: false,
|
|
54
|
-
autoFixable: true,
|
|
55
|
-
errorMessages: [
|
|
56
|
-
`Failed to create README.md for package "${packageName}"`,
|
|
57
|
-
],
|
|
58
|
-
};
|
|
44
|
+
return [`Failed to create README.md for package "${packageName}"`].map((message) => ({ message, fixStatus: "failed" }));
|
|
59
45
|
}
|
|
60
46
|
}
|
|
61
|
-
return {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
errorMessages: [`README.md missing for package "${packageName}"`],
|
|
66
|
-
};
|
|
47
|
+
return [`README.md missing for package "${packageName}"`].map((message) => ({
|
|
48
|
+
message,
|
|
49
|
+
fixStatus: "available",
|
|
50
|
+
}));
|
|
67
51
|
}
|
|
68
52
|
/**
|
|
69
53
|
* Validate README title against package name.
|
|
@@ -82,7 +66,7 @@ function validateTitle(readmeContent, packageName) {
|
|
|
82
66
|
*/
|
|
83
67
|
async function handleRequiredContent(readmePath, readmeContent, requiredContent, resolve) {
|
|
84
68
|
if (readmeContent.includes(requiredContent)) {
|
|
85
|
-
return
|
|
69
|
+
return undefined;
|
|
86
70
|
}
|
|
87
71
|
if (resolve) {
|
|
88
72
|
try {
|
|
@@ -91,8 +75,8 @@ async function handleRequiredContent(readmePath, readmeContent, requiredContent,
|
|
|
91
75
|
: `${readmeContent}\n${requiredContent}\n`;
|
|
92
76
|
await writeFile(readmePath, newContent);
|
|
93
77
|
return {
|
|
94
|
-
|
|
95
|
-
|
|
78
|
+
message: `README missing required content: "${requiredContent}"`,
|
|
79
|
+
fixStatus: "fixed",
|
|
96
80
|
};
|
|
97
81
|
}
|
|
98
82
|
catch {
|
|
@@ -100,52 +84,30 @@ async function handleRequiredContent(readmePath, readmeContent, requiredContent,
|
|
|
100
84
|
}
|
|
101
85
|
}
|
|
102
86
|
return {
|
|
103
|
-
|
|
104
|
-
|
|
87
|
+
message: `README missing required content: "${requiredContent}"`,
|
|
88
|
+
fixStatus: resolve ? "failed" : "available",
|
|
105
89
|
};
|
|
106
90
|
}
|
|
107
91
|
/**
|
|
108
92
|
* Validate existing README content.
|
|
109
93
|
*/
|
|
110
94
|
async function validateExistingReadme(ctx) {
|
|
111
|
-
const
|
|
112
|
-
let wasFixed = false;
|
|
95
|
+
const findings = [];
|
|
113
96
|
// Check title matches package name
|
|
114
97
|
if (ctx.requireMatchingTitle) {
|
|
115
98
|
const titleResult = validateTitle(ctx.readmeContent, ctx.packageName);
|
|
116
99
|
if (titleResult !== true) {
|
|
117
|
-
|
|
100
|
+
findings.push({ message: titleResult });
|
|
118
101
|
}
|
|
119
102
|
}
|
|
120
103
|
// Check required content
|
|
121
104
|
if (ctx.requiredContent) {
|
|
122
105
|
const contentResult = await handleRequiredContent(ctx.readmePath, ctx.readmeContent, ctx.requiredContent, ctx.resolve);
|
|
123
|
-
if (contentResult
|
|
124
|
-
|
|
106
|
+
if (contentResult) {
|
|
107
|
+
findings.push(contentResult);
|
|
125
108
|
}
|
|
126
|
-
if (contentResult.fixed) {
|
|
127
|
-
wasFixed = true;
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
if (errors.length === 0) {
|
|
131
|
-
return true;
|
|
132
|
-
}
|
|
133
|
-
if (wasFixed) {
|
|
134
|
-
return {
|
|
135
|
-
name: "PackageReadme",
|
|
136
|
-
file: ctx.file,
|
|
137
|
-
resolved: true,
|
|
138
|
-
autoFixable: true,
|
|
139
|
-
errorMessages: [errors.join("; ")],
|
|
140
|
-
};
|
|
141
109
|
}
|
|
142
|
-
return
|
|
143
|
-
name: "PackageReadme",
|
|
144
|
-
file: ctx.file,
|
|
145
|
-
autoFixable: ctx.requiredContent !== undefined &&
|
|
146
|
-
!ctx.readmeContent.includes(ctx.requiredContent),
|
|
147
|
-
errorMessages: [errors.join("; ")],
|
|
148
|
-
};
|
|
110
|
+
return findings;
|
|
149
111
|
}
|
|
150
112
|
/**
|
|
151
113
|
* A repo policy that ensures each package has a README.md file with proper content.
|
|
@@ -167,17 +129,18 @@ async function validateExistingReadme(ctx) {
|
|
|
167
129
|
* const config: RepopoConfig = {
|
|
168
130
|
* policies: [
|
|
169
131
|
* // Basic README validation
|
|
170
|
-
* makePolicy(PackageReadme),
|
|
132
|
+
* makePolicy(PackageReadme, { instanceId: "readme" }),
|
|
171
133
|
*
|
|
172
134
|
* // Require trademark notice in all READMEs
|
|
173
135
|
* makePolicy(PackageReadme, {
|
|
174
|
-
*
|
|
136
|
+
* instanceId: "readme-trademark",
|
|
137
|
+
* config: { requiredContent: "## Trademark" },
|
|
175
138
|
* }),
|
|
176
139
|
* ],
|
|
177
140
|
* };
|
|
178
141
|
* ```
|
|
179
142
|
*
|
|
180
|
-
* @
|
|
143
|
+
* @public
|
|
181
144
|
*/
|
|
182
145
|
export const PackageReadme = definePackagePolicy({
|
|
183
146
|
name: "PackageReadme",
|
|
@@ -188,7 +151,7 @@ export const PackageReadme = definePackagePolicy({
|
|
|
188
151
|
const requiredContent = config?.requiredContent;
|
|
189
152
|
// Skip private packages if configured to do so
|
|
190
153
|
if (skipPrivate && json.private === true) {
|
|
191
|
-
return
|
|
154
|
+
return [];
|
|
192
155
|
}
|
|
193
156
|
const packageName = json.name ?? "unknown";
|
|
194
157
|
const packageDir = path.dirname(path.resolve(root, file));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PackageReadme.js","sourceRoot":"","sources":["../../src/policies/PackageReadme.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,IAAI,MAAM,OAAO,CAAC;AAGzB,OAAO,EAAE,mBAAmB,EAAE,MAAM,0CAA0C,CAAC;AAE/E,qCAAqC;AACrC,MAAM,gBAAgB,GAAG,OAAO,CAAC;AACjC,MAAM,aAAa,GAAG,YAAY,CAAC;AAgDnC;;;;;GAKG;AACH,SAAS,kBAAkB,CAAC,OAAe;IAC1C,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC;IAC9C,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QAC1B,MAAM,KAAK,GAAG,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACvC,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAChB,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QACxB,CAAC;IACF,CAAC;IACD,OAAO,SAAS,CAAC;AAClB,CAAC;AAED;;GAEG;AACH,SAAS,mBAAmB,CAC3B,WAAmB,EACnB,eAAwB;IAExB,IAAI,OAAO,GAAG,KAAK,WAAW,IAAI,CAAC;IACnC,IAAI,eAAe,EAAE,CAAC;QACrB,OAAO,IAAI,KAAK,eAAe,IAAI,CAAC;IACrC,CAAC;IACD,OAAO,OAAO,CAAC;AAChB,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,mBAAmB,CACjC,UAAkB,EAClB,WAAmB,EACnB,IAAY,EACZ,OAAgB,EAChB,eAAwB;IAExB,IAAI,OAAO,EAAE,CAAC;QACb,IAAI,CAAC;YACJ,MAAM,SAAS,CACd,UAAU,EACV,mBAAmB,CAAC,WAAW,EAAE,eAAe,CAAC,CACjD,CAAC;YACF,OAAO;gBACN,IAAI,EAAE,eAAe;gBACrB,IAAI;gBACJ,QAAQ,EAAE,IAAI;gBACd,WAAW,EAAE,IAAI;gBACjB,aAAa,EAAE,CAAC,kCAAkC,WAAW,GAAG,CAAC;aACjE,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACR,OAAO;gBACN,IAAI,EAAE,eAAe;gBACrB,IAAI;gBACJ,QAAQ,EAAE,KAAK;gBACf,WAAW,EAAE,IAAI;gBACjB,aAAa,EAAE;oBACd,2CAA2C,WAAW,GAAG;iBACzD;aACD,CAAC;QACH,CAAC;IACF,CAAC;IAED,OAAO;QACN,IAAI,EAAE,eAAe;QACrB,IAAI;QACJ,WAAW,EAAE,IAAI;QACjB,aAAa,EAAE,CAAC,kCAAkC,WAAW,GAAG,CAAC;KACjE,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,SAAS,aAAa,CACrB,aAAqB,EACrB,WAAmB;IAEnB,MAAM,KAAK,GAAG,kBAAkB,CAAC,aAAa,CAAC,CAAC;IAChD,IAAI,KAAK,KAAK,WAAW,EAAE,CAAC;QAC3B,OAAO,iBAAiB,KAAK,IAAI,QAAQ,iCAAiC,WAAW,GAAG,CAAC;IAC1F,CAAC;IACD,OAAO,IAAI,CAAC;AACb,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,qBAAqB,CACnC,UAAkB,EAClB,aAAqB,EACrB,eAAuB,EACvB,OAAgB;IAEhB,IAAI,aAAa,CAAC,QAAQ,CAAC,eAAe,CAAC,EAAE,CAAC;QAC7C,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;IACzB,CAAC;IAED,IAAI,OAAO,EAAE,CAAC;QACb,IAAI,CAAC;YACJ,MAAM,UAAU,GAAG,aAAa,CAAC,QAAQ,CAAC,IAAI,CAAC;gBAC9C,CAAC,CAAC,GAAG,aAAa,GAAG,eAAe,IAAI;gBACxC,CAAC,CAAC,GAAG,aAAa,KAAK,eAAe,IAAI,CAAC;YAC5C,MAAM,SAAS,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;YACxC,OAAO;gBACN,KAAK,EAAE,qCAAqC,eAAe,GAAG;gBAC9D,KAAK,EAAE,IAAI;aACX,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACR,+BAA+B;QAChC,CAAC;IACF,CAAC;IAED,OAAO;QACN,KAAK,EAAE,qCAAqC,eAAe,GAAG;QAC9D,KAAK,EAAE,KAAK;KACZ,CAAC;AACH,CAAC;AAYD;;GAEG;AACH,KAAK,UAAU,sBAAsB,CACpC,GAAsB;IAEtB,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,IAAI,QAAQ,GAAG,KAAK,CAAC;IAErB,mCAAmC;IACnC,IAAI,GAAG,CAAC,oBAAoB,EAAE,CAAC;QAC9B,MAAM,WAAW,GAAG,aAAa,CAAC,GAAG,CAAC,aAAa,EAAE,GAAG,CAAC,WAAW,CAAC,CAAC;QACtE,IAAI,WAAW,KAAK,IAAI,EAAE,CAAC;YAC1B,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAC1B,CAAC;IACF,CAAC;IAED,yBAAyB;IACzB,IAAI,GAAG,CAAC,eAAe,EAAE,CAAC;QACzB,MAAM,aAAa,GAAG,MAAM,qBAAqB,CAChD,GAAG,CAAC,UAAU,EACd,GAAG,CAAC,aAAa,EACjB,GAAG,CAAC,eAAe,EACnB,GAAG,CAAC,OAAO,CACX,CAAC;QACF,IAAI,aAAa,CAAC,KAAK,EAAE,CAAC;YACzB,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;QAClC,CAAC;QACD,IAAI,aAAa,CAAC,KAAK,EAAE,CAAC;YACzB,QAAQ,GAAG,IAAI,CAAC;QACjB,CAAC;IACF,CAAC;IAED,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,IAAI,CAAC;IACb,CAAC;IAED,IAAI,QAAQ,EAAE,CAAC;QACd,OAAO;YACN,IAAI,EAAE,eAAe;YACrB,IAAI,EAAE,GAAG,CAAC,IAAI;YACd,QAAQ,EAAE,IAAI;YACd,WAAW,EAAE,IAAI;YACjB,aAAa,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;SAClC,CAAC;IACH,CAAC;IAED,OAAO;QACN,IAAI,EAAE,eAAe;QACrB,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,WAAW,EACV,GAAG,CAAC,eAAe,KAAK,SAAS;YACjC,CAAC,GAAG,CAAC,aAAa,CAAC,QAAQ,CAAC,GAAG,CAAC,eAAe,CAAC;QACjD,aAAa,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;KAClC,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,mBAAmB,CAG9C;IACD,IAAI,EAAE,eAAe;IACrB,WAAW,EACV,mFAAmF;IACpF,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE;QACxD,MAAM,WAAW,GAAG,MAAM,EAAE,WAAW,IAAI,IAAI,CAAC;QAChD,MAAM,oBAAoB,GAAG,MAAM,EAAE,oBAAoB,IAAI,IAAI,CAAC;QAClE,MAAM,eAAe,GAAG,MAAM,EAAE,eAAe,CAAC;QAEhD,+CAA+C;QAC/C,IAAI,WAAW,IAAI,IAAI,CAAC,OAAO,KAAK,IAAI,EAAE,CAAC;YAC1C,OAAO,IAAI,CAAC;QACb,CAAC;QAED,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,IAAI,SAAS,CAAC;QAC3C,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC;QAC1D,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;QAEtD,yBAAyB;QACzB,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;YAC7B,OAAO,MAAM,mBAAmB,CAC/B,UAAU,EACV,WAAW,EACX,IAAI,EACJ,OAAO,EACP,eAAe,CACf,CAAC;QACH,CAAC;QAED,kCAAkC;QAClC,MAAM,aAAa,GAAG,MAAM,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QAC1D,OAAO,MAAM,sBAAsB,CAAC;YACnC,UAAU;YACV,aAAa;YACb,WAAW;YACX,IAAI;YACJ,OAAO;YACP,oBAAoB;YACpB,eAAe;SACf,CAAC,CAAC;IACJ,CAAC;CACD,CAAC,CAAC","sourcesContent":["import { existsSync } from \"node:fs\";\nimport { readFile, writeFile } from \"node:fs/promises\";\nimport path from \"pathe\";\nimport type { PackageJson } from \"type-fest\";\nimport type { PolicyFailure, PolicyFixResult } from \"../policy.js\";\nimport { definePackagePolicy } from \"../policyDefiners/definePackagePolicy.js\";\n\n// Pre-compiled regex for performance\nconst LINE_SPLIT_REGEX = /\\r?\\n/;\nconst HEADING_REGEX = /^#\\s+(.+)$/;\n\n/**\n * Policy settings for the PackageReadme repo policy.\n *\n * @alpha\n */\nexport interface PackageReadmeSettings {\n\t/**\n\t * Whether to skip validation for private packages.\n\t *\n\t * @defaultValue true\n\t *\n\t * @remarks\n\t *\n\t * Private packages are typically not published and may not need a README file.\n\t * Set to `false` to require README files for all packages.\n\t */\n\tskipPrivate?: boolean;\n\n\t/**\n\t * Whether to require the README title to match the package name.\n\t *\n\t * @defaultValue true\n\t *\n\t * @remarks\n\t *\n\t * When enabled, the first line of the README must be `# package-name`.\n\t * This ensures consistency and helps users identify the package.\n\t */\n\trequireMatchingTitle?: boolean;\n\n\t/**\n\t * A string that must appear in the README content.\n\t *\n\t * @remarks\n\t *\n\t * Useful for ensuring all READMEs include required content like trademark\n\t * notices, disclaimers, or standard documentation sections.\n\t *\n\t * @example\n\t * ```typescript\n\t * requiredContent: \"## Trademark\"\n\t * ```\n\t */\n\trequiredContent?: string;\n}\n\n/**\n * Extract the title from a README file (first markdown H1 heading).\n *\n * @param content - The README content\n * @returns The title text (without the `# ` prefix) or `undefined` if not found\n */\nfunction extractReadmeTitle(content: string): string | undefined {\n\tconst lines = content.split(LINE_SPLIT_REGEX);\n\tfor (const line of lines) {\n\t\tconst match = HEADING_REGEX.exec(line);\n\t\tif (match?.[1]) {\n\t\t\treturn match[1].trim();\n\t\t}\n\t}\n\treturn undefined;\n}\n\n/**\n * Create a basic README content with package name as title.\n */\nfunction createReadmeContent(\n\tpackageName: string,\n\trequiredContent?: string,\n): string {\n\tlet content = `# ${packageName}\\n`;\n\tif (requiredContent) {\n\t\tcontent += `\\n${requiredContent}\\n`;\n\t}\n\treturn content;\n}\n\n/**\n * Handle case when README is missing.\n */\nasync function handleMissingReadme(\n\treadmePath: string,\n\tpackageName: string,\n\tfile: string,\n\tresolve: boolean,\n\trequiredContent?: string,\n): Promise<PolicyFailure | PolicyFixResult> {\n\tif (resolve) {\n\t\ttry {\n\t\t\tawait writeFile(\n\t\t\t\treadmePath,\n\t\t\t\tcreateReadmeContent(packageName, requiredContent),\n\t\t\t);\n\t\t\treturn {\n\t\t\t\tname: \"PackageReadme\",\n\t\t\t\tfile,\n\t\t\t\tresolved: true,\n\t\t\t\tautoFixable: true,\n\t\t\t\terrorMessages: [`Created README.md for package \"${packageName}\"`],\n\t\t\t};\n\t\t} catch {\n\t\t\treturn {\n\t\t\t\tname: \"PackageReadme\",\n\t\t\t\tfile,\n\t\t\t\tresolved: false,\n\t\t\t\tautoFixable: true,\n\t\t\t\terrorMessages: [\n\t\t\t\t\t`Failed to create README.md for package \"${packageName}\"`,\n\t\t\t\t],\n\t\t\t};\n\t\t}\n\t}\n\n\treturn {\n\t\tname: \"PackageReadme\",\n\t\tfile,\n\t\tautoFixable: true,\n\t\terrorMessages: [`README.md missing for package \"${packageName}\"`],\n\t};\n}\n\n/**\n * Validate README title against package name.\n *\n * @returns `true` if valid, or an error message string if invalid\n */\nfunction validateTitle(\n\treadmeContent: string,\n\tpackageName: string,\n): true | string {\n\tconst title = extractReadmeTitle(readmeContent);\n\tif (title !== packageName) {\n\t\treturn `README title \"${title ?? \"(none)\"}\" doesn't match package name \"${packageName}\"`;\n\t}\n\treturn true;\n}\n\n/**\n * Handle required content validation and auto-fix.\n */\nasync function handleRequiredContent(\n\treadmePath: string,\n\treadmeContent: string,\n\trequiredContent: string,\n\tresolve: boolean,\n): Promise<{ error?: string; fixed: boolean }> {\n\tif (readmeContent.includes(requiredContent)) {\n\t\treturn { fixed: false };\n\t}\n\n\tif (resolve) {\n\t\ttry {\n\t\t\tconst newContent = readmeContent.endsWith(\"\\n\")\n\t\t\t\t? `${readmeContent}${requiredContent}\\n`\n\t\t\t\t: `${readmeContent}\\n${requiredContent}\\n`;\n\t\t\tawait writeFile(readmePath, newContent);\n\t\t\treturn {\n\t\t\t\terror: `README missing required content: \"${requiredContent}\"`,\n\t\t\t\tfixed: true,\n\t\t\t};\n\t\t} catch {\n\t\t\t// Fall through to report error\n\t\t}\n\t}\n\n\treturn {\n\t\terror: `README missing required content: \"${requiredContent}\"`,\n\t\tfixed: false,\n\t};\n}\n\ninterface ValidationContext {\n\treadmePath: string;\n\treadmeContent: string;\n\tpackageName: string;\n\tfile: string;\n\tresolve: boolean;\n\trequireMatchingTitle: boolean;\n\trequiredContent: string | undefined;\n}\n\n/**\n * Validate existing README content.\n */\nasync function validateExistingReadme(\n\tctx: ValidationContext,\n): Promise<true | PolicyFailure | PolicyFixResult> {\n\tconst errors: string[] = [];\n\tlet wasFixed = false;\n\n\t// Check title matches package name\n\tif (ctx.requireMatchingTitle) {\n\t\tconst titleResult = validateTitle(ctx.readmeContent, ctx.packageName);\n\t\tif (titleResult !== true) {\n\t\t\terrors.push(titleResult);\n\t\t}\n\t}\n\n\t// Check required content\n\tif (ctx.requiredContent) {\n\t\tconst contentResult = await handleRequiredContent(\n\t\t\tctx.readmePath,\n\t\t\tctx.readmeContent,\n\t\t\tctx.requiredContent,\n\t\t\tctx.resolve,\n\t\t);\n\t\tif (contentResult.error) {\n\t\t\terrors.push(contentResult.error);\n\t\t}\n\t\tif (contentResult.fixed) {\n\t\t\twasFixed = true;\n\t\t}\n\t}\n\n\tif (errors.length === 0) {\n\t\treturn true;\n\t}\n\n\tif (wasFixed) {\n\t\treturn {\n\t\t\tname: \"PackageReadme\",\n\t\t\tfile: ctx.file,\n\t\t\tresolved: true,\n\t\t\tautoFixable: true,\n\t\t\terrorMessages: [errors.join(\"; \")],\n\t\t};\n\t}\n\n\treturn {\n\t\tname: \"PackageReadme\",\n\t\tfile: ctx.file,\n\t\tautoFixable:\n\t\t\tctx.requiredContent !== undefined &&\n\t\t\t!ctx.readmeContent.includes(ctx.requiredContent),\n\t\terrorMessages: [errors.join(\"; \")],\n\t};\n}\n\n/**\n * A repo policy that ensures each package has a README.md file with proper content.\n *\n * @remarks\n *\n * This policy validates that packages have documentation and can enforce:\n * 1. README.md file exists\n * 2. README title matches the package name\n * 3. README contains required content (e.g., trademark notices)\n *\n * The policy supports auto-fixing by creating a basic README with the package name as title.\n *\n * @example\n * ```typescript\n * import { makePolicy } from \"repopo\";\n * import { PackageReadme } from \"repopo/policies\";\n *\n * const config: RepopoConfig = {\n * policies: [\n * // Basic README validation\n * makePolicy(PackageReadme),\n *\n * // Require trademark notice in all READMEs\n * makePolicy(PackageReadme, {\n * requiredContent: \"## Trademark\",\n * }),\n * ],\n * };\n * ```\n *\n * @alpha\n */\nexport const PackageReadme = definePackagePolicy<\n\tPackageJson,\n\tPackageReadmeSettings | undefined\n>({\n\tname: \"PackageReadme\",\n\tdescription:\n\t\t\"Ensures each package has a README.md file with proper title and required content.\",\n\thandler: async (json, { file, root, resolve, config }) => {\n\t\tconst skipPrivate = config?.skipPrivate ?? true;\n\t\tconst requireMatchingTitle = config?.requireMatchingTitle ?? true;\n\t\tconst requiredContent = config?.requiredContent;\n\n\t\t// Skip private packages if configured to do so\n\t\tif (skipPrivate && json.private === true) {\n\t\t\treturn true;\n\t\t}\n\n\t\tconst packageName = json.name ?? \"unknown\";\n\t\tconst packageDir = path.dirname(path.resolve(root, file));\n\t\tconst readmePath = path.join(packageDir, \"README.md\");\n\n\t\t// Check if README exists\n\t\tif (!existsSync(readmePath)) {\n\t\t\treturn await handleMissingReadme(\n\t\t\t\treadmePath,\n\t\t\t\tpackageName,\n\t\t\t\tfile,\n\t\t\t\tresolve,\n\t\t\t\trequiredContent,\n\t\t\t);\n\t\t}\n\n\t\t// README exists, validate content\n\t\tconst readmeContent = await readFile(readmePath, \"utf-8\");\n\t\treturn await validateExistingReadme({\n\t\t\treadmePath,\n\t\t\treadmeContent,\n\t\t\tpackageName,\n\t\t\tfile,\n\t\t\tresolve,\n\t\t\trequireMatchingTitle,\n\t\t\trequiredContent,\n\t\t});\n\t},\n});\n"]}
|
|
1
|
+
{"version":3,"file":"PackageReadme.js","sourceRoot":"","sources":["../../src/policies/PackageReadme.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync } from \"node:fs\";\nimport { readFile, writeFile } from \"node:fs/promises\";\nimport path from \"pathe\";\nimport type { PackageJson } from \"type-fest\";\nimport type { PolicyFinding } from \"../policy.js\";\nimport { definePackagePolicy } from \"../policyDefiners/definePackagePolicy.js\";\n\n// Pre-compiled regex for performance\nconst LINE_SPLIT_REGEX = /\\r?\\n/;\nconst HEADING_REGEX = /^#\\s+(.+)$/;\n\n/**\n * Policy settings for the PackageReadme repo policy.\n *\n * @public\n */\nexport interface PackageReadmeSettings {\n\t/**\n\t * Whether to skip validation for private packages.\n\t *\n\t * @defaultValue true\n\t *\n\t * @remarks\n\t *\n\t * Private packages are typically not published and may not need a README file.\n\t * Set to `false` to require README files for all packages.\n\t */\n\tskipPrivate?: boolean;\n\n\t/**\n\t * Whether to require the README title to match the package name.\n\t *\n\t * @defaultValue true\n\t *\n\t * @remarks\n\t *\n\t * When enabled, the first line of the README must be `# package-name`.\n\t * This ensures consistency and helps users identify the package.\n\t */\n\trequireMatchingTitle?: boolean;\n\n\t/**\n\t * A string that must appear in the README content.\n\t *\n\t * @remarks\n\t *\n\t * Useful for ensuring all READMEs include required content like trademark\n\t * notices, disclaimers, or standard documentation sections.\n\t *\n\t * @example\n\t * ```typescript\n\t * requiredContent: \"## Trademark\"\n\t * ```\n\t */\n\trequiredContent?: string;\n}\n\n/**\n * Extract the title from a README file (first markdown H1 heading).\n *\n * @param content - The README content\n * @returns The title text (without the `# ` prefix) or `undefined` if not found\n */\nfunction extractReadmeTitle(content: string): string | undefined {\n\tconst lines = content.split(LINE_SPLIT_REGEX);\n\tfor (const line of lines) {\n\t\tconst match = HEADING_REGEX.exec(line);\n\t\tif (match?.[1]) {\n\t\t\treturn match[1].trim();\n\t\t}\n\t}\n\treturn undefined;\n}\n\n/**\n * Create a basic README content with package name as title.\n */\nfunction createReadmeContent(\n\tpackageName: string,\n\trequiredContent?: string,\n): string {\n\tlet content = `# ${packageName}\\n`;\n\tif (requiredContent) {\n\t\tcontent += `\\n${requiredContent}\\n`;\n\t}\n\treturn content;\n}\n\n/**\n * Handle case when README is missing.\n */\nasync function handleMissingReadme(\n\treadmePath: string,\n\tpackageName: string,\n\t_file: string,\n\tresolve: boolean,\n\trequiredContent?: string,\n): Promise<PolicyFinding[]> {\n\tif (resolve) {\n\t\ttry {\n\t\t\tawait writeFile(\n\t\t\t\treadmePath,\n\t\t\t\tcreateReadmeContent(packageName, requiredContent),\n\t\t\t);\n\t\t\treturn [`Created README.md for package \"${packageName}\"`].map(\n\t\t\t\t(message) => ({ message, fixStatus: \"fixed\" }),\n\t\t\t);\n\t\t} catch {\n\t\t\treturn [`Failed to create README.md for package \"${packageName}\"`].map(\n\t\t\t\t(message) => ({ message, fixStatus: \"failed\" }),\n\t\t\t);\n\t\t}\n\t}\n\treturn [`README.md missing for package \"${packageName}\"`].map((message) => ({\n\t\tmessage,\n\t\tfixStatus: \"available\",\n\t}));\n}\n\n/**\n * Validate README title against package name.\n *\n * @returns `true` if valid, or an error message string if invalid\n */\nfunction validateTitle(\n\treadmeContent: string,\n\tpackageName: string,\n): true | string {\n\tconst title = extractReadmeTitle(readmeContent);\n\tif (title !== packageName) {\n\t\treturn `README title \"${title ?? \"(none)\"}\" doesn't match package name \"${packageName}\"`;\n\t}\n\treturn true;\n}\n\n/**\n * Handle required content validation and auto-fix.\n */\nasync function handleRequiredContent(\n\treadmePath: string,\n\treadmeContent: string,\n\trequiredContent: string,\n\tresolve: boolean,\n): Promise<PolicyFinding | undefined> {\n\tif (readmeContent.includes(requiredContent)) {\n\t\treturn undefined;\n\t}\n\n\tif (resolve) {\n\t\ttry {\n\t\t\tconst newContent = readmeContent.endsWith(\"\\n\")\n\t\t\t\t? `${readmeContent}${requiredContent}\\n`\n\t\t\t\t: `${readmeContent}\\n${requiredContent}\\n`;\n\t\t\tawait writeFile(readmePath, newContent);\n\t\t\treturn {\n\t\t\t\tmessage: `README missing required content: \"${requiredContent}\"`,\n\t\t\t\tfixStatus: \"fixed\",\n\t\t\t};\n\t\t} catch {\n\t\t\t// Fall through to report error\n\t\t}\n\t}\n\n\treturn {\n\t\tmessage: `README missing required content: \"${requiredContent}\"`,\n\t\tfixStatus: resolve ? \"failed\" : \"available\",\n\t};\n}\n\ninterface ValidationContext {\n\treadmePath: string;\n\treadmeContent: string;\n\tpackageName: string;\n\tfile: string;\n\tresolve: boolean;\n\trequireMatchingTitle: boolean;\n\trequiredContent: string | undefined;\n}\n\n/**\n * Validate existing README content.\n */\nasync function validateExistingReadme(\n\tctx: ValidationContext,\n): Promise<PolicyFinding[]> {\n\tconst findings: PolicyFinding[] = [];\n\t// Check title matches package name\n\tif (ctx.requireMatchingTitle) {\n\t\tconst titleResult = validateTitle(ctx.readmeContent, ctx.packageName);\n\t\tif (titleResult !== true) {\n\t\t\tfindings.push({ message: titleResult });\n\t\t}\n\t}\n\n\t// Check required content\n\tif (ctx.requiredContent) {\n\t\tconst contentResult = await handleRequiredContent(\n\t\t\tctx.readmePath,\n\t\t\tctx.readmeContent,\n\t\t\tctx.requiredContent,\n\t\t\tctx.resolve,\n\t\t);\n\t\tif (contentResult) {\n\t\t\tfindings.push(contentResult);\n\t\t}\n\t}\n\treturn findings;\n}\n\n/**\n * A repo policy that ensures each package has a README.md file with proper content.\n *\n * @remarks\n *\n * This policy validates that packages have documentation and can enforce:\n * 1. README.md file exists\n * 2. README title matches the package name\n * 3. README contains required content (e.g., trademark notices)\n *\n * The policy supports auto-fixing by creating a basic README with the package name as title.\n *\n * @example\n * ```typescript\n * import { makePolicy } from \"repopo\";\n * import { PackageReadme } from \"repopo/policies\";\n *\n * const config: RepopoConfig = {\n * policies: [\n * // Basic README validation\n * makePolicy(PackageReadme, { instanceId: \"readme\" }),\n *\n * // Require trademark notice in all READMEs\n * makePolicy(PackageReadme, {\n * instanceId: \"readme-trademark\",\n * config: { requiredContent: \"## Trademark\" },\n * }),\n * ],\n * };\n * ```\n *\n * @public\n */\nexport const PackageReadme = definePackagePolicy<\n\tPackageJson,\n\tPackageReadmeSettings | undefined\n>({\n\tname: \"PackageReadme\",\n\tdescription:\n\t\t\"Ensures each package has a README.md file with proper title and required content.\",\n\thandler: async (json, { file, root, resolve, config }) => {\n\t\tconst skipPrivate = config?.skipPrivate ?? true;\n\t\tconst requireMatchingTitle = config?.requireMatchingTitle ?? true;\n\t\tconst requiredContent = config?.requiredContent;\n\n\t\t// Skip private packages if configured to do so\n\t\tif (skipPrivate && json.private === true) {\n\t\t\treturn [];\n\t\t}\n\n\t\tconst packageName = json.name ?? \"unknown\";\n\t\tconst packageDir = path.dirname(path.resolve(root, file));\n\t\tconst readmePath = path.join(packageDir, \"README.md\");\n\n\t\t// Check if README exists\n\t\tif (!existsSync(readmePath)) {\n\t\t\treturn await handleMissingReadme(\n\t\t\t\treadmePath,\n\t\t\t\tpackageName,\n\t\t\t\tfile,\n\t\t\t\tresolve,\n\t\t\t\trequiredContent,\n\t\t\t);\n\t\t}\n\n\t\t// README exists, validate content\n\t\tconst readmeContent = await readFile(readmePath, \"utf-8\");\n\t\treturn await validateExistingReadme({\n\t\t\treadmePath,\n\t\t\treadmeContent,\n\t\t\tpackageName,\n\t\t\tfile,\n\t\t\tresolve,\n\t\t\trequireMatchingTitle,\n\t\t\trequiredContent,\n\t\t});\n\t},\n});\n"]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Configuration for validating that a script's body contains required substrings.
|
|
3
3
|
*
|
|
4
|
-
* @
|
|
4
|
+
* @public
|
|
5
5
|
*/
|
|
6
6
|
export interface ScriptMustContainRule {
|
|
7
7
|
/**
|
|
@@ -17,7 +17,7 @@ export interface ScriptMustContainRule {
|
|
|
17
17
|
/**
|
|
18
18
|
* Configuration for conditional script requirements.
|
|
19
19
|
*
|
|
20
|
-
* @
|
|
20
|
+
* @public
|
|
21
21
|
*/
|
|
22
22
|
export interface ConditionalScriptRule {
|
|
23
23
|
/**
|
|
@@ -56,13 +56,13 @@ export interface ConditionalScriptRule {
|
|
|
56
56
|
* { lint: "biome lint ." }
|
|
57
57
|
* ```
|
|
58
58
|
*
|
|
59
|
-
* @
|
|
59
|
+
* @public
|
|
60
60
|
*/
|
|
61
61
|
export type RequiredScriptEntry = string | Record<string, string>;
|
|
62
62
|
/**
|
|
63
63
|
* Policy settings for the PackageScripts repo policy.
|
|
64
64
|
*
|
|
65
|
-
* @
|
|
65
|
+
* @public
|
|
66
66
|
*/
|
|
67
67
|
export interface PackageScriptsSettings {
|
|
68
68
|
/**
|
|
@@ -180,29 +180,33 @@ export interface PackageScriptsSettings {
|
|
|
180
180
|
* const config: RepopoConfig = {
|
|
181
181
|
* policies: [
|
|
182
182
|
* makePolicy(PackageScripts, {
|
|
183
|
-
*
|
|
184
|
-
*
|
|
185
|
-
*
|
|
186
|
-
*
|
|
187
|
-
*
|
|
188
|
-
*
|
|
189
|
-
*
|
|
190
|
-
*
|
|
191
|
-
*
|
|
192
|
-
*
|
|
183
|
+
* config: {
|
|
184
|
+
* // Scripts that must exist
|
|
185
|
+
* must: [
|
|
186
|
+
* "test", // Must exist, no auto-fix
|
|
187
|
+
* { build: "tsc" }, // Must exist, auto-fix available
|
|
188
|
+
* { lint: "biome lint ." }, // Must exist, auto-fix available
|
|
189
|
+
* ],
|
|
190
|
+
* // Scripts that must exist AND match exactly
|
|
191
|
+
* exact: {
|
|
192
|
+
* clean: "rimraf dist esm",
|
|
193
|
+
* format: "biome format --write .",
|
|
194
|
+
* },
|
|
195
|
+
* // If "build" exists, "clean" must also exist (with inline auto-fix default)
|
|
196
|
+
* conditionalRequired: [{ ifPresent: "build", requires: [{ clean: "rimraf dist" }] }],
|
|
197
|
+
* // Script body must contain these substrings
|
|
198
|
+
* scriptMustContain: [{ script: "build", mustContain: ["tsc"] }],
|
|
199
|
+
* // Only one of these can exist
|
|
200
|
+
* mutuallyExclusive: [["lint:eslint", "lint:biome"]],
|
|
193
201
|
* },
|
|
194
|
-
* // If "build" exists, "clean" must also exist (with inline auto-fix default)
|
|
195
|
-
* conditionalRequired: [{ ifPresent: "build", requires: [{ clean: "rimraf dist" }] }],
|
|
196
|
-
* // Script body must contain these substrings
|
|
197
|
-
* scriptMustContain: [{ script: "build", mustContain: ["tsc"] }],
|
|
198
|
-
* // Only one of these can exist
|
|
199
|
-
* mutuallyExclusive: [["lint:eslint", "lint:biome"]],
|
|
200
202
|
* }),
|
|
201
203
|
* ],
|
|
202
204
|
* };
|
|
203
205
|
* ```
|
|
204
206
|
*
|
|
205
|
-
* @
|
|
207
|
+
* @public
|
|
206
208
|
*/
|
|
207
|
-
export declare const PackageScripts: import("../policy.js").
|
|
209
|
+
export declare const PackageScripts: import("../policy.js").FilePolicyDefinition<PackageScriptsSettings | undefined> & {
|
|
210
|
+
requiresConfig: true;
|
|
211
|
+
};
|
|
208
212
|
//# sourceMappingURL=PackageScripts.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PackageScripts.d.ts","sourceRoot":"","sources":["../../src/policies/PackageScripts.ts"],"names":[],"mappings":"AAKA;;;;GAIG;AACH,MAAM,WAAW,qBAAqB;IACrC;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IAEf;;;OAGG;IACH,WAAW,EAAE,MAAM,EAAE,CAAC;CACtB;AAED;;;;GAIG;AACH,MAAM,WAAW,qBAAqB;IACrC;;;OAGG;IACH,SAAS,EAAE,MAAM,CAAC;IAElB;;;;;;;;;;;;;;OAcG;IACH,QAAQ,EAAE,mBAAmB,EAAE,CAAC;CAChC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,MAAM,mBAAmB,GAAG,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAElE;;;;GAIG;AACH,MAAM,WAAW,sBAAsB;IACtC;;;;;;;;;;;;;;;;;;OAkBG;IACH,IAAI,CAAC,EAAE,mBAAmB,EAAE,CAAC;IAE7B;;;;;;;;;;;;;;;;OAgBG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAE/B;;;;;;;;;;;;OAYG;IACH,iBAAiB,CAAC,EAAE,MAAM,EAAE,EAAE,CAAC;IAE/B;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,mBAAmB,CAAC,EAAE,qBAAqB,EAAE,CAAC;IAE9C;;;;;;;;;;;;;;;;;;OAkBG;IACH,iBAAiB,CAAC,EAAE,qBAAqB,EAAE,CAAC;CAC5C;AAseD
|
|
1
|
+
{"version":3,"file":"PackageScripts.d.ts","sourceRoot":"","sources":["../../src/policies/PackageScripts.ts"],"names":[],"mappings":"AAKA;;;;GAIG;AACH,MAAM,WAAW,qBAAqB;IACrC;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IAEf;;;OAGG;IACH,WAAW,EAAE,MAAM,EAAE,CAAC;CACtB;AAED;;;;GAIG;AACH,MAAM,WAAW,qBAAqB;IACrC;;;OAGG;IACH,SAAS,EAAE,MAAM,CAAC;IAElB;;;;;;;;;;;;;;OAcG;IACH,QAAQ,EAAE,mBAAmB,EAAE,CAAC;CAChC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,MAAM,mBAAmB,GAAG,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAElE;;;;GAIG;AACH,MAAM,WAAW,sBAAsB;IACtC;;;;;;;;;;;;;;;;;;OAkBG;IACH,IAAI,CAAC,EAAE,mBAAmB,EAAE,CAAC;IAE7B;;;;;;;;;;;;;;;;OAgBG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAE/B;;;;;;;;;;;;OAYG;IACH,iBAAiB,CAAC,EAAE,MAAM,EAAE,EAAE,CAAC;IAE/B;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,mBAAmB,CAAC,EAAE,qBAAqB,EAAE,CAAC;IAE9C;;;;;;;;;;;;;;;;;;OAkBG;IACH,iBAAiB,CAAC,EAAE,qBAAqB,EAAE,CAAC;CAC5C;AAseD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AACH,eAAO,MAAM,cAAc;;CA+DzB,CAAC"}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import jsonfile from "jsonfile";
|
|
2
2
|
import { resolve as resolvePath } from "pathe";
|
|
3
3
|
import { definePackagePolicy } from "../policyDefiners/definePackagePolicy.js";
|
|
4
|
-
const POLICY_NAME = "PackageScripts";
|
|
5
4
|
/**
|
|
6
5
|
* Extracts script name and optional default value from a RequiredScriptEntry.
|
|
7
6
|
*/
|
|
@@ -318,42 +317,45 @@ function runAllValidations(config, scripts, defaultScripts) {
|
|
|
318
317
|
* const config: RepopoConfig = {
|
|
319
318
|
* policies: [
|
|
320
319
|
* makePolicy(PackageScripts, {
|
|
321
|
-
*
|
|
322
|
-
*
|
|
323
|
-
*
|
|
324
|
-
*
|
|
325
|
-
*
|
|
326
|
-
*
|
|
327
|
-
*
|
|
328
|
-
*
|
|
329
|
-
*
|
|
330
|
-
*
|
|
320
|
+
* config: {
|
|
321
|
+
* // Scripts that must exist
|
|
322
|
+
* must: [
|
|
323
|
+
* "test", // Must exist, no auto-fix
|
|
324
|
+
* { build: "tsc" }, // Must exist, auto-fix available
|
|
325
|
+
* { lint: "biome lint ." }, // Must exist, auto-fix available
|
|
326
|
+
* ],
|
|
327
|
+
* // Scripts that must exist AND match exactly
|
|
328
|
+
* exact: {
|
|
329
|
+
* clean: "rimraf dist esm",
|
|
330
|
+
* format: "biome format --write .",
|
|
331
|
+
* },
|
|
332
|
+
* // If "build" exists, "clean" must also exist (with inline auto-fix default)
|
|
333
|
+
* conditionalRequired: [{ ifPresent: "build", requires: [{ clean: "rimraf dist" }] }],
|
|
334
|
+
* // Script body must contain these substrings
|
|
335
|
+
* scriptMustContain: [{ script: "build", mustContain: ["tsc"] }],
|
|
336
|
+
* // Only one of these can exist
|
|
337
|
+
* mutuallyExclusive: [["lint:eslint", "lint:biome"]],
|
|
331
338
|
* },
|
|
332
|
-
* // If "build" exists, "clean" must also exist (with inline auto-fix default)
|
|
333
|
-
* conditionalRequired: [{ ifPresent: "build", requires: [{ clean: "rimraf dist" }] }],
|
|
334
|
-
* // Script body must contain these substrings
|
|
335
|
-
* scriptMustContain: [{ script: "build", mustContain: ["tsc"] }],
|
|
336
|
-
* // Only one of these can exist
|
|
337
|
-
* mutuallyExclusive: [["lint:eslint", "lint:biome"]],
|
|
338
339
|
* }),
|
|
339
340
|
* ],
|
|
340
341
|
* };
|
|
341
342
|
* ```
|
|
342
343
|
*
|
|
343
|
-
* @
|
|
344
|
+
* @public
|
|
344
345
|
*/
|
|
345
346
|
export const PackageScripts = definePackagePolicy({
|
|
346
347
|
name: "PackageScripts",
|
|
348
|
+
requiresConfig: true,
|
|
347
349
|
description: "Validates package.json scripts based on configurable rules including required scripts, exact content, and conditional requirements.",
|
|
348
350
|
handler: async (json, { file, root, resolve, config }) => {
|
|
349
351
|
if (config === undefined) {
|
|
350
|
-
return
|
|
352
|
+
return [];
|
|
351
353
|
}
|
|
352
354
|
const scripts = Object.hasOwn(json, "scripts") ? json.scripts : {};
|
|
353
355
|
const defaultScripts = buildDefaultsMap(config);
|
|
354
356
|
const { errors, missingWithDefaults, mismatchedScripts } = runAllValidations(config, scripts, defaultScripts);
|
|
355
357
|
if (errors.length === 0) {
|
|
356
|
-
return
|
|
358
|
+
return [];
|
|
357
359
|
}
|
|
358
360
|
const hasAutoFixableErrors = missingWithDefaults.length > 0 || mismatchedScripts.length > 0;
|
|
359
361
|
if (resolve && hasAutoFixableErrors) {
|
|
@@ -361,27 +363,28 @@ export const PackageScripts = definePackagePolicy({
|
|
|
361
363
|
try {
|
|
362
364
|
await jsonfile.writeFile(resolvePath(root, file), { ...json, scripts: updatedScripts }, { spaces: 2 });
|
|
363
365
|
const postFixErrors = revalidateAfterFix(config, updatedScripts);
|
|
364
|
-
return
|
|
365
|
-
name
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
};
|
|
366
|
+
return [
|
|
367
|
+
...fixedScripts.map((name) => ({
|
|
368
|
+
message: `Fixed scripts: ${name}`,
|
|
369
|
+
fixStatus: "fixed",
|
|
370
|
+
})),
|
|
371
|
+
...postFixErrors.map((message) => ({
|
|
372
|
+
message: `Remaining errors:\n${message}`,
|
|
373
|
+
})),
|
|
374
|
+
];
|
|
374
375
|
}
|
|
375
376
|
catch {
|
|
376
377
|
// Fall through to return regular failure
|
|
377
378
|
}
|
|
378
379
|
}
|
|
379
|
-
return {
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
380
|
+
return errors.map((message) => ({
|
|
381
|
+
message,
|
|
382
|
+
fixStatus: hasAutoFixableErrors
|
|
383
|
+
? resolve
|
|
384
|
+
? "failed"
|
|
385
|
+
: "available"
|
|
386
|
+
: undefined,
|
|
387
|
+
}));
|
|
385
388
|
},
|
|
386
389
|
});
|
|
387
390
|
//# sourceMappingURL=PackageScripts.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PackageScripts.js","sourceRoot":"","sources":["../../src/policies/PackageScripts.ts"],"names":[],"mappings":"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","sourcesContent":["import jsonfile from \"jsonfile\";\nimport { resolve as resolvePath } from \"pathe\";\nimport type { PackageJson } from \"type-fest\";\nimport { definePackagePolicy } from \"../policyDefiners/definePackagePolicy.js\";\n\n/**\n * Configuration for validating that a script's body contains required substrings.\n *\n * @alpha\n */\nexport interface ScriptMustContainRule {\n\t/**\n\t * The name of the script to validate.\n\t */\n\tscript: string;\n\n\t/**\n\t * Substrings that must appear in the script body.\n\t * All substrings must be present for the script to pass validation.\n\t */\n\tmustContain: string[];\n}\n\n/**\n * Configuration for conditional script requirements.\n *\n * @alpha\n */\nexport interface ConditionalScriptRule {\n\t/**\n\t * The script that triggers the requirement.\n\t * If this script exists, then `requires` must also exist.\n\t */\n\tifPresent: string;\n\n\t/**\n\t * Scripts that must be present if `ifPresent` script exists.\n\t *\n\t * @remarks\n\t *\n\t * Each entry can be:\n\t * - A string: Script must exist, no auto-fix available\n\t * - An object `{ scriptName: \"default value\" }`: Script must exist, auto-fix adds the\n\t * default value if missing\n\t *\n\t * @example\n\t * ```typescript\n\t * requires: [\"test\", { clean: \"rimraf dist\" }]\n\t * ```\n\t */\n\trequires: RequiredScriptEntry[];\n}\n\n/**\n * A required script entry. Can be either:\n * - A string (script name only, no default value)\n * - An object with a single key-value pair (script name → default value)\n *\n * @example\n * ```typescript\n * // Script must exist, no auto-fix available\n * \"test\"\n *\n * // Script must exist, auto-fix will add this value if missing\n * { lint: \"biome lint .\" }\n * ```\n *\n * @alpha\n */\nexport type RequiredScriptEntry = string | Record<string, string>;\n\n/**\n * Policy settings for the PackageScripts repo policy.\n *\n * @alpha\n */\nexport interface PackageScriptsSettings {\n\t/**\n\t * Scripts that must be present in package.json.\n\t *\n\t * @remarks\n\t *\n\t * Each entry can be:\n\t * - A string: Script must exist, no auto-fix available, content not validated\n\t * - An object `{ scriptName: \"default value\" }`: Script must exist, auto-fix adds the\n\t * default value if missing, but existing scripts with different content still pass\n\t *\n\t * @example\n\t * ```typescript\n\t * must: [\n\t * \"test\", // Must exist, no auto-fix\n\t * { lint: \"biome lint .\" }, // Must exist, auto-fix available\n\t * { clean: \"rimraf dist esm\" }, // Must exist, auto-fix available\n\t * ]\n\t * ```\n\t */\n\tmust?: RequiredScriptEntry[];\n\n\t/**\n\t * Scripts that must exist AND have exact content.\n\t *\n\t * @remarks\n\t *\n\t * Unlike {@link PackageScriptsSettings.must}, scripts in `exact` are validated against\n\t * the specified value. If a script exists but has different content, it fails validation.\n\t * Both missing and mismatched scripts are auto-fixable.\n\t *\n\t * @example\n\t * ```typescript\n\t * exact: {\n\t * clean: \"rimraf dist esm\",\n\t * format: \"biome format --write .\",\n\t * }\n\t * ```\n\t */\n\texact?: Record<string, string>;\n\n\t/**\n\t * List of mutually exclusive script groups. Each group is an array of script names\n\t * where at most one script from the group can be present.\n\t *\n\t * Packages can have zero scripts from the group (all optional), but cannot have\n\t * multiple scripts from the same group.\n\t *\n\t * @example\n\t * ```typescript\n\t * // Allow \"test:unit\" OR \"test:vitest\" OR neither, but not both\n\t * mutuallyExclusive: [[\"test:unit\", \"test:vitest\"]]\n\t * ```\n\t */\n\tmutuallyExclusive?: string[][];\n\n\t/**\n\t * Conditional script requirements. If a script is present, other scripts must also exist.\n\t *\n\t * @remarks\n\t *\n\t * This is useful for enforcing that packages with a \"build\" script also have a \"clean\" script,\n\t * or packages with \"test\" also have \"test:coverage\".\n\t *\n\t * The `requires` field accepts the same format as {@link PackageScriptsSettings.must}:\n\t * - Strings: Script must exist, no auto-fix available\n\t * - Objects `{ scriptName: \"default\" }`: Script must exist, auto-fix adds the default if missing\n\t *\n\t * @example\n\t * ```typescript\n\t * // If \"build\" exists, \"clean\" must also exist (with auto-fix default)\n\t * conditionalRequired: [{ ifPresent: \"build\", requires: [{ clean: \"rimraf dist\" }] }]\n\t *\n\t * // Mixed: \"test\" required without default, \"clean\" with default\n\t * conditionalRequired: [{ ifPresent: \"build\", requires: [\"test\", { clean: \"rimraf dist\" }] }]\n\t * ```\n\t */\n\tconditionalRequired?: ConditionalScriptRule[];\n\n\t/**\n\t * Rules for validating script body content.\n\t *\n\t * @remarks\n\t *\n\t * This allows you to ensure that specific scripts contain required commands or patterns\n\t * without requiring an exact match. For example, requiring that the \"clean\" script uses\n\t * \"rimraf\" or that \"build\" includes \"tsc\".\n\t *\n\t * For exact content matching, use {@link PackageScriptsSettings.exact} instead.\n\t *\n\t * @example\n\t * ```typescript\n\t * scriptMustContain: [\n\t * { script: \"clean\", mustContain: [\"rimraf\"] },\n\t * { script: \"build\", mustContain: [\"tsc\"] },\n\t * ]\n\t * ```\n\t */\n\tscriptMustContain?: ScriptMustContainRule[];\n}\n\nconst POLICY_NAME = \"PackageScripts\";\n\n/**\n * Extracts script name and optional default value from a RequiredScriptEntry.\n */\nfunction parseRequiredEntry(entry: RequiredScriptEntry): {\n\tname: string;\n\tdefaultValue?: string;\n} {\n\tif (typeof entry === \"string\") {\n\t\treturn { name: entry };\n\t}\n\tconst keys = Object.keys(entry);\n\tif (keys.length !== 1) {\n\t\tthrow new Error(\n\t\t\t`Invalid required script entry: expected single key-value pair, got ${keys.length} keys`,\n\t\t);\n\t}\n\t// biome-ignore lint/style/noNonNullAssertion: we verified length is 1\n\tconst name = keys[0]!;\n\tconst defaultValue = entry[name];\n\t// Only include defaultValue if it's defined (exactOptionalPropertyTypes)\n\treturn defaultValue !== undefined ? { name, defaultValue } : { name };\n}\n\n/**\n * Extract defaults from RequiredScriptEntry array.\n */\nfunction extractDefaultsFromEntries(\n\tentries: RequiredScriptEntry[],\n\tdefaults: Record<string, string>,\n): void {\n\tfor (const entry of entries) {\n\t\tconst { name, defaultValue } = parseRequiredEntry(entry);\n\t\tif (defaultValue !== undefined) {\n\t\t\tdefaults[name] = defaultValue;\n\t\t}\n\t}\n}\n\n/**\n * Builds a map of script names to their default values from must, conditionalRequired, and exact configs.\n */\nfunction buildDefaultsMap(\n\tconfig: PackageScriptsSettings,\n): Record<string, string> {\n\tconst defaults: Record<string, string> = {};\n\n\t// Add defaults from must entries\n\tif (config.must) {\n\t\textractDefaultsFromEntries(config.must, defaults);\n\t}\n\n\t// Add defaults from conditionalRequired entries\n\tif (config.conditionalRequired) {\n\t\tfor (const rule of config.conditionalRequired) {\n\t\t\textractDefaultsFromEntries(rule.requires, defaults);\n\t\t}\n\t}\n\n\t// Add defaults from exact (these override must/conditionalRequired if both specified)\n\tif (config.exact) {\n\t\tObject.assign(defaults, config.exact);\n\t}\n\n\treturn defaults;\n}\n\n/**\n * Validates mutually exclusive script groups and returns error messages for violations.\n */\nfunction validateMutuallyExclusiveScripts(\n\tgroups: string[][],\n\tscripts: PackageJson[\"scripts\"],\n): string[] {\n\tconst errors: string[] = [];\n\n\tfor (const group of groups) {\n\t\tconst presentScripts = group.filter(\n\t\t\t(scriptName) => scripts && Object.hasOwn(scripts, scriptName),\n\t\t);\n\n\t\t// Only fail if MORE than one script from the group is present\n\t\tif (presentScripts.length > 1) {\n\t\t\terrors.push(\n\t\t\t\t`Scripts are mutually exclusive, but found ${presentScripts.length}: ${presentScripts.join(\", \")}`,\n\t\t\t);\n\t\t}\n\t}\n\n\treturn errors;\n}\n\n/**\n * Validates conditional script requirements (if X exists, Y must also exist).\n */\nfunction validateConditionalRequirements(\n\trules: ConditionalScriptRule[],\n\tscripts: PackageJson[\"scripts\"],\n): string[] {\n\tconst errors: string[] = [];\n\n\tfor (const rule of rules) {\n\t\t// Check if the triggering script exists\n\t\tif (scripts && Object.hasOwn(scripts, rule.ifPresent)) {\n\t\t\t// Find missing required scripts\n\t\t\tconst missingRequired: string[] = [];\n\t\t\tfor (const entry of rule.requires) {\n\t\t\t\tconst { name } = parseRequiredEntry(entry);\n\t\t\t\tif (!Object.hasOwn(scripts, name)) {\n\t\t\t\t\tmissingRequired.push(name);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (missingRequired.length > 0) {\n\t\t\t\terrors.push(\n\t\t\t\t\t`Script \"${rule.ifPresent}\" is present, but required companion script(s) missing: ${missingRequired.join(\", \")}`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn errors;\n}\n\n/**\n * Validates that script bodies contain required substrings.\n */\nfunction validateScriptContents(\n\trules: ScriptMustContainRule[],\n\tscripts: PackageJson[\"scripts\"],\n): string[] {\n\tconst errors: string[] = [];\n\n\tfor (const rule of rules) {\n\t\t// Only validate if the script exists\n\t\tif (scripts && Object.hasOwn(scripts, rule.script)) {\n\t\t\tconst scriptBody = scripts[rule.script];\n\t\t\tif (typeof scriptBody === \"string\") {\n\t\t\t\tconst missingSubstrings = rule.mustContain.filter(\n\t\t\t\t\t(substring) => !scriptBody.includes(substring),\n\t\t\t\t);\n\n\t\t\t\tif (missingSubstrings.length > 0) {\n\t\t\t\t\terrors.push(\n\t\t\t\t\t\t`Script \"${rule.script}\" must contain: ${missingSubstrings.join(\", \")}`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn errors;\n}\n\n/**\n * Result of validating required scripts.\n */\ninterface MustScriptsResult {\n\terrors: string[];\n\tmissingWithDefaults: string[];\n}\n\n/**\n * Validates required scripts from 'must' config.\n */\nfunction validateMustScripts(\n\tmust: RequiredScriptEntry[],\n\tscripts: PackageJson[\"scripts\"],\n): MustScriptsResult {\n\tconst missingScripts: string[] = [];\n\tconst missingWithDefaults: string[] = [];\n\n\tfor (const entry of must) {\n\t\tconst { name, defaultValue } = parseRequiredEntry(entry);\n\t\tif (!(scripts && Object.hasOwn(scripts, name))) {\n\t\t\tmissingScripts.push(name);\n\t\t\tif (defaultValue !== undefined) {\n\t\t\t\tmissingWithDefaults.push(name);\n\t\t\t}\n\t\t}\n\t}\n\n\tconst errors: string[] = [];\n\tif (missingScripts.length > 0) {\n\t\terrors.push(`Missing required scripts:\\n\\t${missingScripts.join(\"\\n\\t\")}`);\n\t}\n\n\treturn { errors, missingWithDefaults };\n}\n\n/**\n * Result of validating exact scripts.\n */\ninterface ExactScriptsResult {\n\terrors: string[];\n\tmissingWithDefaults: string[];\n\tmismatchedScripts: string[];\n}\n\n/**\n * Validates scripts from 'exact' config - must exist AND match exactly.\n */\nfunction validateExactScripts(\n\texact: Record<string, string>,\n\tscripts: PackageJson[\"scripts\"],\n): ExactScriptsResult {\n\tconst errors: string[] = [];\n\tconst missingWithDefaults: string[] = [];\n\tconst mismatchedScripts: string[] = [];\n\n\tfor (const [scriptName, expectedValue] of Object.entries(exact)) {\n\t\tif (scripts && Object.hasOwn(scripts, scriptName)) {\n\t\t\tconst actualValue = scripts[scriptName];\n\t\t\tif (actualValue !== expectedValue) {\n\t\t\t\tmismatchedScripts.push(scriptName);\n\t\t\t\terrors.push(\n\t\t\t\t\t`Script \"${scriptName}\" must be \"${expectedValue}\", but found \"${actualValue}\"`,\n\t\t\t\t);\n\t\t\t}\n\t\t} else {\n\t\t\tmissingWithDefaults.push(scriptName);\n\t\t\terrors.push(`Missing required script: ${scriptName}`);\n\t\t}\n\t}\n\n\treturn { errors, missingWithDefaults, mismatchedScripts };\n}\n\n/**\n * Result of validating conditional requirements.\n */\ninterface ConditionalResult {\n\terrors: string[];\n\tmissingWithDefaults: string[];\n}\n\n/**\n * Find missing scripts from a conditional rule's requirements.\n */\nfunction findMissingFromRule(\n\trule: ConditionalScriptRule,\n\tscripts: PackageJson[\"scripts\"],\n): string[] {\n\treturn rule.requires\n\t\t.map((entry) => parseRequiredEntry(entry).name)\n\t\t.filter((name) => !(scripts && Object.hasOwn(scripts, name)));\n}\n\n/**\n * Validates conditional requirements and tracks missing scripts with defaults.\n */\nfunction validateConditionalWithDefaults(\n\trules: ConditionalScriptRule[],\n\tscripts: PackageJson[\"scripts\"],\n\tdefaultScripts: Record<string, string>,\n\texistingMissingWithDefaults: string[],\n): ConditionalResult {\n\tconst errors: string[] = [];\n\tconst missingWithDefaults: string[] = [];\n\tconst allTracked = new Set(existingMissingWithDefaults);\n\n\tfor (const rule of rules) {\n\t\tif (!(scripts && Object.hasOwn(scripts, rule.ifPresent))) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst missingRequired = findMissingFromRule(rule, scripts);\n\n\t\tfor (const scriptName of missingRequired) {\n\t\t\tif (scriptName in defaultScripts && !allTracked.has(scriptName)) {\n\t\t\t\tmissingWithDefaults.push(scriptName);\n\t\t\t\tallTracked.add(scriptName);\n\t\t\t}\n\t\t}\n\n\t\tif (missingRequired.length > 0) {\n\t\t\terrors.push(\n\t\t\t\t`Script \"${rule.ifPresent}\" is present, but required companion script(s) missing: ${missingRequired.join(\", \")}`,\n\t\t\t);\n\t\t}\n\t}\n\n\treturn { errors, missingWithDefaults };\n}\n\n/**\n * Apply fixes to scripts and return the updated scripts object.\n */\nfunction applyScriptFixes(\n\tscripts: PackageJson[\"scripts\"],\n\tmissingScriptsWithDefaults: string[],\n\tmismatchedScripts: string[],\n\tdefaultScripts: Record<string, string>,\n): { updatedScripts: Record<string, string>; fixedScripts: string[] } {\n\tconst updatedScripts: Record<string, string> = {};\n\n\t// Copy existing scripts, filtering out undefined values\n\tif (scripts) {\n\t\tfor (const [key, value] of Object.entries(scripts)) {\n\t\t\tif (value !== undefined) {\n\t\t\t\tupdatedScripts[key] = value;\n\t\t\t}\n\t\t}\n\t}\n\n\tconst fixedScripts: string[] = [];\n\n\tfor (const scriptName of missingScriptsWithDefaults) {\n\t\tconst defaultValue = defaultScripts[scriptName];\n\t\tif (defaultValue !== undefined) {\n\t\t\tupdatedScripts[scriptName] = defaultValue;\n\t\t\tfixedScripts.push(scriptName);\n\t\t}\n\t}\n\n\tfor (const scriptName of mismatchedScripts) {\n\t\tconst defaultValue = defaultScripts[scriptName];\n\t\tif (defaultValue !== undefined && !fixedScripts.includes(scriptName)) {\n\t\t\tupdatedScripts[scriptName] = defaultValue;\n\t\t\tfixedScripts.push(scriptName);\n\t\t}\n\t}\n\n\treturn { updatedScripts, fixedScripts };\n}\n\n/**\n * Validate 'must' scripts and return any remaining missing scripts.\n */\nfunction validateRemainingMustScripts(\n\tmust: RequiredScriptEntry[],\n\tscripts: Record<string, string>,\n): string[] {\n\tconst stillMissing = must\n\t\t.map((entry) => parseRequiredEntry(entry).name)\n\t\t.filter((name) => !Object.hasOwn(scripts, name));\n\n\tif (stillMissing.length > 0) {\n\t\treturn [`Missing required scripts:\\n\\t${stillMissing.join(\"\\n\\t\")}`];\n\t}\n\treturn [];\n}\n\n/**\n * Validate 'exact' scripts and return any errors.\n */\nfunction validateRemainingExactScripts(\n\texact: Record<string, string>,\n\tscripts: Record<string, string>,\n): string[] {\n\tconst errors: string[] = [];\n\tfor (const [scriptName, expectedValue] of Object.entries(exact)) {\n\t\tif (!Object.hasOwn(scripts, scriptName)) {\n\t\t\terrors.push(`Missing required script: ${scriptName}`);\n\t\t} else if (scripts[scriptName] !== expectedValue) {\n\t\t\terrors.push(\n\t\t\t\t`Script \"${scriptName}\" must be \"${expectedValue}\", but found \"${scripts[scriptName]}\"`,\n\t\t\t);\n\t\t}\n\t}\n\treturn errors;\n}\n\n/**\n * Re-validate all rules after applying fixes.\n */\nfunction revalidateAfterFix(\n\tconfig: PackageScriptsSettings,\n\tupdatedScripts: Record<string, string>,\n): string[] {\n\tconst errors: string[] = [];\n\n\tif (\n\t\tconfig.mutuallyExclusive !== undefined &&\n\t\tconfig.mutuallyExclusive.length > 0\n\t) {\n\t\terrors.push(\n\t\t\t...validateMutuallyExclusiveScripts(\n\t\t\t\tconfig.mutuallyExclusive,\n\t\t\t\tupdatedScripts,\n\t\t\t),\n\t\t);\n\t}\n\n\tif (\n\t\tconfig.scriptMustContain !== undefined &&\n\t\tconfig.scriptMustContain.length > 0\n\t) {\n\t\terrors.push(\n\t\t\t...validateScriptContents(config.scriptMustContain, updatedScripts),\n\t\t);\n\t}\n\n\tif (config.must !== undefined && config.must.length > 0) {\n\t\terrors.push(...validateRemainingMustScripts(config.must, updatedScripts));\n\t}\n\n\tif (config.exact !== undefined && Object.keys(config.exact).length > 0) {\n\t\terrors.push(...validateRemainingExactScripts(config.exact, updatedScripts));\n\t}\n\n\tif (\n\t\tconfig.conditionalRequired !== undefined &&\n\t\tconfig.conditionalRequired.length > 0\n\t) {\n\t\terrors.push(\n\t\t\t...validateConditionalRequirements(\n\t\t\t\tconfig.conditionalRequired,\n\t\t\t\tupdatedScripts,\n\t\t\t),\n\t\t);\n\t}\n\n\treturn errors;\n}\n\n/**\n * Result of running all script validations.\n */\ninterface AllValidationsResult {\n\terrors: string[];\n\tmissingWithDefaults: string[];\n\tmismatchedScripts: string[];\n}\n\n/**\n * Run all script validations and aggregate results.\n */\nfunction runAllValidations(\n\tconfig: PackageScriptsSettings,\n\tscripts: PackageJson[\"scripts\"],\n\tdefaultScripts: Record<string, string>,\n): AllValidationsResult {\n\tconst errors: string[] = [];\n\tconst missingWithDefaults: string[] = [];\n\tlet mismatchedScripts: string[] = [];\n\n\tif (config.must !== undefined && config.must.length > 0) {\n\t\tconst result = validateMustScripts(config.must, scripts);\n\t\terrors.push(...result.errors);\n\t\tmissingWithDefaults.push(...result.missingWithDefaults);\n\t}\n\n\tif (config.exact !== undefined && Object.keys(config.exact).length > 0) {\n\t\tconst result = validateExactScripts(config.exact, scripts);\n\t\terrors.push(...result.errors);\n\t\tmissingWithDefaults.push(...result.missingWithDefaults);\n\t\tmismatchedScripts = result.mismatchedScripts;\n\t}\n\n\tif (\n\t\tconfig.mutuallyExclusive !== undefined &&\n\t\tconfig.mutuallyExclusive.length > 0\n\t) {\n\t\terrors.push(\n\t\t\t...validateMutuallyExclusiveScripts(config.mutuallyExclusive, scripts),\n\t\t);\n\t}\n\n\tif (\n\t\tconfig.conditionalRequired !== undefined &&\n\t\tconfig.conditionalRequired.length > 0\n\t) {\n\t\tconst result = validateConditionalWithDefaults(\n\t\t\tconfig.conditionalRequired,\n\t\t\tscripts,\n\t\t\tdefaultScripts,\n\t\t\tmissingWithDefaults,\n\t\t);\n\t\terrors.push(...result.errors);\n\t\tmissingWithDefaults.push(...result.missingWithDefaults);\n\t}\n\n\tif (\n\t\tconfig.scriptMustContain !== undefined &&\n\t\tconfig.scriptMustContain.length > 0\n\t) {\n\t\terrors.push(...validateScriptContents(config.scriptMustContain, scripts));\n\t}\n\n\treturn { errors, missingWithDefaults, mismatchedScripts };\n}\n\n/**\n * A RepoPolicy that validates package.json scripts based on configurable rules.\n *\n * @remarks\n *\n * This policy provides comprehensive script validation including:\n * - Required scripts that must be present (with optional defaults for auto-fix)\n * - Exact script content enforcement\n * - Mutually exclusive script groups (at most one can exist)\n * - Conditional requirements (if X exists, Y must also exist)\n * - Script body content validation (script must contain specific substrings)\n *\n * @example\n * ```typescript\n * import { makePolicy } from \"repopo\";\n * import { PackageScripts } from \"repopo/policies\";\n *\n * const config: RepopoConfig = {\n * policies: [\n * makePolicy(PackageScripts, {\n * // Scripts that must exist\n * must: [\n * \"test\", // Must exist, no auto-fix\n * { build: \"tsc\" }, // Must exist, auto-fix available\n * { lint: \"biome lint .\" }, // Must exist, auto-fix available\n * ],\n * // Scripts that must exist AND match exactly\n * exact: {\n * clean: \"rimraf dist esm\",\n * format: \"biome format --write .\",\n * },\n * // If \"build\" exists, \"clean\" must also exist (with inline auto-fix default)\n * conditionalRequired: [{ ifPresent: \"build\", requires: [{ clean: \"rimraf dist\" }] }],\n * // Script body must contain these substrings\n * scriptMustContain: [{ script: \"build\", mustContain: [\"tsc\"] }],\n * // Only one of these can exist\n * mutuallyExclusive: [[\"lint:eslint\", \"lint:biome\"]],\n * }),\n * ],\n * };\n * ```\n *\n * @alpha\n */\nexport const PackageScripts = definePackagePolicy<\n\tPackageJson,\n\tPackageScriptsSettings | undefined\n>({\n\tname: \"PackageScripts\",\n\tdescription:\n\t\t\"Validates package.json scripts based on configurable rules including required scripts, exact content, and conditional requirements.\",\n\thandler: async (json, { file, root, resolve, config }) => {\n\t\tif (config === undefined) {\n\t\t\treturn true;\n\t\t}\n\n\t\tconst scripts = Object.hasOwn(json, \"scripts\") ? json.scripts : {};\n\t\tconst defaultScripts = buildDefaultsMap(config);\n\n\t\tconst { errors, missingWithDefaults, mismatchedScripts } =\n\t\t\trunAllValidations(config, scripts, defaultScripts);\n\n\t\tif (errors.length === 0) {\n\t\t\treturn true;\n\t\t}\n\n\t\tconst hasAutoFixableErrors =\n\t\t\tmissingWithDefaults.length > 0 || mismatchedScripts.length > 0;\n\n\t\tif (resolve && hasAutoFixableErrors) {\n\t\t\tconst { updatedScripts, fixedScripts } = applyScriptFixes(\n\t\t\t\tscripts,\n\t\t\t\tmissingWithDefaults,\n\t\t\t\tmismatchedScripts,\n\t\t\t\tdefaultScripts,\n\t\t\t);\n\n\t\t\ttry {\n\t\t\t\tawait jsonfile.writeFile(\n\t\t\t\t\tresolvePath(root, file),\n\t\t\t\t\t{ ...json, scripts: updatedScripts },\n\t\t\t\t\t{ spaces: 2 },\n\t\t\t\t);\n\t\t\t\tconst postFixErrors = revalidateAfterFix(config, updatedScripts);\n\n\t\t\t\treturn {\n\t\t\t\t\tname: POLICY_NAME,\n\t\t\t\t\tfile,\n\t\t\t\t\tresolved: postFixErrors.length === 0,\n\t\t\t\t\terrorMessages:\n\t\t\t\t\t\tpostFixErrors.length > 0\n\t\t\t\t\t\t\t? [\n\t\t\t\t\t\t\t\t\t`Fixed scripts: ${fixedScripts.join(\", \")}. Remaining errors:\\n${postFixErrors.join(\"\\n\\n\")}`,\n\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t: [`Fixed scripts: ${fixedScripts.join(\", \")}`],\n\t\t\t\t};\n\t\t\t} catch {\n\t\t\t\t// Fall through to return regular failure\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\tname: POLICY_NAME,\n\t\t\tfile,\n\t\t\tautoFixable: hasAutoFixableErrors,\n\t\t\terrorMessages: errors,\n\t\t};\n\t},\n});\n"]}
|
|
1
|
+
{"version":3,"file":"PackageScripts.js","sourceRoot":"","sources":["../../src/policies/PackageScripts.ts"],"names":[],"mappings":"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","sourcesContent":["import jsonfile from \"jsonfile\";\nimport { resolve as resolvePath } from \"pathe\";\nimport type { PackageJson } from \"type-fest\";\nimport { definePackagePolicy } from \"../policyDefiners/definePackagePolicy.js\";\n\n/**\n * Configuration for validating that a script's body contains required substrings.\n *\n * @public\n */\nexport interface ScriptMustContainRule {\n\t/**\n\t * The name of the script to validate.\n\t */\n\tscript: string;\n\n\t/**\n\t * Substrings that must appear in the script body.\n\t * All substrings must be present for the script to pass validation.\n\t */\n\tmustContain: string[];\n}\n\n/**\n * Configuration for conditional script requirements.\n *\n * @public\n */\nexport interface ConditionalScriptRule {\n\t/**\n\t * The script that triggers the requirement.\n\t * If this script exists, then `requires` must also exist.\n\t */\n\tifPresent: string;\n\n\t/**\n\t * Scripts that must be present if `ifPresent` script exists.\n\t *\n\t * @remarks\n\t *\n\t * Each entry can be:\n\t * - A string: Script must exist, no auto-fix available\n\t * - An object `{ scriptName: \"default value\" }`: Script must exist, auto-fix adds the\n\t * default value if missing\n\t *\n\t * @example\n\t * ```typescript\n\t * requires: [\"test\", { clean: \"rimraf dist\" }]\n\t * ```\n\t */\n\trequires: RequiredScriptEntry[];\n}\n\n/**\n * A required script entry. Can be either:\n * - A string (script name only, no default value)\n * - An object with a single key-value pair (script name → default value)\n *\n * @example\n * ```typescript\n * // Script must exist, no auto-fix available\n * \"test\"\n *\n * // Script must exist, auto-fix will add this value if missing\n * { lint: \"biome lint .\" }\n * ```\n *\n * @public\n */\nexport type RequiredScriptEntry = string | Record<string, string>;\n\n/**\n * Policy settings for the PackageScripts repo policy.\n *\n * @public\n */\nexport interface PackageScriptsSettings {\n\t/**\n\t * Scripts that must be present in package.json.\n\t *\n\t * @remarks\n\t *\n\t * Each entry can be:\n\t * - A string: Script must exist, no auto-fix available, content not validated\n\t * - An object `{ scriptName: \"default value\" }`: Script must exist, auto-fix adds the\n\t * default value if missing, but existing scripts with different content still pass\n\t *\n\t * @example\n\t * ```typescript\n\t * must: [\n\t * \"test\", // Must exist, no auto-fix\n\t * { lint: \"biome lint .\" }, // Must exist, auto-fix available\n\t * { clean: \"rimraf dist esm\" }, // Must exist, auto-fix available\n\t * ]\n\t * ```\n\t */\n\tmust?: RequiredScriptEntry[];\n\n\t/**\n\t * Scripts that must exist AND have exact content.\n\t *\n\t * @remarks\n\t *\n\t * Unlike {@link PackageScriptsSettings.must}, scripts in `exact` are validated against\n\t * the specified value. If a script exists but has different content, it fails validation.\n\t * Both missing and mismatched scripts are auto-fixable.\n\t *\n\t * @example\n\t * ```typescript\n\t * exact: {\n\t * clean: \"rimraf dist esm\",\n\t * format: \"biome format --write .\",\n\t * }\n\t * ```\n\t */\n\texact?: Record<string, string>;\n\n\t/**\n\t * List of mutually exclusive script groups. Each group is an array of script names\n\t * where at most one script from the group can be present.\n\t *\n\t * Packages can have zero scripts from the group (all optional), but cannot have\n\t * multiple scripts from the same group.\n\t *\n\t * @example\n\t * ```typescript\n\t * // Allow \"test:unit\" OR \"test:vitest\" OR neither, but not both\n\t * mutuallyExclusive: [[\"test:unit\", \"test:vitest\"]]\n\t * ```\n\t */\n\tmutuallyExclusive?: string[][];\n\n\t/**\n\t * Conditional script requirements. If a script is present, other scripts must also exist.\n\t *\n\t * @remarks\n\t *\n\t * This is useful for enforcing that packages with a \"build\" script also have a \"clean\" script,\n\t * or packages with \"test\" also have \"test:coverage\".\n\t *\n\t * The `requires` field accepts the same format as {@link PackageScriptsSettings.must}:\n\t * - Strings: Script must exist, no auto-fix available\n\t * - Objects `{ scriptName: \"default\" }`: Script must exist, auto-fix adds the default if missing\n\t *\n\t * @example\n\t * ```typescript\n\t * // If \"build\" exists, \"clean\" must also exist (with auto-fix default)\n\t * conditionalRequired: [{ ifPresent: \"build\", requires: [{ clean: \"rimraf dist\" }] }]\n\t *\n\t * // Mixed: \"test\" required without default, \"clean\" with default\n\t * conditionalRequired: [{ ifPresent: \"build\", requires: [\"test\", { clean: \"rimraf dist\" }] }]\n\t * ```\n\t */\n\tconditionalRequired?: ConditionalScriptRule[];\n\n\t/**\n\t * Rules for validating script body content.\n\t *\n\t * @remarks\n\t *\n\t * This allows you to ensure that specific scripts contain required commands or patterns\n\t * without requiring an exact match. For example, requiring that the \"clean\" script uses\n\t * \"rimraf\" or that \"build\" includes \"tsc\".\n\t *\n\t * For exact content matching, use {@link PackageScriptsSettings.exact} instead.\n\t *\n\t * @example\n\t * ```typescript\n\t * scriptMustContain: [\n\t * { script: \"clean\", mustContain: [\"rimraf\"] },\n\t * { script: \"build\", mustContain: [\"tsc\"] },\n\t * ]\n\t * ```\n\t */\n\tscriptMustContain?: ScriptMustContainRule[];\n}\n/**\n * Extracts script name and optional default value from a RequiredScriptEntry.\n */\nfunction parseRequiredEntry(entry: RequiredScriptEntry): {\n\tname: string;\n\tdefaultValue?: string;\n} {\n\tif (typeof entry === \"string\") {\n\t\treturn { name: entry };\n\t}\n\tconst keys = Object.keys(entry);\n\tif (keys.length !== 1) {\n\t\tthrow new Error(\n\t\t\t`Invalid required script entry: expected single key-value pair, got ${keys.length} keys`,\n\t\t);\n\t}\n\t// biome-ignore lint/style/noNonNullAssertion: we verified length is 1\n\tconst name = keys[0]!;\n\tconst defaultValue = entry[name];\n\t// Only include defaultValue if it's defined (exactOptionalPropertyTypes)\n\treturn defaultValue !== undefined ? { name, defaultValue } : { name };\n}\n\n/**\n * Extract defaults from RequiredScriptEntry array.\n */\nfunction extractDefaultsFromEntries(\n\tentries: RequiredScriptEntry[],\n\tdefaults: Record<string, string>,\n): void {\n\tfor (const entry of entries) {\n\t\tconst { name, defaultValue } = parseRequiredEntry(entry);\n\t\tif (defaultValue !== undefined) {\n\t\t\tdefaults[name] = defaultValue;\n\t\t}\n\t}\n}\n\n/**\n * Builds a map of script names to their default values from must, conditionalRequired, and exact configs.\n */\nfunction buildDefaultsMap(\n\tconfig: PackageScriptsSettings,\n): Record<string, string> {\n\tconst defaults: Record<string, string> = {};\n\n\t// Add defaults from must entries\n\tif (config.must) {\n\t\textractDefaultsFromEntries(config.must, defaults);\n\t}\n\n\t// Add defaults from conditionalRequired entries\n\tif (config.conditionalRequired) {\n\t\tfor (const rule of config.conditionalRequired) {\n\t\t\textractDefaultsFromEntries(rule.requires, defaults);\n\t\t}\n\t}\n\n\t// Add defaults from exact (these override must/conditionalRequired if both specified)\n\tif (config.exact) {\n\t\tObject.assign(defaults, config.exact);\n\t}\n\n\treturn defaults;\n}\n\n/**\n * Validates mutually exclusive script groups and returns error messages for violations.\n */\nfunction validateMutuallyExclusiveScripts(\n\tgroups: string[][],\n\tscripts: PackageJson[\"scripts\"],\n): string[] {\n\tconst errors: string[] = [];\n\n\tfor (const group of groups) {\n\t\tconst presentScripts = group.filter(\n\t\t\t(scriptName) => scripts && Object.hasOwn(scripts, scriptName),\n\t\t);\n\n\t\t// Only fail if MORE than one script from the group is present\n\t\tif (presentScripts.length > 1) {\n\t\t\terrors.push(\n\t\t\t\t`Scripts are mutually exclusive, but found ${presentScripts.length}: ${presentScripts.join(\", \")}`,\n\t\t\t);\n\t\t}\n\t}\n\n\treturn errors;\n}\n\n/**\n * Validates conditional script requirements (if X exists, Y must also exist).\n */\nfunction validateConditionalRequirements(\n\trules: ConditionalScriptRule[],\n\tscripts: PackageJson[\"scripts\"],\n): string[] {\n\tconst errors: string[] = [];\n\n\tfor (const rule of rules) {\n\t\t// Check if the triggering script exists\n\t\tif (scripts && Object.hasOwn(scripts, rule.ifPresent)) {\n\t\t\t// Find missing required scripts\n\t\t\tconst missingRequired: string[] = [];\n\t\t\tfor (const entry of rule.requires) {\n\t\t\t\tconst { name } = parseRequiredEntry(entry);\n\t\t\t\tif (!Object.hasOwn(scripts, name)) {\n\t\t\t\t\tmissingRequired.push(name);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (missingRequired.length > 0) {\n\t\t\t\terrors.push(\n\t\t\t\t\t`Script \"${rule.ifPresent}\" is present, but required companion script(s) missing: ${missingRequired.join(\", \")}`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn errors;\n}\n\n/**\n * Validates that script bodies contain required substrings.\n */\nfunction validateScriptContents(\n\trules: ScriptMustContainRule[],\n\tscripts: PackageJson[\"scripts\"],\n): string[] {\n\tconst errors: string[] = [];\n\n\tfor (const rule of rules) {\n\t\t// Only validate if the script exists\n\t\tif (scripts && Object.hasOwn(scripts, rule.script)) {\n\t\t\tconst scriptBody = scripts[rule.script];\n\t\t\tif (typeof scriptBody === \"string\") {\n\t\t\t\tconst missingSubstrings = rule.mustContain.filter(\n\t\t\t\t\t(substring) => !scriptBody.includes(substring),\n\t\t\t\t);\n\n\t\t\t\tif (missingSubstrings.length > 0) {\n\t\t\t\t\terrors.push(\n\t\t\t\t\t\t`Script \"${rule.script}\" must contain: ${missingSubstrings.join(\", \")}`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn errors;\n}\n\n/**\n * Result of validating required scripts.\n */\ninterface MustScriptsResult {\n\terrors: string[];\n\tmissingWithDefaults: string[];\n}\n\n/**\n * Validates required scripts from 'must' config.\n */\nfunction validateMustScripts(\n\tmust: RequiredScriptEntry[],\n\tscripts: PackageJson[\"scripts\"],\n): MustScriptsResult {\n\tconst missingScripts: string[] = [];\n\tconst missingWithDefaults: string[] = [];\n\n\tfor (const entry of must) {\n\t\tconst { name, defaultValue } = parseRequiredEntry(entry);\n\t\tif (!(scripts && Object.hasOwn(scripts, name))) {\n\t\t\tmissingScripts.push(name);\n\t\t\tif (defaultValue !== undefined) {\n\t\t\t\tmissingWithDefaults.push(name);\n\t\t\t}\n\t\t}\n\t}\n\n\tconst errors: string[] = [];\n\tif (missingScripts.length > 0) {\n\t\terrors.push(`Missing required scripts:\\n\\t${missingScripts.join(\"\\n\\t\")}`);\n\t}\n\n\treturn { errors, missingWithDefaults };\n}\n\n/**\n * Result of validating exact scripts.\n */\ninterface ExactScriptsResult {\n\terrors: string[];\n\tmissingWithDefaults: string[];\n\tmismatchedScripts: string[];\n}\n\n/**\n * Validates scripts from 'exact' config - must exist AND match exactly.\n */\nfunction validateExactScripts(\n\texact: Record<string, string>,\n\tscripts: PackageJson[\"scripts\"],\n): ExactScriptsResult {\n\tconst errors: string[] = [];\n\tconst missingWithDefaults: string[] = [];\n\tconst mismatchedScripts: string[] = [];\n\n\tfor (const [scriptName, expectedValue] of Object.entries(exact)) {\n\t\tif (scripts && Object.hasOwn(scripts, scriptName)) {\n\t\t\tconst actualValue = scripts[scriptName];\n\t\t\tif (actualValue !== expectedValue) {\n\t\t\t\tmismatchedScripts.push(scriptName);\n\t\t\t\terrors.push(\n\t\t\t\t\t`Script \"${scriptName}\" must be \"${expectedValue}\", but found \"${actualValue}\"`,\n\t\t\t\t);\n\t\t\t}\n\t\t} else {\n\t\t\tmissingWithDefaults.push(scriptName);\n\t\t\terrors.push(`Missing required script: ${scriptName}`);\n\t\t}\n\t}\n\n\treturn { errors, missingWithDefaults, mismatchedScripts };\n}\n\n/**\n * Result of validating conditional requirements.\n */\ninterface ConditionalResult {\n\terrors: string[];\n\tmissingWithDefaults: string[];\n}\n\n/**\n * Find missing scripts from a conditional rule's requirements.\n */\nfunction findMissingFromRule(\n\trule: ConditionalScriptRule,\n\tscripts: PackageJson[\"scripts\"],\n): string[] {\n\treturn rule.requires\n\t\t.map((entry) => parseRequiredEntry(entry).name)\n\t\t.filter((name) => !(scripts && Object.hasOwn(scripts, name)));\n}\n\n/**\n * Validates conditional requirements and tracks missing scripts with defaults.\n */\nfunction validateConditionalWithDefaults(\n\trules: ConditionalScriptRule[],\n\tscripts: PackageJson[\"scripts\"],\n\tdefaultScripts: Record<string, string>,\n\texistingMissingWithDefaults: string[],\n): ConditionalResult {\n\tconst errors: string[] = [];\n\tconst missingWithDefaults: string[] = [];\n\tconst allTracked = new Set(existingMissingWithDefaults);\n\n\tfor (const rule of rules) {\n\t\tif (!(scripts && Object.hasOwn(scripts, rule.ifPresent))) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst missingRequired = findMissingFromRule(rule, scripts);\n\n\t\tfor (const scriptName of missingRequired) {\n\t\t\tif (scriptName in defaultScripts && !allTracked.has(scriptName)) {\n\t\t\t\tmissingWithDefaults.push(scriptName);\n\t\t\t\tallTracked.add(scriptName);\n\t\t\t}\n\t\t}\n\n\t\tif (missingRequired.length > 0) {\n\t\t\terrors.push(\n\t\t\t\t`Script \"${rule.ifPresent}\" is present, but required companion script(s) missing: ${missingRequired.join(\", \")}`,\n\t\t\t);\n\t\t}\n\t}\n\n\treturn { errors, missingWithDefaults };\n}\n\n/**\n * Apply fixes to scripts and return the updated scripts object.\n */\nfunction applyScriptFixes(\n\tscripts: PackageJson[\"scripts\"],\n\tmissingScriptsWithDefaults: string[],\n\tmismatchedScripts: string[],\n\tdefaultScripts: Record<string, string>,\n): {\n\tupdatedScripts: Record<string, string>;\n\tfixedScripts: string[];\n} {\n\tconst updatedScripts: Record<string, string> = {};\n\n\t// Copy existing scripts, filtering out undefined values\n\tif (scripts) {\n\t\tfor (const [key, value] of Object.entries(scripts)) {\n\t\t\tif (value !== undefined) {\n\t\t\t\tupdatedScripts[key] = value;\n\t\t\t}\n\t\t}\n\t}\n\n\tconst fixedScripts: string[] = [];\n\n\tfor (const scriptName of missingScriptsWithDefaults) {\n\t\tconst defaultValue = defaultScripts[scriptName];\n\t\tif (defaultValue !== undefined) {\n\t\t\tupdatedScripts[scriptName] = defaultValue;\n\t\t\tfixedScripts.push(scriptName);\n\t\t}\n\t}\n\n\tfor (const scriptName of mismatchedScripts) {\n\t\tconst defaultValue = defaultScripts[scriptName];\n\t\tif (defaultValue !== undefined && !fixedScripts.includes(scriptName)) {\n\t\t\tupdatedScripts[scriptName] = defaultValue;\n\t\t\tfixedScripts.push(scriptName);\n\t\t}\n\t}\n\n\treturn { updatedScripts, fixedScripts };\n}\n\n/**\n * Validate 'must' scripts and return any remaining missing scripts.\n */\nfunction validateRemainingMustScripts(\n\tmust: RequiredScriptEntry[],\n\tscripts: Record<string, string>,\n): string[] {\n\tconst stillMissing = must\n\t\t.map((entry) => parseRequiredEntry(entry).name)\n\t\t.filter((name) => !Object.hasOwn(scripts, name));\n\n\tif (stillMissing.length > 0) {\n\t\treturn [`Missing required scripts:\\n\\t${stillMissing.join(\"\\n\\t\")}`];\n\t}\n\treturn [];\n}\n\n/**\n * Validate 'exact' scripts and return any errors.\n */\nfunction validateRemainingExactScripts(\n\texact: Record<string, string>,\n\tscripts: Record<string, string>,\n): string[] {\n\tconst errors: string[] = [];\n\tfor (const [scriptName, expectedValue] of Object.entries(exact)) {\n\t\tif (!Object.hasOwn(scripts, scriptName)) {\n\t\t\terrors.push(`Missing required script: ${scriptName}`);\n\t\t} else if (scripts[scriptName] !== expectedValue) {\n\t\t\terrors.push(\n\t\t\t\t`Script \"${scriptName}\" must be \"${expectedValue}\", but found \"${scripts[scriptName]}\"`,\n\t\t\t);\n\t\t}\n\t}\n\treturn errors;\n}\n\n/**\n * Re-validate all rules after applying fixes.\n */\nfunction revalidateAfterFix(\n\tconfig: PackageScriptsSettings,\n\tupdatedScripts: Record<string, string>,\n): string[] {\n\tconst errors: string[] = [];\n\n\tif (\n\t\tconfig.mutuallyExclusive !== undefined &&\n\t\tconfig.mutuallyExclusive.length > 0\n\t) {\n\t\terrors.push(\n\t\t\t...validateMutuallyExclusiveScripts(\n\t\t\t\tconfig.mutuallyExclusive,\n\t\t\t\tupdatedScripts,\n\t\t\t),\n\t\t);\n\t}\n\n\tif (\n\t\tconfig.scriptMustContain !== undefined &&\n\t\tconfig.scriptMustContain.length > 0\n\t) {\n\t\terrors.push(\n\t\t\t...validateScriptContents(config.scriptMustContain, updatedScripts),\n\t\t);\n\t}\n\n\tif (config.must !== undefined && config.must.length > 0) {\n\t\terrors.push(...validateRemainingMustScripts(config.must, updatedScripts));\n\t}\n\n\tif (config.exact !== undefined && Object.keys(config.exact).length > 0) {\n\t\terrors.push(...validateRemainingExactScripts(config.exact, updatedScripts));\n\t}\n\n\tif (\n\t\tconfig.conditionalRequired !== undefined &&\n\t\tconfig.conditionalRequired.length > 0\n\t) {\n\t\terrors.push(\n\t\t\t...validateConditionalRequirements(\n\t\t\t\tconfig.conditionalRequired,\n\t\t\t\tupdatedScripts,\n\t\t\t),\n\t\t);\n\t}\n\n\treturn errors;\n}\n\n/**\n * Result of running all script validations.\n */\ninterface AllValidationsResult {\n\terrors: string[];\n\tmissingWithDefaults: string[];\n\tmismatchedScripts: string[];\n}\n\n/**\n * Run all script validations and aggregate results.\n */\nfunction runAllValidations(\n\tconfig: PackageScriptsSettings,\n\tscripts: PackageJson[\"scripts\"],\n\tdefaultScripts: Record<string, string>,\n): AllValidationsResult {\n\tconst errors: string[] = [];\n\tconst missingWithDefaults: string[] = [];\n\tlet mismatchedScripts: string[] = [];\n\n\tif (config.must !== undefined && config.must.length > 0) {\n\t\tconst result = validateMustScripts(config.must, scripts);\n\t\terrors.push(...result.errors);\n\t\tmissingWithDefaults.push(...result.missingWithDefaults);\n\t}\n\n\tif (config.exact !== undefined && Object.keys(config.exact).length > 0) {\n\t\tconst result = validateExactScripts(config.exact, scripts);\n\t\terrors.push(...result.errors);\n\t\tmissingWithDefaults.push(...result.missingWithDefaults);\n\t\tmismatchedScripts = result.mismatchedScripts;\n\t}\n\n\tif (\n\t\tconfig.mutuallyExclusive !== undefined &&\n\t\tconfig.mutuallyExclusive.length > 0\n\t) {\n\t\terrors.push(\n\t\t\t...validateMutuallyExclusiveScripts(config.mutuallyExclusive, scripts),\n\t\t);\n\t}\n\n\tif (\n\t\tconfig.conditionalRequired !== undefined &&\n\t\tconfig.conditionalRequired.length > 0\n\t) {\n\t\tconst result = validateConditionalWithDefaults(\n\t\t\tconfig.conditionalRequired,\n\t\t\tscripts,\n\t\t\tdefaultScripts,\n\t\t\tmissingWithDefaults,\n\t\t);\n\t\terrors.push(...result.errors);\n\t\tmissingWithDefaults.push(...result.missingWithDefaults);\n\t}\n\n\tif (\n\t\tconfig.scriptMustContain !== undefined &&\n\t\tconfig.scriptMustContain.length > 0\n\t) {\n\t\terrors.push(...validateScriptContents(config.scriptMustContain, scripts));\n\t}\n\n\treturn { errors, missingWithDefaults, mismatchedScripts };\n}\n\n/**\n * A RepoPolicy that validates package.json scripts based on configurable rules.\n *\n * @remarks\n *\n * This policy provides comprehensive script validation including:\n * - Required scripts that must be present (with optional defaults for auto-fix)\n * - Exact script content enforcement\n * - Mutually exclusive script groups (at most one can exist)\n * - Conditional requirements (if X exists, Y must also exist)\n * - Script body content validation (script must contain specific substrings)\n *\n * @example\n * ```typescript\n * import { makePolicy } from \"repopo\";\n * import { PackageScripts } from \"repopo/policies\";\n *\n * const config: RepopoConfig = {\n * policies: [\n * makePolicy(PackageScripts, {\n * config: {\n * // Scripts that must exist\n * must: [\n * \"test\", // Must exist, no auto-fix\n * { build: \"tsc\" }, // Must exist, auto-fix available\n * { lint: \"biome lint .\" }, // Must exist, auto-fix available\n * ],\n * // Scripts that must exist AND match exactly\n * exact: {\n * clean: \"rimraf dist esm\",\n * format: \"biome format --write .\",\n * },\n * // If \"build\" exists, \"clean\" must also exist (with inline auto-fix default)\n * conditionalRequired: [{ ifPresent: \"build\", requires: [{ clean: \"rimraf dist\" }] }],\n * // Script body must contain these substrings\n * scriptMustContain: [{ script: \"build\", mustContain: [\"tsc\"] }],\n * // Only one of these can exist\n * mutuallyExclusive: [[\"lint:eslint\", \"lint:biome\"]],\n * },\n * }),\n * ],\n * };\n * ```\n *\n * @public\n */\nexport const PackageScripts = definePackagePolicy<\n\tPackageJson,\n\tPackageScriptsSettings | undefined\n>({\n\tname: \"PackageScripts\",\n\trequiresConfig: true,\n\tdescription:\n\t\t\"Validates package.json scripts based on configurable rules including required scripts, exact content, and conditional requirements.\",\n\thandler: async (json, { file, root, resolve, config }) => {\n\t\tif (config === undefined) {\n\t\t\treturn [];\n\t\t}\n\n\t\tconst scripts = Object.hasOwn(json, \"scripts\") ? json.scripts : {};\n\t\tconst defaultScripts = buildDefaultsMap(config);\n\n\t\tconst { errors, missingWithDefaults, mismatchedScripts } =\n\t\t\trunAllValidations(config, scripts, defaultScripts);\n\n\t\tif (errors.length === 0) {\n\t\t\treturn [];\n\t\t}\n\n\t\tconst hasAutoFixableErrors =\n\t\t\tmissingWithDefaults.length > 0 || mismatchedScripts.length > 0;\n\n\t\tif (resolve && hasAutoFixableErrors) {\n\t\t\tconst { updatedScripts, fixedScripts } = applyScriptFixes(\n\t\t\t\tscripts,\n\t\t\t\tmissingWithDefaults,\n\t\t\t\tmismatchedScripts,\n\t\t\t\tdefaultScripts,\n\t\t\t);\n\n\t\t\ttry {\n\t\t\t\tawait jsonfile.writeFile(\n\t\t\t\t\tresolvePath(root, file),\n\t\t\t\t\t{ ...json, scripts: updatedScripts },\n\t\t\t\t\t{ spaces: 2 },\n\t\t\t\t);\n\t\t\t\tconst postFixErrors = revalidateAfterFix(config, updatedScripts);\n\t\t\t\treturn [\n\t\t\t\t\t...fixedScripts.map((name) => ({\n\t\t\t\t\t\tmessage: `Fixed scripts: ${name}`,\n\t\t\t\t\t\tfixStatus: \"fixed\" as const,\n\t\t\t\t\t})),\n\t\t\t\t\t...postFixErrors.map((message) => ({\n\t\t\t\t\t\tmessage: `Remaining errors:\\n${message}`,\n\t\t\t\t\t})),\n\t\t\t\t];\n\t\t\t} catch {\n\t\t\t\t// Fall through to return regular failure\n\t\t\t}\n\t\t}\n\t\treturn errors.map((message) => ({\n\t\t\tmessage,\n\t\t\tfixStatus: hasAutoFixableErrors\n\t\t\t\t? resolve\n\t\t\t\t\t? \"failed\"\n\t\t\t\t\t: \"available\"\n\t\t\t\t: undefined,\n\t\t}));\n\t},\n});\n"]}
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
* };
|
|
22
22
|
* ```
|
|
23
23
|
*
|
|
24
|
-
* @
|
|
24
|
+
* @public
|
|
25
25
|
*/
|
|
26
26
|
export interface PackageTestScriptsConfig {
|
|
27
27
|
/**
|
|
@@ -86,15 +86,19 @@ export interface PackageTestScriptsConfig {
|
|
|
86
86
|
* const config: RepopoConfig = {
|
|
87
87
|
* policies: [
|
|
88
88
|
* makePolicy(PackageTestScripts, {
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
89
|
+
* config: {
|
|
90
|
+
* testDirectories: ["test", "tests"],
|
|
91
|
+
* testDependencies: ["vitest", "jest"],
|
|
92
|
+
* requiredScripts: ["test"],
|
|
93
|
+
* },
|
|
92
94
|
* }),
|
|
93
95
|
* ],
|
|
94
96
|
* };
|
|
95
97
|
* ```
|
|
96
98
|
*
|
|
97
|
-
* @
|
|
99
|
+
* @public
|
|
98
100
|
*/
|
|
99
|
-
export declare const PackageTestScripts: import("../policy.js").
|
|
101
|
+
export declare const PackageTestScripts: import("../policy.js").FilePolicyDefinition<PackageTestScriptsConfig | undefined> & {
|
|
102
|
+
requiresConfig: true;
|
|
103
|
+
};
|
|
100
104
|
//# sourceMappingURL=PackageTestScripts.d.ts.map
|