repopo 0.2.2 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/README.md +20 -5
  3. package/esm/baseCommand.d.ts +52 -0
  4. package/esm/baseCommand.d.ts.map +1 -0
  5. package/esm/baseCommand.js +87 -0
  6. package/esm/baseCommand.js.map +1 -0
  7. package/esm/commands/check.d.ts +7 -11
  8. package/esm/commands/check.d.ts.map +1 -1
  9. package/esm/commands/check.js +20 -150
  10. package/esm/commands/check.js.map +1 -1
  11. package/esm/commands/list.d.ts +13 -0
  12. package/esm/commands/list.d.ts.map +1 -0
  13. package/esm/commands/list.js +19 -0
  14. package/esm/commands/list.js.map +1 -0
  15. package/esm/config.d.ts +9 -9
  16. package/esm/config.d.ts.map +1 -1
  17. package/esm/config.js +7 -1
  18. package/esm/config.js.map +1 -1
  19. package/esm/index.d.ts +1 -1
  20. package/esm/index.d.ts.map +1 -1
  21. package/esm/index.js.map +1 -1
  22. package/esm/perf.d.ts +12 -0
  23. package/esm/perf.d.ts.map +1 -0
  24. package/esm/perf.js +31 -0
  25. package/esm/perf.js.map +1 -0
  26. package/esm/policies/PackageJsonProperties.d.ts.map +1 -1
  27. package/esm/policies/PackageJsonProperties.js +2 -1
  28. package/esm/policies/PackageJsonProperties.js.map +1 -1
  29. package/esm/policies/PackageJsonRepoDirectoryProperty.d.ts.map +1 -1
  30. package/esm/policies/PackageJsonRepoDirectoryProperty.js +2 -1
  31. package/esm/policies/PackageJsonRepoDirectoryProperty.js.map +1 -1
  32. package/esm/policies/PackageJsonSortedPolicy.d.ts.map +1 -1
  33. package/esm/policies/PackageJsonSortedPolicy.js +2 -1
  34. package/esm/policies/PackageJsonSortedPolicy.js.map +1 -1
  35. package/esm/policy.d.ts +17 -0
  36. package/esm/policy.d.ts.map +1 -1
  37. package/esm/policy.js +0 -12
  38. package/esm/policy.js.map +1 -1
  39. package/oclif.manifest.json +65 -32
  40. package/package.json +6 -6
package/CHANGELOG.md CHANGED
@@ -1,5 +1,45 @@
1
1
  # repopo
2
2
 
