-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathdefinePackagePolicy.ts
More file actions
125 lines (115 loc) · 3.17 KB
/
Copy pathdefinePackagePolicy.ts
File metadata and controls
125 lines (115 loc) · 3.17 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
import { call, type Operation } from "effection";
import jsonfile from "jsonfile";
import type { PackageJson } from "type-fest";
import { PackageJsonRegexMatch } from "../policies/constants.js";
import type {
PolicyArgs,
PolicyHandlerResult,
PolicyShape,
} from "../policy.js";
import { resolveRepoFilePath } from "../utils/safePaths.js";
const { readFile: readJson } = jsonfile;
/**
* A policy handler especially for policies that target package.json.
*
* @remarks
* Package JSON handlers can be implemented in two ways:
* - As an async function returning a Promise
* - As an Effection generator function returning an Operation
*
* Both styles are supported to allow gradual migration and flexibility.
*
* @alpha
*/
export type PackageJsonHandler<J, C> = (
json: J,
args: PolicyArgs<C>,
) => Operation<PolicyHandlerResult> | Promise<PolicyHandlerResult>;
/**
* Type guard to check if a value is an Effection Operation (generator).
*/
function isOperation<T>(value: unknown): value is Operation<T> {
return (
typeof value === "object" &&
value !== null &&
"next" in value &&
typeof (value as { next: unknown }).next === "function"
);
}
/**
* Input arguments for defining a package.json policy.
*
* @alpha
*/
export interface DefinePackagePolicyArgs<J, C> {
/**
* The name of the policy.
*/
name: string;
/**
* A description of the policy's purpose.
*/
description: string;
/**
* The handler function that receives the parsed package.json and policy arguments.
*/
handler: PackageJsonHandler<J, C>;
/**
* Optional default configuration for the policy.
*/
defaultConfig?: C;
}
/**
* Define a repo policy for package.json files.
*
* @remarks
* This is a helper function that creates a policy pre-configured to match
* package.json files. The handler receives the parsed JSON content.
*
* @example
* ```typescript
* const MyPackagePolicy = definePackagePolicy({
* name: "MyPackagePolicy",
* description: "Ensures package.json has required fields",
* handler: async (json, { file }) => {
* if (!json.name) {
* return { error: "Missing name", fixable: false };
* }
* return true;
* },
* });
* ```
*
* @alpha
*/
export function definePackagePolicy<J = PackageJson, C = undefined>(
args: DefinePackagePolicyArgs<J, C>,
): PolicyShape<C> {
const { name, description, handler: packageHandler, defaultConfig } = args;
return {
name,
description,
match: PackageJsonRegexMatch,
defaultConfig,
handler: function* (innerArgs) {
const filePath = resolveRepoFilePath(innerArgs.root, innerArgs.file);
const json: J = yield* call(() => readJson(filePath));
const result = packageHandler(json, innerArgs);
// Handle both Operation (generator) and Promise return types
if (result instanceof Promise) {
return yield* call(() => result);
}
// Check if it's an Effection Operation (generator)
if (isOperation<PolicyHandlerResult>(result)) {
return yield* result;
}
// This should never happen with proper handler typing
throw new Error(`Unexpected handler result type: ${typeof result}`);
},
};
}
/**
* Alias for definePackagePolicy.
* @alpha
*/
export const packagePolicy = definePackagePolicy;