repopo 0.9.0 → 0.10.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 +21 -0
- package/esm/commands/check.d.ts +1 -0
- package/esm/commands/check.d.ts.map +1 -1
- package/esm/commands/check.js +48 -11
- package/esm/commands/check.js.map +1 -1
- package/esm/config.d.ts +19 -4
- package/esm/config.d.ts.map +1 -1
- package/esm/config.js +2 -3
- package/esm/config.js.map +1 -1
- package/esm/index.d.ts +2 -2
- package/esm/index.d.ts.map +1 -1
- package/esm/index.js +6 -1
- package/esm/index.js.map +1 -1
- package/esm/makePolicy.d.ts +50 -8
- package/esm/makePolicy.d.ts.map +1 -1
- package/esm/makePolicy.js +64 -4
- package/esm/makePolicy.js.map +1 -1
- package/esm/policies/ConditionalScripts.d.ts +94 -0
- package/esm/policies/ConditionalScripts.d.ts.map +1 -0
- package/esm/policies/ConditionalScripts.js +144 -0
- package/esm/policies/ConditionalScripts.js.map +1 -0
- package/esm/policies/ExactScripts.d.ts +51 -0
- package/esm/policies/ExactScripts.d.ts.map +1 -0
- package/esm/policies/ExactScripts.js +107 -0
- package/esm/policies/ExactScripts.js.map +1 -0
- package/esm/policies/HtmlFileHeaders.d.ts +1 -1
- package/esm/policies/HtmlFileHeaders.d.ts.map +1 -1
- package/esm/policies/JsTsFileHeaders.d.ts +1 -1
- package/esm/policies/JsTsFileHeaders.d.ts.map +1 -1
- package/esm/policies/MutuallyExclusiveScripts.d.ts +46 -0
- package/esm/policies/MutuallyExclusiveScripts.d.ts.map +1 -0
- package/esm/policies/MutuallyExclusiveScripts.js +52 -0
- package/esm/policies/MutuallyExclusiveScripts.js.map +1 -0
- package/esm/policies/NoPrivateWorkspaceDependencies.d.ts +1 -1
- package/esm/policies/NoPrivateWorkspaceDependencies.d.ts.map +1 -1
- package/esm/policies/PackageAllowedScopes.d.ts +1 -1
- package/esm/policies/PackageAllowedScopes.d.ts.map +1 -1
- package/esm/policies/PackageEsmType.d.ts +1 -1
- package/esm/policies/PackageEsmType.d.ts.map +1 -1
- package/esm/policies/PackageFolderName.d.ts +1 -1
- package/esm/policies/PackageFolderName.d.ts.map +1 -1
- package/esm/policies/PackageJsonProperties.d.ts +1 -1
- package/esm/policies/PackageJsonProperties.d.ts.map +1 -1
- package/esm/policies/PackageJsonRepoDirectoryProperty.d.ts +1 -1
- package/esm/policies/PackageJsonRepoDirectoryProperty.d.ts.map +1 -1
- package/esm/policies/PackageJsonSorted.d.ts +1 -1
- package/esm/policies/PackageJsonSorted.d.ts.map +1 -1
- package/esm/policies/PackageLicense.d.ts +1 -1
- package/esm/policies/PackageLicense.d.ts.map +1 -1
- package/esm/policies/PackagePrivateField.d.ts +1 -1
- package/esm/policies/PackagePrivateField.d.ts.map +1 -1
- package/esm/policies/PackageReadme.d.ts +1 -1
- package/esm/policies/PackageReadme.d.ts.map +1 -1
- package/esm/policies/PackageScripts.d.ts +1 -1
- package/esm/policies/PackageScripts.d.ts.map +1 -1
- package/esm/policies/PackageTestScripts.d.ts +1 -1
- package/esm/policies/PackageTestScripts.d.ts.map +1 -1
- package/esm/policies/RequiredScripts.d.ts +71 -0
- package/esm/policies/RequiredScripts.d.ts.map +1 -0
- package/esm/policies/RequiredScripts.js +120 -0
- package/esm/policies/RequiredScripts.js.map +1 -0
- package/esm/policies/ScriptContains.d.ts +64 -0
- package/esm/policies/ScriptContains.d.ts.map +1 -0
- package/esm/policies/ScriptContains.js +62 -0
- package/esm/policies/ScriptContains.js.map +1 -0
- package/esm/policies.d.ts +6 -1
- package/esm/policies.d.ts.map +1 -1
- package/esm/policies.js +18 -0
- package/esm/policies.js.map +1 -1
- package/esm/policy.d.ts +180 -86
- package/esm/policy.d.ts.map +1 -1
- package/esm/policy.js +103 -21
- package/esm/policy.js.map +1 -1
- package/esm/policyDefiners/defineFileHeaderPolicy.d.ts +7 -2
- package/esm/policyDefiners/defineFileHeaderPolicy.d.ts.map +1 -1
- package/esm/policyDefiners/defineFileHeaderPolicy.js +5 -0
- package/esm/policyDefiners/defineFileHeaderPolicy.js.map +1 -1
- package/esm/policyDefiners/definePackagePolicy.d.ts +18 -5
- package/esm/policyDefiners/definePackagePolicy.d.ts.map +1 -1
- package/esm/policyDefiners/definePackagePolicy.js +14 -4
- package/esm/policyDefiners/definePackagePolicy.js.map +1 -1
- package/oclif.manifest.json +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A required script entry for conditional requirements. Can be either:
|
|
3
|
+
* - A string (script name only, no default value)
|
|
4
|
+
* - An object with a single key-value pair (script name → default value for auto-fix)
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```typescript
|
|
8
|
+
* // Script must exist, no auto-fix available
|
|
9
|
+
* "test"
|
|
10
|
+
*
|
|
11
|
+
* // Script must exist, auto-fix will add this value if missing
|
|
12
|
+
* { clean: "rimraf dist" }
|
|
13
|
+
* ```
|
|
14
|
+
*
|
|
15
|
+
* @alpha
|
|
16
|
+
*/
|
|
17
|
+
export type ConditionalScriptEntry = string | Record<string, string>;
|
|
18
|
+
/**
|
|
19
|
+
* Configuration for a conditional script requirement.
|
|
20
|
+
*
|
|
21
|
+
* @alpha
|
|
22
|
+
*/
|
|
23
|
+
export interface ConditionalScriptRule {
|
|
24
|
+
/**
|
|
25
|
+
* The script that triggers the requirement.
|
|
26
|
+
* If this script exists, then `requires` must also exist.
|
|
27
|
+
*/
|
|
28
|
+
ifPresent: string;
|
|
29
|
+
/**
|
|
30
|
+
* Scripts that must be present if `ifPresent` script exists.
|
|
31
|
+
*
|
|
32
|
+
* @remarks
|
|
33
|
+
* Each entry can be:
|
|
34
|
+
* - A string: Script must exist, no auto-fix available
|
|
35
|
+
* - An object `{ scriptName: "default value" }`: Script must exist, auto-fix adds the
|
|
36
|
+
* default value if missing
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```typescript
|
|
40
|
+
* requires: ["test", { clean: "rimraf dist" }]
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
requires: ConditionalScriptEntry[];
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Configuration for the ConditionalScripts policy.
|
|
47
|
+
*
|
|
48
|
+
* @alpha
|
|
49
|
+
*/
|
|
50
|
+
export interface ConditionalScriptsConfig {
|
|
51
|
+
/**
|
|
52
|
+
* Conditional script requirements. If a script is present, other scripts must also exist.
|
|
53
|
+
*
|
|
54
|
+
* @remarks
|
|
55
|
+
* This is useful for enforcing that packages with a "build" script also have a "clean" script,
|
|
56
|
+
* or packages with "test" also have "test:coverage".
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```typescript
|
|
60
|
+
* rules: [
|
|
61
|
+
* { ifPresent: "build", requires: [{ clean: "rimraf dist" }] },
|
|
62
|
+
* { ifPresent: "test", requires: ["test:coverage"] },
|
|
63
|
+
* ]
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
rules: ConditionalScriptRule[];
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* A policy that validates conditional script requirements in package.json.
|
|
70
|
+
*
|
|
71
|
+
* @remarks
|
|
72
|
+
* Use this policy when you want to enforce that the presence of one script
|
|
73
|
+
* requires other scripts to also exist. For example, if "build" exists,
|
|
74
|
+
* then "clean" must also exist.
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* ```typescript
|
|
78
|
+
* import { policy } from "repopo";
|
|
79
|
+
* import { ConditionalScripts } from "repopo/policies";
|
|
80
|
+
*
|
|
81
|
+
* policy(ConditionalScripts, {
|
|
82
|
+
* rules: [
|
|
83
|
+
* // If "build" exists, "clean" must also exist (with auto-fix default)
|
|
84
|
+
* { ifPresent: "build", requires: [{ clean: "rimraf dist" }] },
|
|
85
|
+
* // If "test" exists, "test:coverage" must also exist (no auto-fix)
|
|
86
|
+
* { ifPresent: "test", requires: ["test:coverage"] },
|
|
87
|
+
* ],
|
|
88
|
+
* })
|
|
89
|
+
* ```
|
|
90
|
+
*
|
|
91
|
+
* @alpha
|
|
92
|
+
*/
|
|
93
|
+
export declare const ConditionalScripts: import("../policy.js").PolicyShape<ConditionalScriptsConfig | undefined>;
|
|
94
|
+
//# sourceMappingURL=ConditionalScripts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ConditionalScripts.d.ts","sourceRoot":"","sources":["../../src/policies/ConditionalScripts.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;GAeG;AACH,MAAM,MAAM,sBAAsB,GAAG,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAErE;;;;GAIG;AACH,MAAM,WAAW,qBAAqB;IACrC;;;OAGG;IACH,SAAS,EAAE,MAAM,CAAC;IAElB;;;;;;;;;;;;;OAaG;IACH,QAAQ,EAAE,sBAAsB,EAAE,CAAC;CACnC;AAED;;;;GAIG;AACH,MAAM,WAAW,wBAAwB;IACxC;;;;;;;;;;;;;;OAcG;IACH,KAAK,EAAE,qBAAqB,EAAE,CAAC;CAC/B;AAiHD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,eAAO,MAAM,kBAAkB,0EAuE7B,CAAC"}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import jsonfile from "jsonfile";
|
|
2
|
+
import { definePackagePolicy } from "../policyDefiners/definePackagePolicy.js";
|
|
3
|
+
const POLICY_NAME = "ConditionalScripts";
|
|
4
|
+
/**
|
|
5
|
+
* Extracts script name and optional default value from a ConditionalScriptEntry.
|
|
6
|
+
*/
|
|
7
|
+
function parseEntry(entry) {
|
|
8
|
+
if (typeof entry === "string") {
|
|
9
|
+
return { name: entry };
|
|
10
|
+
}
|
|
11
|
+
const keys = Object.keys(entry);
|
|
12
|
+
if (keys.length !== 1) {
|
|
13
|
+
throw new Error(`Invalid conditional script entry: expected single key-value pair, got ${keys.length} keys`);
|
|
14
|
+
}
|
|
15
|
+
// biome-ignore lint/style/noNonNullAssertion: we verified length is 1
|
|
16
|
+
const name = keys[0];
|
|
17
|
+
const defaultValue = entry[name];
|
|
18
|
+
return defaultValue !== undefined ? { name, defaultValue } : { name };
|
|
19
|
+
}
|
|
20
|
+
function validateConditionalRules(scripts, rules) {
|
|
21
|
+
const violations = [];
|
|
22
|
+
const missingWithDefaults = [];
|
|
23
|
+
const seenMissing = new Set();
|
|
24
|
+
for (const rule of rules) {
|
|
25
|
+
// Skip if the triggering script doesn't exist
|
|
26
|
+
if (!Object.hasOwn(scripts, rule.ifPresent)) {
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
const missingScripts = [];
|
|
30
|
+
for (const entry of rule.requires) {
|
|
31
|
+
const { name, defaultValue } = parseEntry(entry);
|
|
32
|
+
if (!Object.hasOwn(scripts, name)) {
|
|
33
|
+
missingScripts.push(name);
|
|
34
|
+
if (defaultValue !== undefined && !seenMissing.has(name)) {
|
|
35
|
+
missingWithDefaults.push({ name, defaultValue });
|
|
36
|
+
seenMissing.add(name);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
if (missingScripts.length > 0) {
|
|
41
|
+
violations.push({
|
|
42
|
+
triggerScript: rule.ifPresent,
|
|
43
|
+
missingScripts,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return { violations, missingWithDefaults };
|
|
48
|
+
}
|
|
49
|
+
function formatViolationErrors(violations) {
|
|
50
|
+
return violations.map((v) => `Script "${v.triggerScript}" is present, but required companion script(s) missing: ${v.missingScripts.join(", ")}`);
|
|
51
|
+
}
|
|
52
|
+
async function fixMissingScripts(json, file, missingWithDefaults, existingScripts) {
|
|
53
|
+
const updatedScripts = {};
|
|
54
|
+
// Copy existing scripts
|
|
55
|
+
for (const [key, value] of Object.entries(existingScripts)) {
|
|
56
|
+
if (value !== undefined) {
|
|
57
|
+
updatedScripts[key] = value;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
// Add missing scripts with defaults
|
|
61
|
+
const fixedScripts = [];
|
|
62
|
+
for (const { name, defaultValue } of missingWithDefaults) {
|
|
63
|
+
updatedScripts[name] = defaultValue;
|
|
64
|
+
fixedScripts.push(name);
|
|
65
|
+
}
|
|
66
|
+
await jsonfile.writeFile(file, { ...json, scripts: updatedScripts }, { spaces: 2 });
|
|
67
|
+
return fixedScripts;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* A policy that validates conditional script requirements in package.json.
|
|
71
|
+
*
|
|
72
|
+
* @remarks
|
|
73
|
+
* Use this policy when you want to enforce that the presence of one script
|
|
74
|
+
* requires other scripts to also exist. For example, if "build" exists,
|
|
75
|
+
* then "clean" must also exist.
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* ```typescript
|
|
79
|
+
* import { policy } from "repopo";
|
|
80
|
+
* import { ConditionalScripts } from "repopo/policies";
|
|
81
|
+
*
|
|
82
|
+
* policy(ConditionalScripts, {
|
|
83
|
+
* rules: [
|
|
84
|
+
* // If "build" exists, "clean" must also exist (with auto-fix default)
|
|
85
|
+
* { ifPresent: "build", requires: [{ clean: "rimraf dist" }] },
|
|
86
|
+
* // If "test" exists, "test:coverage" must also exist (no auto-fix)
|
|
87
|
+
* { ifPresent: "test", requires: ["test:coverage"] },
|
|
88
|
+
* ],
|
|
89
|
+
* })
|
|
90
|
+
* ```
|
|
91
|
+
*
|
|
92
|
+
* @alpha
|
|
93
|
+
*/
|
|
94
|
+
export const ConditionalScripts = definePackagePolicy({
|
|
95
|
+
name: POLICY_NAME,
|
|
96
|
+
description: "Validates that if certain scripts exist, other required scripts must also exist.",
|
|
97
|
+
handler: async (json, { file, resolve, config }) => {
|
|
98
|
+
if (config === undefined || config.rules.length === 0) {
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
const scripts = json.scripts ?? {};
|
|
102
|
+
const validation = validateConditionalRules(scripts, config.rules);
|
|
103
|
+
if (validation.violations.length === 0) {
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
106
|
+
const errors = formatViolationErrors(validation.violations);
|
|
107
|
+
const hasAutoFixableErrors = validation.missingWithDefaults.length > 0;
|
|
108
|
+
if (resolve && hasAutoFixableErrors) {
|
|
109
|
+
try {
|
|
110
|
+
const fixedScripts = await fixMissingScripts(json, file, validation.missingWithDefaults, scripts);
|
|
111
|
+
// Re-validate after fix to check for remaining errors
|
|
112
|
+
const updatedScripts = { ...scripts };
|
|
113
|
+
for (const name of fixedScripts) {
|
|
114
|
+
const entry = validation.missingWithDefaults.find((m) => m.name === name);
|
|
115
|
+
if (entry) {
|
|
116
|
+
updatedScripts[name] = entry.defaultValue;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
const revalidation = validateConditionalRules(updatedScripts, config.rules);
|
|
120
|
+
const remainingErrors = formatViolationErrors(revalidation.violations);
|
|
121
|
+
return {
|
|
122
|
+
name: POLICY_NAME,
|
|
123
|
+
file,
|
|
124
|
+
resolved: remainingErrors.length === 0,
|
|
125
|
+
errorMessages: remainingErrors.length > 0
|
|
126
|
+
? [
|
|
127
|
+
`Fixed scripts: ${fixedScripts.join(", ")}. Remaining errors:\n${remainingErrors.join("\n")}`,
|
|
128
|
+
]
|
|
129
|
+
: [`Fixed scripts: ${fixedScripts.join(", ")}`],
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
// Fall through to return regular failure
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return {
|
|
137
|
+
name: POLICY_NAME,
|
|
138
|
+
file,
|
|
139
|
+
autoFixable: hasAutoFixableErrors,
|
|
140
|
+
errorMessages: errors,
|
|
141
|
+
};
|
|
142
|
+
},
|
|
143
|
+
});
|
|
144
|
+
//# sourceMappingURL=ConditionalScripts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ConditionalScripts.js","sourceRoot":"","sources":["../../src/policies/ConditionalScripts.ts"],"names":[],"mappings":"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","sourcesContent":["import jsonfile from \"jsonfile\";\nimport type { PackageJson } from \"type-fest\";\nimport { definePackagePolicy } from \"../policyDefiners/definePackagePolicy.js\";\n\n/**\n * A required script entry for conditional requirements. 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 for auto-fix)\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 * { clean: \"rimraf dist\" }\n * ```\n *\n * @alpha\n */\nexport type ConditionalScriptEntry = string | Record<string, string>;\n\n/**\n * Configuration for a conditional script requirement.\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 * 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: ConditionalScriptEntry[];\n}\n\n/**\n * Configuration for the ConditionalScripts policy.\n *\n * @alpha\n */\nexport interface ConditionalScriptsConfig {\n\t/**\n\t * Conditional script requirements. If a script is present, other scripts must also exist.\n\t *\n\t * @remarks\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 * @example\n\t * ```typescript\n\t * rules: [\n\t * { ifPresent: \"build\", requires: [{ clean: \"rimraf dist\" }] },\n\t * { ifPresent: \"test\", requires: [\"test:coverage\"] },\n\t * ]\n\t * ```\n\t */\n\trules: ConditionalScriptRule[];\n}\n\nconst POLICY_NAME = \"ConditionalScripts\";\n\ninterface ParsedEntry {\n\tname: string;\n\tdefaultValue?: string;\n}\n\n/**\n * Extracts script name and optional default value from a ConditionalScriptEntry.\n */\nfunction parseEntry(entry: ConditionalScriptEntry): ParsedEntry {\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 conditional 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\treturn defaultValue !== undefined ? { name, defaultValue } : { name };\n}\n\ninterface RuleViolation {\n\ttriggerScript: string;\n\tmissingScripts: string[];\n}\n\ninterface ValidationResult {\n\tviolations: RuleViolation[];\n\tmissingWithDefaults: Array<{ name: string; defaultValue: string }>;\n}\n\nfunction validateConditionalRules(\n\tscripts: Record<string, string | undefined>,\n\trules: ConditionalScriptRule[],\n): ValidationResult {\n\tconst violations: RuleViolation[] = [];\n\tconst missingWithDefaults: Array<{ name: string; defaultValue: string }> = [];\n\tconst seenMissing = new Set<string>();\n\n\tfor (const rule of rules) {\n\t\t// Skip if the triggering script doesn't exist\n\t\tif (!Object.hasOwn(scripts, rule.ifPresent)) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst missingScripts: string[] = [];\n\t\tfor (const entry of rule.requires) {\n\t\t\tconst { name, defaultValue } = parseEntry(entry);\n\t\t\tif (!Object.hasOwn(scripts, name)) {\n\t\t\t\tmissingScripts.push(name);\n\t\t\t\tif (defaultValue !== undefined && !seenMissing.has(name)) {\n\t\t\t\t\tmissingWithDefaults.push({ name, defaultValue });\n\t\t\t\t\tseenMissing.add(name);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (missingScripts.length > 0) {\n\t\t\tviolations.push({\n\t\t\t\ttriggerScript: rule.ifPresent,\n\t\t\t\tmissingScripts,\n\t\t\t});\n\t\t}\n\t}\n\n\treturn { violations, missingWithDefaults };\n}\n\nfunction formatViolationErrors(violations: RuleViolation[]): string[] {\n\treturn violations.map(\n\t\t(v) =>\n\t\t\t`Script \"${v.triggerScript}\" is present, but required companion script(s) missing: ${v.missingScripts.join(\", \")}`,\n\t);\n}\n\nasync function fixMissingScripts(\n\tjson: PackageJson,\n\tfile: string,\n\tmissingWithDefaults: Array<{ name: string; defaultValue: string }>,\n\texistingScripts: Record<string, string | undefined>,\n): Promise<string[]> {\n\tconst updatedScripts: Record<string, string> = {};\n\n\t// Copy existing scripts\n\tfor (const [key, value] of Object.entries(existingScripts)) {\n\t\tif (value !== undefined) {\n\t\t\tupdatedScripts[key] = value;\n\t\t}\n\t}\n\n\t// Add missing scripts with defaults\n\tconst fixedScripts: string[] = [];\n\tfor (const { name, defaultValue } of missingWithDefaults) {\n\t\tupdatedScripts[name] = defaultValue;\n\t\tfixedScripts.push(name);\n\t}\n\n\tawait jsonfile.writeFile(\n\t\tfile,\n\t\t{ ...json, scripts: updatedScripts },\n\t\t{ spaces: 2 },\n\t);\n\n\treturn fixedScripts;\n}\n\n/**\n * A policy that validates conditional script requirements in package.json.\n *\n * @remarks\n * Use this policy when you want to enforce that the presence of one script\n * requires other scripts to also exist. For example, if \"build\" exists,\n * then \"clean\" must also exist.\n *\n * @example\n * ```typescript\n * import { policy } from \"repopo\";\n * import { ConditionalScripts } from \"repopo/policies\";\n *\n * policy(ConditionalScripts, {\n * rules: [\n * // If \"build\" exists, \"clean\" must also exist (with auto-fix default)\n * { ifPresent: \"build\", requires: [{ clean: \"rimraf dist\" }] },\n * // If \"test\" exists, \"test:coverage\" must also exist (no auto-fix)\n * { ifPresent: \"test\", requires: [\"test:coverage\"] },\n * ],\n * })\n * ```\n *\n * @alpha\n */\nexport const ConditionalScripts = definePackagePolicy<\n\tPackageJson,\n\tConditionalScriptsConfig | undefined\n>({\n\tname: POLICY_NAME,\n\tdescription:\n\t\t\"Validates that if certain scripts exist, other required scripts must also exist.\",\n\thandler: async (json, { file, resolve, config }) => {\n\t\tif (config === undefined || config.rules.length === 0) {\n\t\t\treturn true;\n\t\t}\n\n\t\tconst scripts = json.scripts ?? {};\n\t\tconst validation = validateConditionalRules(scripts, config.rules);\n\n\t\tif (validation.violations.length === 0) {\n\t\t\treturn true;\n\t\t}\n\n\t\tconst errors = formatViolationErrors(validation.violations);\n\t\tconst hasAutoFixableErrors = validation.missingWithDefaults.length > 0;\n\n\t\tif (resolve && hasAutoFixableErrors) {\n\t\t\ttry {\n\t\t\t\tconst fixedScripts = await fixMissingScripts(\n\t\t\t\t\tjson,\n\t\t\t\t\tfile,\n\t\t\t\t\tvalidation.missingWithDefaults,\n\t\t\t\t\tscripts,\n\t\t\t\t);\n\n\t\t\t\t// Re-validate after fix to check for remaining errors\n\t\t\t\tconst updatedScripts = { ...scripts };\n\t\t\t\tfor (const name of fixedScripts) {\n\t\t\t\t\tconst entry = validation.missingWithDefaults.find(\n\t\t\t\t\t\t(m) => m.name === name,\n\t\t\t\t\t);\n\t\t\t\t\tif (entry) {\n\t\t\t\t\t\tupdatedScripts[name] = entry.defaultValue;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst revalidation = validateConditionalRules(\n\t\t\t\t\tupdatedScripts,\n\t\t\t\t\tconfig.rules,\n\t\t\t\t);\n\t\t\t\tconst remainingErrors = formatViolationErrors(revalidation.violations);\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: remainingErrors.length === 0,\n\t\t\t\t\terrorMessages:\n\t\t\t\t\t\tremainingErrors.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${remainingErrors.join(\"\\n\")}`,\n\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t: [`Fixed scripts: ${fixedScripts.join(\", \")}`],\n\t\t\t\t};\n\t\t\t} catch {\n\t\t\t\t// Fall through to return regular failure\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\tname: POLICY_NAME,\n\t\t\tfile,\n\t\t\tautoFixable: hasAutoFixableErrors,\n\t\t\terrorMessages: errors,\n\t\t};\n\t},\n});\n"]}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration for the ExactScripts policy.
|
|
3
|
+
*
|
|
4
|
+
* @alpha
|
|
5
|
+
*/
|
|
6
|
+
export interface ExactScriptsConfig {
|
|
7
|
+
/**
|
|
8
|
+
* Scripts that must exist AND have exact content.
|
|
9
|
+
*
|
|
10
|
+
* @remarks
|
|
11
|
+
* Unlike RequiredScripts, scripts in `exact` are validated against
|
|
12
|
+
* the specified value. If a script exists but has different content, it fails validation.
|
|
13
|
+
* Both missing and mismatched scripts are auto-fixable.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```typescript
|
|
17
|
+
* scripts: {
|
|
18
|
+
* clean: "rimraf dist esm",
|
|
19
|
+
* format: "biome format --write .",
|
|
20
|
+
* }
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
scripts: Record<string, string>;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* A policy that validates scripts exist AND have exact content in package.json.
|
|
27
|
+
*
|
|
28
|
+
* @remarks
|
|
29
|
+
* Use this policy when you want to enforce exact script content, not just presence.
|
|
30
|
+
* This is useful for scripts that must be exactly the same across packages.
|
|
31
|
+
*
|
|
32
|
+
* For scripts where you only care about presence (not content), use RequiredScripts instead.
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```typescript
|
|
36
|
+
* import { policy } from "repopo";
|
|
37
|
+
* import { ExactScripts } from "repopo/policies";
|
|
38
|
+
*
|
|
39
|
+
* policy(ExactScripts, {
|
|
40
|
+
* scripts: {
|
|
41
|
+
* clean: "rimraf dist esm",
|
|
42
|
+
* format: "biome format --write .",
|
|
43
|
+
* lint: "biome lint .",
|
|
44
|
+
* },
|
|
45
|
+
* })
|
|
46
|
+
* ```
|
|
47
|
+
*
|
|
48
|
+
* @alpha
|
|
49
|
+
*/
|
|
50
|
+
export declare const ExactScripts: import("../policy.js").PolicyShape<ExactScriptsConfig | undefined>;
|
|
51
|
+
//# sourceMappingURL=ExactScripts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ExactScripts.d.ts","sourceRoot":"","sources":["../../src/policies/ExactScripts.ts"],"names":[],"mappings":"AAIA;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IAClC;;;;;;;;;;;;;;;OAeG;IACH,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChC;AAuED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,eAAO,MAAM,YAAY,oEAqDvB,CAAC"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import jsonfile from "jsonfile";
|
|
2
|
+
import { definePackagePolicy } from "../policyDefiners/definePackagePolicy.js";
|
|
3
|
+
const POLICY_NAME = "ExactScripts";
|
|
4
|
+
function validateScripts(scripts, expectedScripts) {
|
|
5
|
+
const errors = [];
|
|
6
|
+
const missingScripts = [];
|
|
7
|
+
const mismatchedScripts = [];
|
|
8
|
+
for (const [scriptName, expectedValue] of Object.entries(expectedScripts)) {
|
|
9
|
+
if (Object.hasOwn(scripts, scriptName)) {
|
|
10
|
+
const actualValue = scripts[scriptName];
|
|
11
|
+
if (actualValue !== expectedValue) {
|
|
12
|
+
mismatchedScripts.push(scriptName);
|
|
13
|
+
errors.push(`Script "${scriptName}" must be "${expectedValue}", but found "${actualValue}"`);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
missingScripts.push(scriptName);
|
|
18
|
+
errors.push(`Missing required script: ${scriptName}`);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return { errors, missingScripts, mismatchedScripts };
|
|
22
|
+
}
|
|
23
|
+
async function fixScripts(json, file, scriptsToFix, expectedScripts, existingScripts) {
|
|
24
|
+
const updatedScripts = {};
|
|
25
|
+
// Copy existing scripts
|
|
26
|
+
for (const [key, value] of Object.entries(existingScripts)) {
|
|
27
|
+
if (value !== undefined) {
|
|
28
|
+
updatedScripts[key] = value;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
// Add/update scripts
|
|
32
|
+
const fixedScripts = [];
|
|
33
|
+
for (const scriptName of scriptsToFix) {
|
|
34
|
+
const expectedValue = expectedScripts[scriptName];
|
|
35
|
+
if (expectedValue !== undefined) {
|
|
36
|
+
updatedScripts[scriptName] = expectedValue;
|
|
37
|
+
fixedScripts.push(scriptName);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
await jsonfile.writeFile(file, { ...json, scripts: updatedScripts }, { spaces: 2 });
|
|
41
|
+
return fixedScripts;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* A policy that validates scripts exist AND have exact content in package.json.
|
|
45
|
+
*
|
|
46
|
+
* @remarks
|
|
47
|
+
* Use this policy when you want to enforce exact script content, not just presence.
|
|
48
|
+
* This is useful for scripts that must be exactly the same across packages.
|
|
49
|
+
*
|
|
50
|
+
* For scripts where you only care about presence (not content), use RequiredScripts instead.
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* ```typescript
|
|
54
|
+
* import { policy } from "repopo";
|
|
55
|
+
* import { ExactScripts } from "repopo/policies";
|
|
56
|
+
*
|
|
57
|
+
* policy(ExactScripts, {
|
|
58
|
+
* scripts: {
|
|
59
|
+
* clean: "rimraf dist esm",
|
|
60
|
+
* format: "biome format --write .",
|
|
61
|
+
* lint: "biome lint .",
|
|
62
|
+
* },
|
|
63
|
+
* })
|
|
64
|
+
* ```
|
|
65
|
+
*
|
|
66
|
+
* @alpha
|
|
67
|
+
*/
|
|
68
|
+
export const ExactScripts = definePackagePolicy({
|
|
69
|
+
name: POLICY_NAME,
|
|
70
|
+
description: "Validates that scripts exist and have exact content in package.json.",
|
|
71
|
+
handler: async (json, { file, resolve, config }) => {
|
|
72
|
+
if (config === undefined || Object.keys(config.scripts).length === 0) {
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
const scripts = json.scripts ?? {};
|
|
76
|
+
const validation = validateScripts(scripts, config.scripts);
|
|
77
|
+
if (validation.errors.length === 0) {
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
const scriptsToFix = [
|
|
81
|
+
...validation.missingScripts,
|
|
82
|
+
...validation.mismatchedScripts,
|
|
83
|
+
];
|
|
84
|
+
const hasAutoFixableErrors = scriptsToFix.length > 0;
|
|
85
|
+
if (resolve && hasAutoFixableErrors) {
|
|
86
|
+
try {
|
|
87
|
+
const fixedScripts = await fixScripts(json, file, scriptsToFix, config.scripts, scripts);
|
|
88
|
+
return {
|
|
89
|
+
name: POLICY_NAME,
|
|
90
|
+
file,
|
|
91
|
+
resolved: true,
|
|
92
|
+
errorMessages: [`Fixed scripts: ${fixedScripts.join(", ")}`],
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
// Fall through to return regular failure
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return {
|
|
100
|
+
name: POLICY_NAME,
|
|
101
|
+
file,
|
|
102
|
+
autoFixable: hasAutoFixableErrors,
|
|
103
|
+
errorMessages: validation.errors,
|
|
104
|
+
};
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
//# sourceMappingURL=ExactScripts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ExactScripts.js","sourceRoot":"","sources":["../../src/policies/ExactScripts.ts"],"names":[],"mappings":"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","sourcesContent":["import jsonfile from \"jsonfile\";\nimport type { PackageJson } from \"type-fest\";\nimport { definePackagePolicy } from \"../policyDefiners/definePackagePolicy.js\";\n\n/**\n * Configuration for the ExactScripts policy.\n *\n * @alpha\n */\nexport interface ExactScriptsConfig {\n\t/**\n\t * Scripts that must exist AND have exact content.\n\t *\n\t * @remarks\n\t * Unlike RequiredScripts, 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 * scripts: {\n\t * clean: \"rimraf dist esm\",\n\t * format: \"biome format --write .\",\n\t * }\n\t * ```\n\t */\n\tscripts: Record<string, string>;\n}\n\nconst POLICY_NAME = \"ExactScripts\";\n\ninterface ValidationResult {\n\terrors: string[];\n\tmissingScripts: string[];\n\tmismatchedScripts: string[];\n}\n\nfunction validateScripts(\n\tscripts: Record<string, string | undefined>,\n\texpectedScripts: Record<string, string>,\n): ValidationResult {\n\tconst errors: string[] = [];\n\tconst missingScripts: string[] = [];\n\tconst mismatchedScripts: string[] = [];\n\n\tfor (const [scriptName, expectedValue] of Object.entries(expectedScripts)) {\n\t\tif (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\tmissingScripts.push(scriptName);\n\t\t\terrors.push(`Missing required script: ${scriptName}`);\n\t\t}\n\t}\n\n\treturn { errors, missingScripts, mismatchedScripts };\n}\n\nasync function fixScripts(\n\tjson: PackageJson,\n\tfile: string,\n\tscriptsToFix: string[],\n\texpectedScripts: Record<string, string>,\n\texistingScripts: Record<string, string | undefined>,\n): Promise<string[]> {\n\tconst updatedScripts: Record<string, string> = {};\n\n\t// Copy existing scripts\n\tfor (const [key, value] of Object.entries(existingScripts)) {\n\t\tif (value !== undefined) {\n\t\t\tupdatedScripts[key] = value;\n\t\t}\n\t}\n\n\t// Add/update scripts\n\tconst fixedScripts: string[] = [];\n\tfor (const scriptName of scriptsToFix) {\n\t\tconst expectedValue = expectedScripts[scriptName];\n\t\tif (expectedValue !== undefined) {\n\t\t\tupdatedScripts[scriptName] = expectedValue;\n\t\t\tfixedScripts.push(scriptName);\n\t\t}\n\t}\n\n\tawait jsonfile.writeFile(\n\t\tfile,\n\t\t{ ...json, scripts: updatedScripts },\n\t\t{ spaces: 2 },\n\t);\n\n\treturn fixedScripts;\n}\n\n/**\n * A policy that validates scripts exist AND have exact content in package.json.\n *\n * @remarks\n * Use this policy when you want to enforce exact script content, not just presence.\n * This is useful for scripts that must be exactly the same across packages.\n *\n * For scripts where you only care about presence (not content), use RequiredScripts instead.\n *\n * @example\n * ```typescript\n * import { policy } from \"repopo\";\n * import { ExactScripts } from \"repopo/policies\";\n *\n * policy(ExactScripts, {\n * scripts: {\n * clean: \"rimraf dist esm\",\n * format: \"biome format --write .\",\n * lint: \"biome lint .\",\n * },\n * })\n * ```\n *\n * @alpha\n */\nexport const ExactScripts = definePackagePolicy<\n\tPackageJson,\n\tExactScriptsConfig | undefined\n>({\n\tname: POLICY_NAME,\n\tdescription:\n\t\t\"Validates that scripts exist and have exact content in package.json.\",\n\thandler: async (json, { file, resolve, config }) => {\n\t\tif (config === undefined || Object.keys(config.scripts).length === 0) {\n\t\t\treturn true;\n\t\t}\n\n\t\tconst scripts = json.scripts ?? {};\n\t\tconst validation = validateScripts(scripts, config.scripts);\n\n\t\tif (validation.errors.length === 0) {\n\t\t\treturn true;\n\t\t}\n\n\t\tconst scriptsToFix = [\n\t\t\t...validation.missingScripts,\n\t\t\t...validation.mismatchedScripts,\n\t\t];\n\t\tconst hasAutoFixableErrors = scriptsToFix.length > 0;\n\n\t\tif (resolve && hasAutoFixableErrors) {\n\t\t\ttry {\n\t\t\t\tconst fixedScripts = await fixScripts(\n\t\t\t\t\tjson,\n\t\t\t\t\tfile,\n\t\t\t\t\tscriptsToFix,\n\t\t\t\t\tconfig.scripts,\n\t\t\t\t\tscripts,\n\t\t\t\t);\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: true,\n\t\t\t\t\terrorMessages: [`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: validation.errors,\n\t\t};\n\t},\n});\n"]}
|
|
@@ -4,5 +4,5 @@ import { type FileHeaderPolicyConfig } from "../policyDefiners/defineFileHeaderP
|
|
|
4
4
|
*
|
|
5
5
|
* @alpha
|
|
6
6
|
*/
|
|
7
|
-
export declare const HtmlFileHeaders: import("../policy.js").
|
|
7
|
+
export declare const HtmlFileHeaders: import("../policy.js").PolicyShape<FileHeaderPolicyConfig>;
|
|
8
8
|
//# sourceMappingURL=HtmlFileHeaders.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HtmlFileHeaders.d.ts","sourceRoot":"","sources":["../../src/policies/HtmlFileHeaders.ts"],"names":[],"mappings":"AACA,OAAO,EAEN,KAAK,sBAAsB,EAC3B,MAAM,6CAA6C,CAAC;AAErD;;;;GAIG;AACH,eAAO,MAAM,eAAe,
|
|
1
|
+
{"version":3,"file":"HtmlFileHeaders.d.ts","sourceRoot":"","sources":["../../src/policies/HtmlFileHeaders.ts"],"names":[],"mappings":"AACA,OAAO,EAEN,KAAK,sBAAsB,EAC3B,MAAM,6CAA6C,CAAC;AAErD;;;;GAIG;AACH,eAAO,MAAM,eAAe,4DAkB1B,CAAC"}
|
|
@@ -4,5 +4,5 @@ import { type FileHeaderPolicyConfig } from "../policyDefiners/defineFileHeaderP
|
|
|
4
4
|
*
|
|
5
5
|
* @alpha
|
|
6
6
|
*/
|
|
7
|
-
export declare const JsTsFileHeaders: import("../policy.js").
|
|
7
|
+
export declare const JsTsFileHeaders: import("../policy.js").PolicyShape<FileHeaderPolicyConfig>;
|
|
8
8
|
//# sourceMappingURL=JsTsFileHeaders.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"JsTsFileHeaders.d.ts","sourceRoot":"","sources":["../../src/policies/JsTsFileHeaders.ts"],"names":[],"mappings":"AACA,OAAO,EAEN,KAAK,sBAAsB,EAC3B,MAAM,6CAA6C,CAAC;AAkBrD;;;;GAIG;AACH,eAAO,MAAM,eAAe,
|
|
1
|
+
{"version":3,"file":"JsTsFileHeaders.d.ts","sourceRoot":"","sources":["../../src/policies/JsTsFileHeaders.ts"],"names":[],"mappings":"AACA,OAAO,EAEN,KAAK,sBAAsB,EAC3B,MAAM,6CAA6C,CAAC;AAkBrD;;;;GAIG;AACH,eAAO,MAAM,eAAe,4DAY1B,CAAC"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration for the MutuallyExclusiveScripts policy.
|
|
3
|
+
*
|
|
4
|
+
* @alpha
|
|
5
|
+
*/
|
|
6
|
+
export interface MutuallyExclusiveScriptsConfig {
|
|
7
|
+
/**
|
|
8
|
+
* List of mutually exclusive script groups. Each group is an array of script names
|
|
9
|
+
* where at most one script from the group can be present.
|
|
10
|
+
*
|
|
11
|
+
* Packages can have zero scripts from the group (all optional), but cannot have
|
|
12
|
+
* multiple scripts from the same group.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```typescript
|
|
16
|
+
* // Allow "test:unit" OR "test:vitest" OR neither, but not both
|
|
17
|
+
* groups: [["test:unit", "test:vitest"]]
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
groups: string[][];
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* A policy that validates mutually exclusive script groups in package.json.
|
|
24
|
+
*
|
|
25
|
+
* @remarks
|
|
26
|
+
* Use this policy when you want to enforce that only one script from a group
|
|
27
|
+
* can exist in a package. This is useful for ensuring that packages don't have
|
|
28
|
+
* conflicting scripts (e.g., both "lint:eslint" and "lint:biome").
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```typescript
|
|
32
|
+
* import { policy } from "repopo";
|
|
33
|
+
* import { MutuallyExclusiveScripts } from "repopo/policies";
|
|
34
|
+
*
|
|
35
|
+
* policy(MutuallyExclusiveScripts, {
|
|
36
|
+
* groups: [
|
|
37
|
+
* ["lint:eslint", "lint:biome"],
|
|
38
|
+
* ["test:jest", "test:vitest"],
|
|
39
|
+
* ],
|
|
40
|
+
* })
|
|
41
|
+
* ```
|
|
42
|
+
*
|
|
43
|
+
* @alpha
|
|
44
|
+
*/
|
|
45
|
+
export declare const MutuallyExclusiveScripts: import("../policy.js").PolicyShape<MutuallyExclusiveScriptsConfig | undefined>;
|
|
46
|
+
//# sourceMappingURL=MutuallyExclusiveScripts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MutuallyExclusiveScripts.d.ts","sourceRoot":"","sources":["../../src/policies/MutuallyExclusiveScripts.ts"],"names":[],"mappings":"AAGA;;;;GAIG;AACH,MAAM,WAAW,8BAA8B;IAC9C;;;;;;;;;;;;OAYG;IACH,MAAM,EAAE,MAAM,EAAE,EAAE,CAAC;CACnB;AAID;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,wBAAwB,gFAsCnC,CAAC"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { definePackagePolicy } from "../policyDefiners/definePackagePolicy.js";
|
|
2
|
+
const POLICY_NAME = "MutuallyExclusiveScripts";
|
|
3
|
+
/**
|
|
4
|
+
* A policy that validates mutually exclusive script groups in package.json.
|
|
5
|
+
*
|
|
6
|
+
* @remarks
|
|
7
|
+
* Use this policy when you want to enforce that only one script from a group
|
|
8
|
+
* can exist in a package. This is useful for ensuring that packages don't have
|
|
9
|
+
* conflicting scripts (e.g., both "lint:eslint" and "lint:biome").
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* import { policy } from "repopo";
|
|
14
|
+
* import { MutuallyExclusiveScripts } from "repopo/policies";
|
|
15
|
+
*
|
|
16
|
+
* policy(MutuallyExclusiveScripts, {
|
|
17
|
+
* groups: [
|
|
18
|
+
* ["lint:eslint", "lint:biome"],
|
|
19
|
+
* ["test:jest", "test:vitest"],
|
|
20
|
+
* ],
|
|
21
|
+
* })
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* @alpha
|
|
25
|
+
*/
|
|
26
|
+
export const MutuallyExclusiveScripts = definePackagePolicy({
|
|
27
|
+
name: POLICY_NAME,
|
|
28
|
+
description: "Validates that at most one script from each exclusive group exists in package.json.",
|
|
29
|
+
handler: async (json, { file, config }) => {
|
|
30
|
+
if (config === undefined || config.groups.length === 0) {
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
const scripts = json.scripts ?? {};
|
|
34
|
+
const errors = [];
|
|
35
|
+
for (const group of config.groups) {
|
|
36
|
+
const presentScripts = group.filter((scriptName) => Object.hasOwn(scripts, scriptName));
|
|
37
|
+
if (presentScripts.length > 1) {
|
|
38
|
+
errors.push(`Scripts are mutually exclusive, but found ${presentScripts.length}: ${presentScripts.join(", ")}`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
if (errors.length === 0) {
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
return {
|
|
45
|
+
name: POLICY_NAME,
|
|
46
|
+
file,
|
|
47
|
+
autoFixable: false,
|
|
48
|
+
errorMessages: errors,
|
|
49
|
+
};
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
//# sourceMappingURL=MutuallyExclusiveScripts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MutuallyExclusiveScripts.js","sourceRoot":"","sources":["../../src/policies/MutuallyExclusiveScripts.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,MAAM,0CAA0C,CAAC;AAwB/E,MAAM,WAAW,GAAG,0BAA0B,CAAC;AAE/C;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,mBAAmB,CAGzD;IACD,IAAI,EAAE,WAAW;IACjB,WAAW,EACV,qFAAqF;IACtF,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE;QACzC,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACxD,OAAO,IAAI,CAAC;QACb,CAAC;QAED,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,IAAI,EAAE,CAAC;QACnC,MAAM,MAAM,GAAa,EAAE,CAAC;QAE5B,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;YACnC,MAAM,cAAc,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,UAAU,EAAE,EAAE,CAClD,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,CAClC,CAAC;YAEF,IAAI,cAAc,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC/B,MAAM,CAAC,IAAI,CACV,6CAA6C,cAAc,CAAC,MAAM,KAAK,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAClG,CAAC;YACH,CAAC;QACF,CAAC;QAED,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACzB,OAAO,IAAI,CAAC;QACb,CAAC;QAED,OAAO;YACN,IAAI,EAAE,WAAW;YACjB,IAAI;YACJ,WAAW,EAAE,KAAK;YAClB,aAAa,EAAE,MAAM;SACrB,CAAC;IACH,CAAC;CACD,CAAC,CAAC","sourcesContent":["import type { PackageJson } from \"type-fest\";\nimport { definePackagePolicy } from \"../policyDefiners/definePackagePolicy.js\";\n\n/**\n * Configuration for the MutuallyExclusiveScripts policy.\n *\n * @alpha\n */\nexport interface MutuallyExclusiveScriptsConfig {\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 * groups: [[\"test:unit\", \"test:vitest\"]]\n\t * ```\n\t */\n\tgroups: string[][];\n}\n\nconst POLICY_NAME = \"MutuallyExclusiveScripts\";\n\n/**\n * A policy that validates mutually exclusive script groups in package.json.\n *\n * @remarks\n * Use this policy when you want to enforce that only one script from a group\n * can exist in a package. This is useful for ensuring that packages don't have\n * conflicting scripts (e.g., both \"lint:eslint\" and \"lint:biome\").\n *\n * @example\n * ```typescript\n * import { policy } from \"repopo\";\n * import { MutuallyExclusiveScripts } from \"repopo/policies\";\n *\n * policy(MutuallyExclusiveScripts, {\n * groups: [\n * [\"lint:eslint\", \"lint:biome\"],\n * [\"test:jest\", \"test:vitest\"],\n * ],\n * })\n * ```\n *\n * @alpha\n */\nexport const MutuallyExclusiveScripts = definePackagePolicy<\n\tPackageJson,\n\tMutuallyExclusiveScriptsConfig | undefined\n>({\n\tname: POLICY_NAME,\n\tdescription:\n\t\t\"Validates that at most one script from each exclusive group exists in package.json.\",\n\thandler: async (json, { file, config }) => {\n\t\tif (config === undefined || config.groups.length === 0) {\n\t\t\treturn true;\n\t\t}\n\n\t\tconst scripts = json.scripts ?? {};\n\t\tconst errors: string[] = [];\n\n\t\tfor (const group of config.groups) {\n\t\t\tconst presentScripts = group.filter((scriptName) =>\n\t\t\t\tObject.hasOwn(scripts, scriptName),\n\t\t\t);\n\n\t\t\tif (presentScripts.length > 1) {\n\t\t\t\terrors.push(\n\t\t\t\t\t`Scripts are mutually exclusive, but found ${presentScripts.length}: ${presentScripts.join(\", \")}`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tif (errors.length === 0) {\n\t\t\treturn true;\n\t\t}\n\n\t\treturn {\n\t\t\tname: POLICY_NAME,\n\t\t\tfile,\n\t\t\tautoFixable: false,\n\t\t\terrorMessages: errors,\n\t\t};\n\t},\n});\n"]}
|
|
@@ -33,5 +33,5 @@ export interface NoPrivateWorkspaceDependenciesSettings {
|
|
|
33
33
|
* });
|
|
34
34
|
* ```
|
|
35
35
|
*/
|
|
36
|
-
export declare const NoPrivateWorkspaceDependencies: import("../policy.js").
|
|
36
|
+
export declare const NoPrivateWorkspaceDependencies: import("../policy.js").PolicyShape<NoPrivateWorkspaceDependenciesSettings | undefined>;
|
|
37
37
|
//# sourceMappingURL=NoPrivateWorkspaceDependencies.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NoPrivateWorkspaceDependencies.d.ts","sourceRoot":"","sources":["../../src/policies/NoPrivateWorkspaceDependencies.ts"],"names":[],"mappings":"AAYA;;GAEG;AACH,MAAM,WAAW,sCAAsC;IACtD;;;OAGG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAC/B;AA6ED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,eAAO,MAAM,8BAA8B,
|
|
1
|
+
{"version":3,"file":"NoPrivateWorkspaceDependencies.d.ts","sourceRoot":"","sources":["../../src/policies/NoPrivateWorkspaceDependencies.ts"],"names":[],"mappings":"AAYA;;GAEG;AACH,MAAM,WAAW,sCAAsC;IACtD;;;OAGG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAC/B;AA6ED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,eAAO,MAAM,8BAA8B,wFAyEzC,CAAC"}
|
|
@@ -84,5 +84,5 @@ export interface PackageAllowedScopesConfig {
|
|
|
84
84
|
*
|
|
85
85
|
* @alpha
|
|
86
86
|
*/
|
|
87
|
-
export declare const PackageAllowedScopes: import("../policy.js").
|
|
87
|
+
export declare const PackageAllowedScopes: import("../policy.js").PolicyShape<PackageAllowedScopesConfig | undefined>;
|
|
88
88
|
//# sourceMappingURL=PackageAllowedScopes.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PackageAllowedScopes.d.ts","sourceRoot":"","sources":["../../src/policies/PackageAllowedScopes.ts"],"names":[],"mappings":"AAKA;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,WAAW,0BAA0B;IAC1C;;;;;;;;;;;;;;;OAeG;IACH,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IAEzB;;;;;;;;;;;;;OAaG;IACH,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC5B;AAiDD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,eAAO,MAAM,oBAAoB,
|
|
1
|
+
{"version":3,"file":"PackageAllowedScopes.d.ts","sourceRoot":"","sources":["../../src/policies/PackageAllowedScopes.ts"],"names":[],"mappings":"AAKA;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,WAAW,0BAA0B;IAC1C;;;;;;;;;;;;;;;OAeG;IACH,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IAEzB;;;;;;;;;;;;;OAaG;IACH,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC5B;AAiDD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,eAAO,MAAM,oBAAoB,4EAuD/B,CAAC"}
|
|
@@ -96,5 +96,5 @@ export interface PackageEsmTypeConfig {
|
|
|
96
96
|
*
|
|
97
97
|
* @alpha
|
|
98
98
|
*/
|
|
99
|
-
export declare const PackageEsmType: import("../policy.js").
|
|
99
|
+
export declare const PackageEsmType: import("../policy.js").PolicyShape<PackageEsmTypeConfig | undefined>;
|
|
100
100
|
//# sourceMappingURL=PackageEsmType.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PackageEsmType.d.ts","sourceRoot":"","sources":["../../src/policies/PackageEsmType.ts"],"names":[],"mappings":"AAQA;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,WAAW,oBAAoB;IACpC;;;;;;;;;;;OAWG;IACH,YAAY,CAAC,EAAE,QAAQ,GAAG,UAAU,CAAC;IAErC;;;;;;;;;;OAUG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAE5B;;;;;;;OAOG;IACH,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAE3B;;;;;;;;;;OAUG;IACH,kBAAkB,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;CAC9C;AAyPD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,eAAO,MAAM,cAAc,
|
|
1
|
+
{"version":3,"file":"PackageEsmType.d.ts","sourceRoot":"","sources":["../../src/policies/PackageEsmType.ts"],"names":[],"mappings":"AAQA;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,WAAW,oBAAoB;IACpC;;;;;;;;;;;OAWG;IACH,YAAY,CAAC,EAAE,QAAQ,GAAG,UAAU,CAAC;IAErC;;;;;;;;;;OAUG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAE5B;;;;;;;OAOG;IACH,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAE3B;;;;;;;;;;OAUG;IACH,kBAAkB,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;CAC9C;AAyPD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,eAAO,MAAM,cAAc,sEAgEzB,CAAC"}
|
|
@@ -93,5 +93,5 @@ export interface PackageFolderNameConfig {
|
|
|
93
93
|
*
|
|
94
94
|
* @alpha
|
|
95
95
|
*/
|
|
96
|
-
export declare const PackageFolderName: import("../policy.js").
|
|
96
|
+
export declare const PackageFolderName: import("../policy.js").PolicyShape<PackageFolderNameConfig | undefined>;
|
|
97
97
|
//# sourceMappingURL=PackageFolderName.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PackageFolderName.d.ts","sourceRoot":"","sources":["../../src/policies/PackageFolderName.ts"],"names":[],"mappings":"AAOA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,WAAW,uBAAuB;IACvC;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;IACH,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IAEvB;;;;;;;;;;OAUG;IACH,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;CAC3B;AAuCD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,eAAO,MAAM,iBAAiB,
|
|
1
|
+
{"version":3,"file":"PackageFolderName.d.ts","sourceRoot":"","sources":["../../src/policies/PackageFolderName.ts"],"names":[],"mappings":"AAOA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,WAAW,uBAAuB;IACvC;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;IACH,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IAEvB;;;;;;;;;;OAUG;IACH,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;CAC3B;AAuCD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,eAAO,MAAM,iBAAiB,yEAqD5B,CAAC"}
|