3
+ ## 0.3.1
4
+
5
+ ### Patch Changes
6
+
7
+ <details><summary>Updated 2 dependencies</summary>
8
+
9
+ <small>
10
+
11
+ [`b894a2d`](https://github.com/tylerbutler/tools-monorepo/commit/b894a2dfd5538247d1a625b423e61b437207f137) [`b894a2d`](https://github.com/tylerbutler/tools-monorepo/commit/b894a2dfd5538247d1a625b423e61b437207f137)
12
+
13
+ </small>
14
+
15
+ - `@tylerbu/cli-api@0.6.0`
16
+ - `sort-tsconfig@0.1.3`
17
+
18
+ </details>
19
+
20
+ ## 0.3.0
21
+
22
+ ### Minor Changes
23
+
24
+ - New command: `list` _[`#132`](https://github.com/tylerbutler/tools-monorepo/pull/132) [`1fa083d`](https://github.com/tylerbutler/tools-monorepo/commit/1fa083dd64499d108411326377a4463ad6acb040) [@tylerbutler](https://github.com/tylerbutler)_
25
+
26
+ The `repopo list` command lists the policies that are enabled in the repo.
27
+
28
+ ### Patch Changes
29
+
30
+ <details><summary>Updated 2 dependencies</summary>
31
+
32
+ <small>
33
+
34
+ [`f803610`](https://github.com/tylerbutler/tools-monorepo/commit/f803610f64936c5d49d862b2f4240ea248fe3f76)
35
+
36
+ </small>
37
+
38
+ - `@tylerbu/cli-api@0.5.0`
39
+ - `sort-tsconfig@0.1.2`
40
+
41
+ </details>
42
+
3
43
  ## 0.2.2
4
44
 
5
45
  ### Patch Changes
package/README.md CHANGED
@@ -76,6 +76,7 @@ field is a string instead of an object the package will be ignored.
76
76
 
77
77
  <!-- commands -->
78
78
  * [`repopo check`](#repopo-check)
79
+ * [`repopo list`](#repopo-list)
79
80
 
80
81
  ## `repopo check`
81
82
 
@@ -83,7 +84,7 @@ Checks and applies policies to the files in the repository.
83
84
 
84
85
  ```
85
86
  USAGE
86
- $ repopo check [-v | --quiet] [-f] [-D <value>... | -d <value>] [-p <value>] [--stdin]
87
+ $ repopo check [-f] [--stdin] [-D <value>... | -d <value>] [-p <value>]
87
88
 
88
89
  FLAGS
89
90
  -D, --excludePolicy=<value>... Exclude policies by name. Can be specified multiple times to exclude multiple
@@ -93,11 +94,25 @@ FLAGS
93
94
  -f, --fix Fix errors if possible.
94
95
  -p, --path=<value> Filter file paths by <regex>.
95
96
  --stdin Read list of files from stdin.
96
-
97
- LOGGING FLAGS
98
- -v, --verbose Enable verbose logging.
99
- --quiet Disable all logging.
100
97
  ```
101
98
 
102
99
  _See code: [src/commands/check.ts](https://github.com/tylerbutler/tools-monorepo/blob/main/packages/repopo/src/commands/check.ts)_
100
+
101
+ ## `repopo list`
102
+
103
+ Lists the policies configured to run.
104
+
105
+ ```
106
+ USAGE
107
+ $ repopo list [-D <value>... | -d <value>] [-p <value>]
108
+
109
+ FLAGS
110
+ -D, --excludePolicy=<value>... Exclude policies by name. Can be specified multiple times to exclude multiple
111
+ policies.
112
+ -d, --policy=<value> Filter policies to apply by <regex>. Only policies with a name matching the regex will
113
+ be applied.
114
+ -p, --path=<value> Filter file paths by <regex>.
115
+ ```
116
+
117
+ _See code: [src/commands/list.ts](https://github.com/tylerbutler/tools-monorepo/blob/main/packages/repopo/src/commands/list.ts)_
103
118
  <!-- commandsstop -->
@@ -0,0 +1,52 @@
1
+ import { GitCommand } from "@tylerbu/cli-api";
2
+ import type { PolicyConfig } from "./config.js";
3
+ import { type PolicyHandlerPerfStats } from "./perf.js";
4
+ import { type PolicyName, type RepoPolicy } from "./policy.js";
5
+ /**
6
+ * A convenience interface used to pass commonly used parameters between functions.
7
+ */
8
+ export interface PolicyCommandContext {
9
+ /**
10
+ * A regular expression used to filter selected files.
11
+ */
12
+ pathRegex: RegExp;
13
+ /**
14
+ * A list of regular expressions used to exclude files from all handlers.
15
+ */
16
+ excludeFiles: RegExp[];
17
+ /**
18
+ * A list of handlers to apply to selected files.
19
+ */
20
+ policies: RepoPolicy[];
21
+ /**
22
+ * A per-handler list of regular expressions used to exclude files from specific handlers.
23
+ */
24
+ excludePoliciesForFiles: ExcludedPolicyFileMap;
25
+ /**
26
+ * Path to the root of the git repo.
27
+ */
28
+ gitRoot: string;
29
+ /**
30
+ * Performance information for each handler.
31
+ */
32
+ perfStats: PolicyHandlerPerfStats;
33
+ }
34
+ /**
35
+ * This command lists all the policies configured to run.
36
+ */
37
+ export declare abstract class BaseRepopoCommand<T extends typeof BaseRepopoCommand & {
38
+ args: typeof GitCommand.args;
39
+ flags: typeof GitCommand.flags;
40
+ }> extends GitCommand<T, PolicyConfig> {
41
+ static readonly flags: {
42
+ readonly config: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
43
+ readonly policy: import("@oclif/core/interfaces").OptionFlag<RegExp | undefined, import("@oclif/core/interfaces").CustomOptions>;
44
+ readonly excludePolicy: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
45
+ readonly path: import("@oclif/core/interfaces").OptionFlag<RegExp | undefined, import("@oclif/core/interfaces").CustomOptions>;
46
+ };
47
+ private context;
48
+ protected getContext(): PolicyCommandContext;
49
+ init(): Promise<void>;
50
+ }
51
+ export type ExcludedPolicyFileMap = Map<PolicyName, RegExp[]>;
52
+ //# sourceMappingURL=baseCommand.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"baseCommand.d.ts","sourceRoot":"","sources":["../src/baseCommand.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAA2B,MAAM,kBAAkB,CAAC;AAEvE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,KAAK,sBAAsB,EAAgB,MAAM,WAAW,CAAC;AACtE,OAAO,EAAmB,KAAK,UAAU,EAAE,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AAIhF;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACpC;;OAEG;IACH,SAAS,EAAE,MAAM,CAAC;IAElB;;OAEG;IACH,YAAY,EAAE,MAAM,EAAE,CAAC;IAEvB;;OAEG;IACH,QAAQ,EAAE,UAAU,EAAE,CAAC;IAEvB;;OAEG;IACH,uBAAuB,EAAE,qBAAqB,CAAC;IAE/C;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;IAEhB;;OAEG;IACH,SAAS,EAAE,sBAAsB,CAAC;CAClC;AAED;;GAEG;AACH,8BAAsB,iBAAiB,CACtC,CAAC,SAAS,OAAO,iBAAiB,GAAG;IACpC,IAAI,EAAE,OAAO,UAAU,CAAC,IAAI,CAAC;IAC7B,KAAK,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;CAC/B,CACA,SAAQ,UAAU,CAAC,CAAC,EAAE,YAAY,CAAC;IACpC,gBAAyB,KAAK;gCAlCmB,wBAAwB,wCAKtE,wBAAuB;;;;MAiDf;IAEX,OAAO,CAAC,OAAO,CAAmC;IAElD,SAAS,CAAC,UAAU,IAAI,oBAAoB;IAOtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAoD3C;AAED,MAAM,MAAM,qBAAqB,GAAG,GAAG,CAAC,UAAU,EAAE,MAAM,EAAE,CAAC,CAAC"}
@@ -0,0 +1,87 @@
1
+ import { Flags } from "@oclif/core";
2
+ import { GitCommand, RegExpFlag, findGitRoot } from "@tylerbu/cli-api";
3
+ import { newPerfStats } from "./perf.js";
4
+ import { DefaultPolicies } from "./policy.js";
5
+ const DEFAULT_PATH_REGEX = /.?/;
6
+ /**
7
+ * This command lists all the policies configured to run.
8
+ */
9
+ export class BaseRepopoCommand extends GitCommand {
10
+ static flags = {
11
+ policy: RegExpFlag({
12
+ description: "Filter policies to apply by <regex>. Only policies with a name matching the regex will be applied.",
13
+ required: false,
14
+ char: "d",
15
+ }),
16
+ excludePolicy: Flags.string({
17
+ char: "D",
18
+ description: "Exclude policies by name. Can be specified multiple times to exclude multiple policies.",
19
+ exclusive: ["policy"],
20
+ multiple: true,
21
+ }),
22
+ path: RegExpFlag({
23
+ description: "Filter file paths by <regex>.",
24
+ required: false,
25
+ char: "p",
26
+ }),
27
+ ...GitCommand.flags,
28
+ };
29
+ context;
30
+ getContext() {
31
+ if (this.context === undefined) {
32
+ throw new Error("context undefined");
33
+ }
34
+ return this.context;
35
+ }
36
+ async init() {
37
+ await super.init();
38
+ const { flags } = this;
39
+ const config = await this.loadConfig();
40
+ let policies = config?.policies ?? DefaultPolicies;
41
+ policies =
42
+ policies.filter((h) => {
43
+ if (flags.excludePolicy === undefined ||
44
+ flags.excludePolicy.length === 0) {
45
+ return true;
46
+ }
47
+ const shouldRun = flags.excludePolicy?.includes(h.name) === false;
48
+ if (!shouldRun) {
49
+ this.info(`Disabled policy: ${h.name}`);
50
+ }
51
+ return shouldRun;
52
+ }) ?? [];
53
+ if (flags.policy !== undefined) {
54
+ this.info(`Filtering handlers by regex: ${flags.policy}`);
55
+ policies = policies.filter((h) => flags.policy?.test(h.name));
56
+ }
57
+ const excludeFiles = config?.excludeFiles?.map((e) => toRegExp(e)) ?? [];
58
+ const excludePoliciesForFilesRaw = config?.excludePoliciesForFiles;
59
+ const excludePoliciesForFiles = new Map();
60
+ if (excludePoliciesForFilesRaw) {
61
+ for (const [policyName, filters] of Object.entries(excludePoliciesForFilesRaw)) {
62
+ const regexes = filters.map((e) => toRegExp(e));
63
+ excludePoliciesForFiles.set(policyName, regexes);
64
+ }
65
+ }
66
+ const gitRoot = await findGitRoot();
67
+ const pathRegex = this.flags.path ?? DEFAULT_PATH_REGEX;
68
+ if (this.flags.path !== undefined) {
69
+ this.info(`Filtering file paths by regex: ${pathRegex}`);
70
+ }
71
+ this.context = {
72
+ pathRegex,
73
+ excludeFiles,
74
+ policies,
75
+ excludePoliciesForFiles,
76
+ gitRoot,
77
+ perfStats: newPerfStats(),
78
+ };
79
+ }
80
+ }
81
+ function toRegExp(input) {
82
+ if (typeof input === "string") {
83
+ return new RegExp(input, "i");
84
+ }
85
+ return input;
86
+ }
87
+ //# sourceMappingURL=baseCommand.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"baseCommand.js","sourceRoot":"","sources":["../src/baseCommand.ts"],"names":[],"mappings":"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","sourcesContent":["import { Flags } from \"@oclif/core\";\nimport { GitCommand, RegExpFlag, findGitRoot } from \"@tylerbu/cli-api\";\n\nimport type { PolicyConfig } from \"./config.js\";\nimport { type PolicyHandlerPerfStats, newPerfStats } from \"./perf.js\";\nimport { DefaultPolicies, type PolicyName, type RepoPolicy } from \"./policy.js\";\n\nconst DEFAULT_PATH_REGEX = /.?/;\n\n/**\n * A convenience interface used to pass commonly used parameters between functions.\n */\nexport interface PolicyCommandContext {\n\t/**\n\t * A regular expression used to filter selected files.\n\t */\n\tpathRegex: RegExp;\n\n\t/**\n\t * A list of regular expressions used to exclude files from all handlers.\n\t */\n\texcludeFiles: RegExp[];\n\n\t/**\n\t * A list of handlers to apply to selected files.\n\t */\n\tpolicies: RepoPolicy[];\n\n\t/**\n\t * A per-handler list of regular expressions used to exclude files from specific handlers.\n\t */\n\texcludePoliciesForFiles: ExcludedPolicyFileMap;\n\n\t/**\n\t * Path to the root of the git repo.\n\t */\n\tgitRoot: string;\n\n\t/**\n\t * Performance information for each handler.\n\t */\n\tperfStats: PolicyHandlerPerfStats;\n}\n\n/**\n * This command lists all the policies configured to run.\n */\nexport abstract class BaseRepopoCommand<\n\tT extends typeof BaseRepopoCommand & {\n\t\targs: typeof GitCommand.args;\n\t\tflags: typeof GitCommand.flags;\n\t},\n> extends GitCommand<T, PolicyConfig> {\n\tstatic override readonly flags = {\n\t\tpolicy: RegExpFlag({\n\t\t\tdescription:\n\t\t\t\t\"Filter policies to apply by <regex>. Only policies with a name matching the regex will be applied.\",\n\t\t\trequired: false,\n\t\t\tchar: \"d\",\n\t\t}),\n\t\texcludePolicy: Flags.string({\n\t\t\tchar: \"D\",\n\t\t\tdescription:\n\t\t\t\t\"Exclude policies by name. Can be specified multiple times to exclude multiple policies.\",\n\t\t\texclusive: [\"policy\"],\n\t\t\tmultiple: true,\n\t\t}),\n\t\tpath: RegExpFlag({\n\t\t\tdescription: \"Filter file paths by <regex>.\",\n\t\t\trequired: false,\n\t\t\tchar: \"p\",\n\t\t}),\n\t\t...GitCommand.flags,\n\t} as const;\n\n\tprivate context: PolicyCommandContext | undefined;\n\n\tprotected getContext(): PolicyCommandContext {\n\t\tif (this.context === undefined) {\n\t\t\tthrow new Error(\"context undefined\");\n\t\t}\n\t\treturn this.context;\n\t}\n\n\tpublic override async init(): Promise<void> {\n\t\tawait super.init();\n\t\tconst { flags } = this;\n\t\tconst config = await this.loadConfig();\n\t\tlet policies = config?.policies ?? DefaultPolicies;\n\t\tpolicies =\n\t\t\tpolicies.filter((h) => {\n\t\t\t\tif (\n\t\t\t\t\tflags.excludePolicy === undefined ||\n\t\t\t\t\tflags.excludePolicy.length === 0\n\t\t\t\t) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\tconst shouldRun = flags.excludePolicy?.includes(h.name) === false;\n\t\t\t\tif (!shouldRun) {\n\t\t\t\t\tthis.info(`Disabled policy: ${h.name}`);\n\t\t\t\t}\n\t\t\t\treturn shouldRun;\n\t\t\t}) ?? [];\n\t\tif (flags.policy !== undefined) {\n\t\t\tthis.info(`Filtering handlers by regex: ${flags.policy}`);\n\t\t\tpolicies = policies.filter((h) => flags.policy?.test(h.name));\n\t\t}\n\n\t\tconst excludeFiles: RegExp[] =\n\t\t\tconfig?.excludeFiles?.map((e) => toRegExp(e)) ?? [];\n\t\tconst excludePoliciesForFilesRaw = config?.excludePoliciesForFiles;\n\t\tconst excludePoliciesForFiles: ExcludedPolicyFileMap = new Map();\n\t\tif (excludePoliciesForFilesRaw) {\n\t\t\tfor (const [policyName, filters] of Object.entries(\n\t\t\t\texcludePoliciesForFilesRaw,\n\t\t\t)) {\n\t\t\t\tconst regexes = filters.map((e) => toRegExp(e));\n\t\t\t\texcludePoliciesForFiles.set(policyName, regexes);\n\t\t\t}\n\t\t}\n\t\tconst gitRoot = await findGitRoot();\n\n\t\tconst pathRegex = this.flags.path ?? DEFAULT_PATH_REGEX;\n\t\tif (this.flags.path !== undefined) {\n\t\t\tthis.info(`Filtering file paths by regex: ${pathRegex}`);\n\t\t}\n\n\t\tthis.context = {\n\t\t\tpathRegex,\n\t\t\texcludeFiles,\n\t\t\tpolicies,\n\t\t\texcludePoliciesForFiles,\n\t\t\tgitRoot,\n\t\t\tperfStats: newPerfStats(),\n\t\t};\n\t}\n}\n\nexport type ExcludedPolicyFileMap = Map<PolicyName, RegExp[]>;\n\nfunction toRegExp(input: string | RegExp): RegExp {\n\tif (typeof input === \"string\") {\n\t\treturn new RegExp(input, \"i\");\n\t}\n\treturn input;\n}\n"]}
@@ -1,5 +1,4 @@
1
- import { GitCommand } from "@tylerbu/cli-api";
2
- import { type PolicyConfig } from "../config.js";
1
+ import { BaseRepopoCommand } from "../baseCommand.js";
3
2
  /**
4
3
  * This tool enforces policies across the code base via a series of handler functions. The handler functions are
5
4
  * associated with a regular expression, and all files matching that expression are passed to the handler function.
@@ -9,24 +8,22 @@ import { type PolicyConfig } from "../config.js";
9
8
  *
10
9
  * `git ls-files -co --exclude-standard --full-name | repopo check --stdin --verbose`
11
10
  */
12
- export declare class CheckPolicy extends GitCommand<typeof CheckPolicy & {
13
- args: typeof GitCommand.args;
14
- flags: typeof GitCommand.flags;
15
- }, PolicyConfig> {
11
+ export declare class CheckPolicy<T extends typeof BaseRepopoCommand & {
12
+ args: typeof CheckPolicy.args;
13
+ flags: typeof CheckPolicy.flags;
14
+ }> extends BaseRepopoCommand<T> {
16
15
  static readonly summary = "Checks and applies policies to the files in the repository.";
17
16
  static readonly flags: {
18
17
  readonly config: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
19
- readonly fix: import("@oclif/core/interfaces").BooleanFlag<boolean>;
20
18
  readonly policy: import("@oclif/core/interfaces").OptionFlag<RegExp | undefined, import("@oclif/core/interfaces").CustomOptions>;
21
19
  readonly excludePolicy: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
22
20
  readonly path: import("@oclif/core/interfaces").OptionFlag<RegExp | undefined, import("@oclif/core/interfaces").CustomOptions>;
21
+ readonly fix: import("@oclif/core/interfaces").BooleanFlag<boolean>;
23
22
  readonly stdin: import("@oclif/core/interfaces").BooleanFlag<boolean>;
24
23
  };
25
24
  private processed;
26
25
  private count;
27
- private commandContext;
28
- get defaultConfig(): import("type-fest").SetOptional<Required<PolicyConfig>, "policies" | "policySettings">;
29
- protected loadConfig(): Promise<PolicyConfig>;
26
+ defaultConfig: import("../config.js").PolicyConfig;
30
27
  init(): Promise<void>;
31
28
  run(): Promise<void>;
32
29
  private executePolicy;
@@ -36,7 +33,6 @@ export declare class CheckPolicy extends GitCommand<typeof CheckPolicy & {
36
33
  * decision for all handlers.
37
34
  */
38
35
  private routeToHandlers;
39
- private logStats;
40
36
  /**
41
37
  * Given a string that represents a path to a file in the repo, determines if the file should be checked, and if so,
42
38
  * routes the file to the appropriate handlers.
@@ -1 +1 @@
1
- {"version":3,"file":"check.d.ts","sourceRoot":"","sources":["../../src/commands/check.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,UAAU,EAA2B,MAAM,kBAAkB,CAAC;AAGvE,OAAO,EAAuB,KAAK,YAAY,EAAE,MAAM,cAAc,CAAC;AAoDtE;;;;;;;;GAQG;AACH,qBAAa,WAAY,SAAQ,UAAU,CAC1C,OAAO,WAAW,GAAG;IACpB,IAAI,EAAE,OAAO,UAAU,CAAC,IAAI,CAAC;IAC7B,KAAK,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;CAC/B,EACD,YAAY,CACZ;IACA,gBAAyB,OAAO,iEAC+B;IAE/D,gBAAyB,KAAK;gCA7De,wBAAwB,wCAGrD,wBAAwB;;;;;;MAwF7B;IAEX,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,KAAK,CAAK;IAElB,OAAO,CAAC,cAAc,CAAwC;IAE9D,IAAoB,aAAa,2FAEhC;cAEwB,UAAU,IAAI,OAAO,CAAC,YAAY,CAAC;IAuBtC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IA6DrB,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;YAmE5B,aAAa;IAc3B;;;;OAIG;YACW,eAAe;IAwG7B,OAAO,CAAC,QAAQ;IAchB;;;OAGG;YACW,kBAAkB;CA0BhC"}
1
+ {"version":3,"file":"check.d.ts","sourceRoot":"","sources":["../../src/commands/check.ts"],"names":[],"mappings":"AAOA,OAAO,EACN,iBAAiB,EAEjB,MAAM,mBAAmB,CAAC;AAK3B;;;;;;;;GAQG;AACH,qBAAa,WAAW,CACvB,CAAC,SAAS,OAAO,iBAAiB,GAAG;IACpC,IAAI,EAAE,OAAO,WAAW,CAAC,IAAI,CAAC;IAC9B,KAAK,EAAE,OAAO,WAAW,CAAC,KAAK,CAAC;CAChC,CACA,SAAQ,iBAAiB,CAAC,CAAC,CAAC;IAC7B,gBAAyB,OAAO,iEAC+B;IAE/D,gBAAyB,KAAK;gCAhBO,wBAAwB,wCAAwC,wBAE/F;;;;;;MA0BK;IAEX,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,KAAK,CAAK;IAEF,aAAa,sCAAuB;IAE9B,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAQrB,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;YAuC5B,aAAa;IAc3B;;;;OAIG;YACW,eAAe;IA0G7B;;;OAGG;YACW,kBAAkB;CA0BhC"}
@@ -3,15 +3,11 @@ import { EOL as newline } from "node:os";
3
3
  import path from "node:path";
4
4
  import { Flags } from "@oclif/core";
5
5
  import { StringBuilder } from "@rushstack/node-core-library";
6
- import { GitCommand, RegExpFlag, findGitRoot } from "@tylerbu/cli-api";
7
6
  import chalk from "chalk";
8
- import { cosmiconfig } from "cosmiconfig";
7
+ import { BaseRepopoCommand, } from "../baseCommand.js";
9
8
  import { DefaultPolicyConfig } from "../config.js";
10
- import { DefaultPolicies, isPolicyFixResult, } from "../policy.js";
11
- /**
12
- * Stores performance data for each handler. Used to collect and display performance stats.
13
- */
14
- const handlerPerformanceData = new Map();
9
+ import { logStats, runWithPerf } from "../perf.js";
10
+ import { isPolicyFixResult } from "../policy.js";
15
11
  /**
16
12
  * This tool enforces policies across the code base via a series of handler functions. The handler functions are
17
13
  * associated with a regular expression, and all files matching that expression are passed to the handler function.
@@ -21,7 +17,7 @@ const handlerPerformanceData = new Map();
21
17
  *
22
18
  * `git ls-files -co --exclude-standard --full-name | repopo check --stdin --verbose`
23
19
  */
24
- export class CheckPolicy extends GitCommand {
20
+ export class CheckPolicy extends BaseRepopoCommand {
25
21
  static summary = "Checks and applies policies to the files in the repository.";
26
22
  static flags = {
27
23
  fix: Flags.boolean({
@@ -30,132 +26,30 @@ export class CheckPolicy extends GitCommand {
30
26
  required: false,
31
27
  char: "f",
32
28
  }),
33
- policy: RegExpFlag({
34
- description: "Filter policies to apply by <regex>. Only policies with a name matching the regex will be applied.",
35
- required: false,
36
- char: "d",
37
- }),
38
- excludePolicy: Flags.string({
39
- char: "D",
40
- description: "Exclude policies by name. Can be specified multiple times to exclude multiple policies.",
41
- exclusive: ["policy"],
42
- multiple: true,
43
- }),
44
- path: RegExpFlag({
45
- description: "Filter file paths by <regex>.",
46
- required: false,
47
- char: "p",
48
- }),
49
29
  stdin: Flags.boolean({
50
30
  description: "Read list of files from stdin.",
51
31
  required: false,
52
32
  }),
53
- ...GitCommand.flags,
33
+ ...BaseRepopoCommand.flags,
54
34
  };
55
35
  processed = 0;
56
36
  count = 0;
57
- // private policies: RepoPolicy[] | undefined;
58
- commandContext;
59
- get defaultConfig() {
60
- return DefaultPolicyConfig;
61
- }
62
- async loadConfig() {
63
- const gitRoot = await findGitRoot();
64
- // this.configPath ??= this.config.configDir; // path.join(this.config.configDir, "config.ts");
65
- const explorer = cosmiconfig(this.config.bin, {
66
- searchStrategy: "global",
67
- });
68
- this.verbose(`Looking for '${this.config.bin}' config at '${gitRoot}'`);
69
- const config = await explorer.search(gitRoot);
70
- if (config?.config !== undefined) {
71
- this.verbose(`Found config at ${config.filepath}`);
72
- }
73
- if (config?.config === undefined) {
74
- this.warning("No config found; using defaults.");
75
- }
76
- const finalConfig = config?.config ?? this.defaultConfig;
77
- if (finalConfig.includeDefaultPolicies === true) {
78
- finalConfig.policies ??= [];
79
- finalConfig.policies.push(...DefaultPolicies);
80
- }
81
- return finalConfig;
82
- }
37
+ defaultConfig = DefaultPolicyConfig;
83
38
  async init() {
84
39
  await super.init();
85
- let policies = this.commandConfig?.policies?.filter((h) => {
86
- if (this.flags.excludePolicy === undefined ||
87
- this.flags.excludePolicy.length === 0) {
88
- return true;
89
- }
90
- const shouldRun = this.flags.excludePolicy?.includes(h.name) === false;
91
- if (!shouldRun) {
92
- this.info(`Disabled policy: ${h.name}`);
93
- }
94
- return shouldRun;
95
- }) ?? [];
96
- // const pathRegex: RegExp =
97
- // this.flags.path === undefined ? /.?/ : new RegExp(this.flags.path, "i");
98
- if (this.flags.policy !== undefined) {
99
- this.info(`Filtering handlers by regex: ${this.flags.policy}`);
100
- policies = policies?.filter((h) => this.flags.policy?.test(h.name));
101
- }
102
- const pathRegex = this.flags.path ?? /.?/;
103
- if (this.flags.path !== undefined) {
104
- this.info(`Filtering file paths by regex: ${pathRegex}`);
105
- }
106
40
  if (this.flags.fix) {
107
41
  this.info("Resolving errors if possible.");
108
42
  }
109
- const excludeFiles = this.commandConfig?.excludeFiles?.map((e) => toRegExp(e)) ?? [];
110
- const excludePoliciesForFilesRaw = this.commandConfig?.excludePoliciesForFiles;
111
- const excludePoliciesForFiles = new Map();
112
- if (excludePoliciesForFilesRaw) {
113
- for (const [policyName, filters] of Object.entries(excludePoliciesForFilesRaw)) {
114
- const regexes = filters.map((e) => toRegExp(e));
115
- excludePoliciesForFiles.set(policyName, regexes);
116
- }
117
- }
118
- const gitRoot = await findGitRoot();
119
- this.commandContext = {
120
- pathRegex,
121
- excludeFiles,
122
- policies,
123
- excludePoliciesForFiles,
124
- gitRoot,
125
- };
126
43
  }
127
44
  async run() {
128
45
  // list the handlers then exit
129
- // if (this.flags.listHandlers) {
130
- // for (const h of handlersToRun) {
131
- // this.log(`${h.name}\nresolver: ${h.resolver !== undefined}\n`);
132
- // }
133
- // this.log(`${handlersToRun.length} TOTAL POLICY HANDLERS`);
134
- // this.exit(0);
135
- // }
136
- const policies = this.commandConfig?.policies ?? [];
46
+ const config = await this.loadConfig();
47
+ const policies = config?.policies ?? [];
137
48
  this.verbose(`${policies.length} policies loaded.`);
138
49
  for (const h of policies) {
139
50
  this.verbose(h.name);
140
51
  }
141
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
142
- // const rawExclusions: string[] =
143
- // this.flags.exclusions === undefined
144
- // ? manifest.policy?.exclusions
145
- // : await readJson(this.flags.exclusions);
146
- // const exclusions: RegExp[] = rawExclusions.map((e) => new RegExp(e, "i"));
147
- // const rawHandlerExclusions = manifest?.policy?.handlerExclusions;
148
- // const handlerExclusions: ExcludedPolicyFileMap = {};
149
- // if (rawHandlerExclusions) {
150
- // for (const rule of Object.keys(rawHandlerExclusions)) {
151
- // handlerExclusions[rule] = rawHandlerExclusions[rule].map(
152
- // (e) => new RegExp(e, "i"),
153
- // );
154
- // }
155
- // }
156
52
  const filePathsToCheck = [];
157
- // const context = await this.getContext();
158
- // const gitRoot = context.repo.resolvedRoot;
159
53
  if (this.flags.stdin) {
160
54
  const stdInput = await readStdin();
161
55
  if (stdInput !== undefined) {
@@ -163,16 +57,18 @@ export class CheckPolicy extends GitCommand {
163
57
  }
164
58
  }
165
59
  else {
166
- // const repo = new Repository({ baseDir: gitRoot });
167
60
  const gitFiles = (await this.git.raw("ls-files", "-co", "--exclude-standard", "--full-name")) ?? "";
168
61
  filePathsToCheck.push(...gitFiles.split("\n"));
169
62
  }
170
- if (this.commandContext === undefined) {
171
- this.error("Command context was undefined - fatal error.", {
63
+ try {
64
+ const context = this.getContext();
65
+ await this.executePolicy(filePathsToCheck, context);
66
+ }
67
+ catch (error) {
68
+ this.error(`Command context was undefined - fatal error: ${error}`, {
172
69
  exit: 100,
173
70
  });
174
71
  }
175
- await this.executePolicy(filePathsToCheck, this.commandContext);
176
72
  }
177
73
  async executePolicy(pathsToCheck, commandContext) {
178
74
  try {
@@ -182,7 +78,7 @@ export class CheckPolicy extends GitCommand {
182
78
  }
183
79
  }
184
80
  finally {
185
- this.logStats();
81
+ logStats(commandContext.perfStats, this);
186
82
  }
187
83
  }
188
84
  /**
@@ -191,12 +87,11 @@ export class CheckPolicy extends GitCommand {
191
87
  * decision for all handlers.
192
88
  */
193
89
  async routeToHandlers(file, commandContext) {
194
- const {
195
- // context,
196
- policies, excludePoliciesForFiles, gitRoot, } = commandContext;
90
+ const { policies, excludePoliciesForFiles, gitRoot, perfStats } = commandContext;
197
91
  // Use the repo-relative path so that regexes that specify string start (^) will match repo paths.
198
92
  // Replace \ in result with / in case OS is Windows.
199
93
  const relPath = path.relative(gitRoot, file).replace(/\\/g, "/");
94
+ const config = await this.loadConfig();
200
95
  await Promise.all(policies
201
96
  .filter((handler) => handler.match.test(relPath))
202
97
  // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: <explanation>
@@ -208,11 +103,11 @@ export class CheckPolicy extends GitCommand {
208
103
  this.verbose(`Excluded from '${policy.name}' policy: ${relPath}`);
209
104
  return;
210
105
  }
211
- const result = await runWithPerf(policy.name, "handle", async () => policy.handler({
106
+ const result = await runWithPerf(policy.name, "handle", perfStats, async () => policy.handler({
212
107
  file: relPath,
213
108
  root: gitRoot,
214
109
  resolve: this.flags.fix,
215
- config: this.commandConfig?.policySettings?.[policy.name],
110
+ config: config?.policySettings?.[policy.name],
216
111
  }));
217
112
  if (result === true) {
218
113
  return;
@@ -233,7 +128,7 @@ export class CheckPolicy extends GitCommand {
233
128
  if (this.flags.fix && resolver !== undefined) {
234
129
  // Resolve the failure
235
130
  messages.append(`${newline}attempting to resolve: ${relPath}`);
236
- const resolveResult = await runWithPerf(policy.name, "resolve", async () => resolver({ file: relPath, root: gitRoot }));
131
+ const resolveResult = await runWithPerf(policy.name, "resolve", perfStats, async () => resolver({ file: relPath, root: gitRoot }));
237
132
  if (resolveResult.errorMessage !== undefined &&
238
133
  resolveResult.errorMessage !== "") {
239
134
  messages.append(newline + resolveResult.errorMessage);
@@ -262,15 +157,6 @@ export class CheckPolicy extends GitCommand {
262
157
  }
263
158
  }));
264
159
  }
265
- logStats() {
266
- this.log(`Statistics: ${this.processed} processed, ${this.count - this.processed} excluded, ${this.count} total`);
267
- for (const [action, handlerPerf] of handlerPerformanceData.entries()) {
268
- this.log(`Performance for "${action}":`);
269
- for (const [handler, dur] of handlerPerf.entries()) {
270
- this.log(`\t${handler}: ${dur}ms`);
271
- }
272
- }
273
- }
274
160
  /**
275
161
  * Given a string that represents a path to a file in the repo, determines if the file should be checked, and if so,
276
162
  * routes the file to the appropriate handlers.
@@ -295,16 +181,6 @@ export class CheckPolicy extends GitCommand {
295
181
  this.processed++;
296
182
  }
297
183
  }
298
- async function runWithPerf(name, action, run) {
299
- const actionMap = handlerPerformanceData.get(action) ?? new Map();
300
- let dur = actionMap.get(name) ?? 0;
301
- const start = Date.now();
302
- const result = await run();
303
- dur += Date.now() - start;
304
- actionMap.set(name, dur);
305
- handlerPerformanceData.set(action, actionMap);
306
- return result;
307
- }
308
184
  async function readStdin() {
309
185
  return new Promise((resolve) => {
310
186
  const stdin = process.stdin;
@@ -321,10 +197,4 @@ async function readStdin() {
321
197
  }
322
198
  });
323
199
  }
324
- function toRegExp(input) {
325
- if (typeof input === "string") {
326
- return new RegExp(input, "i");
327
- }
328
- return input;
329
- }
330
200
  //# sourceMappingURL=check.js.map