@shivaedev/quality 0.32.0 → 0.33.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 +16 -0
- package/README.md +1 -4
- package/dist/hooks/forwarding.d.ts +7 -0
- package/dist/hooks/forwarding.d.ts.map +1 -0
- package/dist/hooks/forwarding.js +94 -0
- package/dist/hooks/forwarding.js.map +1 -0
- package/dist/hooks/install.d.ts.map +1 -1
- package/dist/hooks/install.js +2 -1
- package/dist/hooks/install.js.map +1 -1
- package/dist/hooks/shim.d.ts +1 -0
- package/dist/hooks/shim.d.ts.map +1 -1
- package/dist/hooks/shim.js +3 -1
- package/dist/hooks/shim.js.map +1 -1
- package/dist/hooks/status.d.ts +1 -1
- package/dist/hooks/status.d.ts.map +1 -1
- package/dist/hooks/status.js +8 -6
- package/dist/hooks/status.js.map +1 -1
- package/dist/hooks/uninstall.d.ts.map +1 -1
- package/dist/hooks/uninstall.js +3 -1
- package/dist/hooks/uninstall.js.map +1 -1
- package/dist/imports/packageExports.d.ts +4 -0
- package/dist/imports/packageExports.d.ts.map +1 -0
- package/dist/imports/packageExports.js +48 -0
- package/dist/imports/packageExports.js.map +1 -0
- package/dist/rules/builtIn.d.ts +1 -3
- package/dist/rules/builtIn.d.ts.map +1 -1
- package/dist/rules/builtIn.js +0 -2
- package/dist/rules/builtIn.js.map +1 -1
- package/dist/rules/imports/bundleInputs.d.ts.map +1 -1
- package/dist/rules/imports/bundleInputs.js +4 -50
- package/dist/rules/imports/bundleInputs.js.map +1 -1
- package/docs/debt-and-hooks.md +3 -3
- package/package.json +3 -3
- package/src/hooks/forwarding.ts +99 -0
- package/src/hooks/install.ts +3 -1
- package/src/hooks/shim.ts +3 -1
- package/src/hooks/status.ts +10 -6
- package/src/hooks/uninstall.ts +3 -1
- package/src/imports/packageExports.ts +55 -0
- package/src/rules/builtIn.ts +0 -2
- package/src/rules/imports/bundleInputs.ts +4 -59
- package/dist/rules/stories/supportImports.d.ts +0 -4
- package/dist/rules/stories/supportImports.d.ts.map +0 -1
- package/dist/rules/stories/supportImports.js +0 -28
- package/dist/rules/stories/supportImports.js.map +0 -1
- package/src/rules/stories/supportImports.ts +0 -31
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.33.0 - 2026-10-21
|
|
4
|
+
|
|
5
|
+
### Breaking
|
|
6
|
+
|
|
7
|
+
- Remove the `tests/support-imports` rule and its module allowlist. Delete the `tests/support-imports` block from your quality config; configs that still name it fail validation. Story kits may import any workspace module; how tests use those modules belongs to testing guidance and review.
|
|
8
|
+
|
|
9
|
+
## 0.32.1 - 2026-10-21
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
|
|
13
|
+
- Let story kits import any workspace package's exported modules through package names or aliases without listing them in `tests/support-imports.options.modules`. Keep the allowlist for additional internal modules; packages without exports retain their existing boundary.
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- Keep all global Git hooks running after `quality hooks install`, including hooks added later, and prevent recursive pre-commit chaining. Report installations that hide global hooks with a reinstall repair hint; uninstall leaves global hooks in effect.
|
|
18
|
+
|
|
3
19
|
## 0.32.0 - 2026-10-19
|
|
4
20
|
|
|
5
21
|
### Changed
|
package/README.md
CHANGED
|
@@ -126,7 +126,7 @@ export default defineConfig({
|
|
|
126
126
|
|
|
127
127
|
### Story tests
|
|
128
128
|
|
|
129
|
-
|
|
129
|
+
Keep story kits and fixtures in `src/test-support` and keep each spec specific to its story. [`@shivaedev/test-story`](https://github.com/ShivaeDev/platform/tree/main/packages/test-story#readme) explains how to build the kit, traits and verbs over a real engine.
|
|
130
130
|
|
|
131
131
|
Quality's `tests/story-specs` rule reports a test file that declares tests without a kit's `it`, including plain Vitest `it` and `test` declarations mixed with stories. Typecheck tests are exempt, as are test-story itself and its transitive workspace dependencies, including development, optional and peer dependencies: requiring those packages to depend on test-story would create a cycle.
|
|
132
132
|
|
|
@@ -140,8 +140,6 @@ Quality's `tests/story-setup` rule reports top-level test functions named with t
|
|
|
140
140
|
|
|
141
141
|
Specs keep expectations visible. `tests/spec-helpers` moves all module-level helper functions out of specs and into the kit; `tests/no-expect-in-support` moves `expect` calls into the spec. `tests/named-values` rejects numeric literals passed to `toBe` or `toHaveLength` in specs: assert the observed items or give the value a domain name. `tests/no-limit-imports` rejects importing `MAX_*` constants from a `limits` module into specs, so a story proves behavior instead of copying an implementation cap.
|
|
142
142
|
|
|
143
|
-
`tests/support-imports` checks resolved repository imports from `test-support/` against `options.modules`, an allowlist of repository-relative module paths or glob patterns. Imports within test-support and imports of external packages remain available for the harness. Name only the public surface the kit drives, such as the host, schemas and queue definitions. The default empty list allows no production repository modules; aliases, dynamic imports and re-exports resolve through the same import graph as the other import rules.
|
|
144
|
-
|
|
145
143
|
`boundaries/queue-sql` rejects SQL mentioning the private `pgboss.` schema. Apps use their queue definitions and let workers process jobs; there is no queue-purge API by design. `boundaries/auth-sql` rejects SQL accessing Better Auth's `user`, `account`, `session` and `verification` tables. Apps use platform's auth API; `options.owners` names platform's own auth implementation and its migrations. These rules inspect SQL templates, literal query calls and query variables, and inventoried `.sql` files. They do not evaluate arbitrary runtime SQL construction.
|
|
146
144
|
|
|
147
145
|
`boundaries/http-status` rejects comparisons with HTTP error status numbers, switch cases and literal status-list membership outside `options.edge`. Domain code handles typed errors; the named HTTP edge translates them into transport responses. Process exit codes remain ordinary domain values.
|
|
@@ -153,7 +151,6 @@ export default defineConfig({
|
|
|
153
151
|
rules: {
|
|
154
152
|
"boundaries/auth-sql": { options: { owners: ["platform/auth/**", "platform/auth-migrations/**"] } },
|
|
155
153
|
"boundaries/http-status": { options: { edge: ["src/http/**"] } },
|
|
156
|
-
"tests/support-imports": { options: { modules: ["src/host.ts", "src/schemas.ts", "src/queues.ts"] } },
|
|
157
154
|
},
|
|
158
155
|
});
|
|
159
156
|
```
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Effect, FileSystem } from "effect";
|
|
2
|
+
import { SetupFailure } from "#failure.ts";
|
|
3
|
+
export declare function forwardingPath(file: string): string;
|
|
4
|
+
export declare const globalHooksPath: (cwd: string) => Effect.Effect<string, SetupFailure, import("effect/process/ChildProcessSpawner").ChildProcessSpawner>;
|
|
5
|
+
export declare const installForwarding: (file: string) => Effect.Effect<string, import("#inventory/filesystem.ts").FilesystemFailure | import("effect/PlatformError").PlatformError | SetupFailure, FileSystem.FileSystem>;
|
|
6
|
+
export declare const removeForwarding: (file: string) => Effect.Effect<void, import("#inventory/filesystem.ts").FilesystemFailure | import("effect/PlatformError").PlatformError, FileSystem.FileSystem>;
|
|
7
|
+
//# sourceMappingURL=forwarding.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"forwarding.d.ts","sourceRoot":"","sources":["../../src/hooks/forwarding.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAqC3C,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED,eAAO,MAAM,eAAe,wHAM1B,CAAC;AAoBH,eAAO,MAAM,iBAAiB,oLAc5B,CAAC;AAEH,eAAO,MAAM,gBAAgB,mKAa3B,CAAC"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { dirname, join } from "node:path";
|
|
2
|
+
import { Effect, FileSystem } from "effect";
|
|
3
|
+
import { SetupFailure } from "#failure.ts";
|
|
4
|
+
import { git } from "#git/command.ts";
|
|
5
|
+
import { readOptionalText } from "#inventory/filesystem.ts";
|
|
6
|
+
import { shellWords } from "./command.js";
|
|
7
|
+
import { isHookShim, OWNER } from "./shim.js";
|
|
8
|
+
const HOOKS = [
|
|
9
|
+
"applypatch-msg",
|
|
10
|
+
"pre-applypatch",
|
|
11
|
+
"post-applypatch",
|
|
12
|
+
"pre-commit",
|
|
13
|
+
"pre-merge-commit",
|
|
14
|
+
"prepare-commit-msg",
|
|
15
|
+
"commit-msg",
|
|
16
|
+
"post-commit",
|
|
17
|
+
"pre-rebase",
|
|
18
|
+
"post-checkout",
|
|
19
|
+
"post-merge",
|
|
20
|
+
"pre-push",
|
|
21
|
+
"pre-receive",
|
|
22
|
+
"update",
|
|
23
|
+
"proc-receive",
|
|
24
|
+
"post-receive",
|
|
25
|
+
"post-update",
|
|
26
|
+
"reference-transaction",
|
|
27
|
+
"push-to-checkout",
|
|
28
|
+
"pre-auto-gc",
|
|
29
|
+
"post-rewrite",
|
|
30
|
+
"sendemail-validate",
|
|
31
|
+
"fsmonitor-watchman",
|
|
32
|
+
"p4-changelist",
|
|
33
|
+
"p4-prepare-changelist",
|
|
34
|
+
"p4-post-changelist",
|
|
35
|
+
"p4-pre-submit",
|
|
36
|
+
"post-index-change",
|
|
37
|
+
];
|
|
38
|
+
export function forwardingPath(file) {
|
|
39
|
+
return join(dirname(file), "quality");
|
|
40
|
+
}
|
|
41
|
+
export const globalHooksPath = Effect.fn("Hooks.globalHooksPath")(function* (cwd) {
|
|
42
|
+
const global = yield* git(cwd, ["config", "--global", "--includes", "--type=path", "--get", "core.hooksPath"]);
|
|
43
|
+
if (global.code !== 0 && global.code !== 1) {
|
|
44
|
+
return yield* new SetupFailure({ message: `cannot read global core.hooksPath: ${global.stderr.trim()}` });
|
|
45
|
+
}
|
|
46
|
+
return global.stdout.trim();
|
|
47
|
+
});
|
|
48
|
+
function forwarder(file, name) {
|
|
49
|
+
return [
|
|
50
|
+
"#!/bin/sh",
|
|
51
|
+
OWNER,
|
|
52
|
+
...(name === "pre-commit"
|
|
53
|
+
? [`exec ${shellWords([file])} "$@"`]
|
|
54
|
+
: [
|
|
55
|
+
'global_path="$(git config --global --includes --type=path --get core.hooksPath)"',
|
|
56
|
+
'[ -n "$global_path" ] || exit 0',
|
|
57
|
+
`global_hook="$global_path/${name}"`,
|
|
58
|
+
'if [ -x "$global_hook" ] && ! [ "$global_hook" -ef "$0" ]; then',
|
|
59
|
+
'\texec "$global_hook" "$@"',
|
|
60
|
+
"fi",
|
|
61
|
+
]),
|
|
62
|
+
"",
|
|
63
|
+
].join("\n");
|
|
64
|
+
}
|
|
65
|
+
export const installForwarding = Effect.fn("Hooks.installForwarding")(function* (file) {
|
|
66
|
+
const fs = yield* FileSystem.FileSystem;
|
|
67
|
+
const path = forwardingPath(file);
|
|
68
|
+
yield* fs.makeDirectory(path, { recursive: true });
|
|
69
|
+
for (const name of HOOKS) {
|
|
70
|
+
const target = join(path, name);
|
|
71
|
+
const existing = yield* readOptionalText(target);
|
|
72
|
+
if (existing !== undefined && !isHookShim(existing)) {
|
|
73
|
+
return yield* new SetupFailure({ message: `cannot replace ${target}: it is not Quality's hook.` });
|
|
74
|
+
}
|
|
75
|
+
yield* fs.writeFileString(target, forwarder(file, name));
|
|
76
|
+
yield* fs.chmod(target, 0o755);
|
|
77
|
+
}
|
|
78
|
+
return path;
|
|
79
|
+
});
|
|
80
|
+
export const removeForwarding = Effect.fn("Hooks.removeForwarding")(function* (file) {
|
|
81
|
+
const fs = yield* FileSystem.FileSystem;
|
|
82
|
+
const path = forwardingPath(file);
|
|
83
|
+
for (const name of HOOKS) {
|
|
84
|
+
const target = join(path, name);
|
|
85
|
+
const existing = yield* readOptionalText(target);
|
|
86
|
+
if (existing !== undefined && isHookShim(existing)) {
|
|
87
|
+
yield* fs.remove(target);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
if ((yield* fs.exists(path)) && (yield* fs.readDirectory(path)).length === 0) {
|
|
91
|
+
yield* fs.remove(path, { recursive: true });
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
//# sourceMappingURL=forwarding.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"forwarding.js","sourceRoot":"","sources":["../../src/hooks/forwarding.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../src/hooks/install.ts"],"names":[],"mappings":"AACA,OAAO,EAAW,MAAM,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAGrD,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAG3C,OAAO,EAA6B,KAAK,MAAM,EAAc,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../src/hooks/install.ts"],"names":[],"mappings":"AACA,OAAO,EAAW,MAAM,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAGrD,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAG3C,OAAO,EAA6B,KAAK,MAAM,EAAc,MAAM,cAAc,CAAC;AAMlF,MAAM,WAAW,WAAW;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACxB;AAoBD,eAAO,MAAM,WAAW,uJA+BtB,CAAC"}
|
package/dist/hooks/install.js
CHANGED
|
@@ -6,6 +6,7 @@ import { SetupFailure } from "#failure.ts";
|
|
|
6
6
|
import { gitOrFail } from "#git/command.ts";
|
|
7
7
|
import { readOptionalText } from "#inventory/filesystem.ts";
|
|
8
8
|
import { commandWords, hookCommand, shellWords } from "./command.js";
|
|
9
|
+
import { globalHooksPath, installForwarding } from "./forwarding.js";
|
|
9
10
|
import { hookLocation } from "./location.js";
|
|
10
11
|
import { hookShim, isHookShim } from "./shim.js";
|
|
11
12
|
import { globalHookStatus } from "./status.js";
|
|
@@ -45,7 +46,7 @@ export const installHook = Effect.fn("Hooks.installHook")(function* (install) {
|
|
|
45
46
|
const done = foreign ? `replaced ${location.file}, which was not quality's hook` : `installed the pre-commit hook at ${location.file}`;
|
|
46
47
|
yield* Console.log(`quality: ${done}; each commit runs \`${words}\`.`);
|
|
47
48
|
}
|
|
48
|
-
const hooks = dirname(location.file);
|
|
49
|
+
const hooks = (yield* globalHooksPath(config.root)) === "" ? dirname(location.file) : yield* installForwarding(location.file).pipe(Effect.mapError(failed));
|
|
49
50
|
yield* gitOrFail(config.root, ["config", "--local", "core.hooksPath", hooks]);
|
|
50
51
|
yield* Console.log(`quality: set repository-local core.hooksPath to ${hooks}.`);
|
|
51
52
|
yield* globalHookStatus(location);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"install.js","sourceRoot":"","sources":["../../src/hooks/install.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"install.js","sourceRoot":"","sources":["../../src/hooks/install.ts"],"names":[],"mappings":"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"}
|
package/dist/hooks/shim.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type HookCommand } from "./command.ts";
|
|
2
|
+
export declare const OWNER = "# Installed by @shivaedev/quality: `quality hooks install` writes it, `quality hooks uninstall` removes it.";
|
|
2
3
|
export declare function isHookShim(text: string): boolean;
|
|
3
4
|
export declare function hookShim(directory: string, command: HookCommand): string;
|
|
4
5
|
//# sourceMappingURL=shim.d.ts.map
|
package/dist/hooks/shim.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shim.d.ts","sourceRoot":"","sources":["../../src/hooks/shim.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,WAAW,EAAc,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"shim.d.ts","sourceRoot":"","sources":["../../src/hooks/shim.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,WAAW,EAAc,MAAM,cAAc,CAAC;AAE5D,eAAO,MAAM,KAAK,gHAAgH,CAAC;AAEnI,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEhD;AAID,wBAAgB,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,GAAG,MAAM,CA6BxE"}
|
package/dist/hooks/shim.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { shellWords } from "./command.js";
|
|
2
|
-
const OWNER = "# Installed by @shivaedev/quality: `quality hooks install` writes it, `quality hooks uninstall` removes it.";
|
|
2
|
+
export const OWNER = "# Installed by @shivaedev/quality: `quality hooks install` writes it, `quality hooks uninstall` removes it.";
|
|
3
3
|
export function isHookShim(text) {
|
|
4
4
|
return text.split("\n").includes(OWNER);
|
|
5
5
|
}
|
|
@@ -11,6 +11,8 @@ export function hookShim(directory, command) {
|
|
|
11
11
|
return [
|
|
12
12
|
"#!/bin/sh",
|
|
13
13
|
OWNER,
|
|
14
|
+
'if [ -n "$QUALITY_PRE_COMMIT_ACTIVE" ]; then exit 0; fi',
|
|
15
|
+
"export QUALITY_PRE_COMMIT_ACTIVE=1",
|
|
14
16
|
'top="$(git rev-parse --show-toplevel)" || exit 2',
|
|
15
17
|
'if [ -n "$GIT_DIR" ] && [ -z "$GIT_WORK_TREE" ]; then export GIT_WORK_TREE="$top"; fi',
|
|
16
18
|
'cd "$top" || exit 2',
|
package/dist/hooks/shim.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shim.js","sourceRoot":"","sources":["../../src/hooks/shim.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,UAAU,EAAE,MAAM,cAAc,CAAC;AAE5D,MAAM,KAAK,GAAG,6GAA6G,CAAC;
|
|
1
|
+
{"version":3,"file":"shim.js","sourceRoot":"","sources":["../../src/hooks/shim.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,UAAU,EAAE,MAAM,cAAc,CAAC;AAE5D,MAAM,CAAC,MAAM,KAAK,GAAG,6GAA6G,CAAC;AAEnI,MAAM,UAAU,UAAU,CAAC,IAAY;IACtC,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AACzC,CAAC;AAED,uHAAuH;AACvH,gJAAgJ;AAChJ,MAAM,UAAU,QAAQ,CAAC,SAAiB,EAAE,OAAoB;IAC/D,MAAM,MAAM,GAAG,SAAS,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,UAAU,CAAC,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC;IACrE,MAAM,IAAI,GAAG,CAAC,UAAU,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,YAAY,EAAE,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,KAAK,EAAE,CAAC,CAAC;IAChH,OAAO;QACN,WAAW;QACX,KAAK;QACL,yDAAyD;QACzD,oCAAoC;QACpC,kDAAkD;QAClD,uFAAuF;QACvF,qBAAqB;QACrB,UAAU;QACV,kFAAkF;QAClF,gCAAgC;QAChC,yCAAyC;QACzC,mEAAmE;QACnE,sCAAsC;QACtC,MAAM;QACN,IAAI;QACJ,YAAY,MAAM,YAAY;QAC9B,WAAW,UAAU,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,EAAE;QAC3C,8BAA8B;QAC9B,sKAAsK;QACtK,UAAU;QACV,IAAI;QACJ,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,eAAe;QAChC,gBAAgB;QAChB,EAAE;KACF,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACd,CAAC"}
|
package/dist/hooks/status.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Effect } from "effect";
|
|
2
2
|
import { GateFailed, SetupFailure } from "#failure.ts";
|
|
3
3
|
import { type HookLocation } from "./location.ts";
|
|
4
|
-
export declare const globalHookStatus: (location: HookLocation) => Effect.Effect<
|
|
4
|
+
export declare const globalHookStatus: (location: HookLocation) => Effect.Effect<void, SetupFailure, import("effect/process/ChildProcessSpawner").ChildProcessSpawner>;
|
|
5
5
|
export declare const hookStatus: (cwd: string) => Effect.Effect<undefined, GateFailed | SetupFailure, import("effect/process/ChildProcessSpawner").ChildProcessSpawner | import("effect/FileSystem").FileSystem>;
|
|
6
6
|
//# sourceMappingURL=status.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/hooks/status.ts"],"names":[],"mappings":"AAGA,OAAO,EAAW,MAAM,EAAE,MAAM,QAAQ,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/hooks/status.ts"],"names":[],"mappings":"AAGA,OAAO,EAAW,MAAM,EAAE,MAAM,QAAQ,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAIvD,OAAO,EAAE,KAAK,YAAY,EAAgB,MAAM,eAAe,CAAC;AAchE,eAAO,MAAM,gBAAgB,iIAU3B,CAAC;AAEH,eAAO,MAAM,UAAU,iLAmBrB,CAAC"}
|
package/dist/hooks/status.js
CHANGED
|
@@ -3,8 +3,9 @@ import { access, realpath } from "node:fs/promises";
|
|
|
3
3
|
import { dirname, resolve } from "node:path";
|
|
4
4
|
import { Console, Effect } from "effect";
|
|
5
5
|
import { GateFailed, SetupFailure } from "#failure.ts";
|
|
6
|
-
import {
|
|
6
|
+
import { gitOrFail } from "#git/command.ts";
|
|
7
7
|
import { readOptionalText } from "#inventory/filesystem.ts";
|
|
8
|
+
import { forwardingPath, globalHooksPath } from "./forwarding.js";
|
|
8
9
|
import { hookLocation } from "./location.js";
|
|
9
10
|
import { isHookShim } from "./shim.js";
|
|
10
11
|
function executable(file) {
|
|
@@ -19,11 +20,7 @@ function executable(file) {
|
|
|
19
20
|
});
|
|
20
21
|
}
|
|
21
22
|
export const globalHookStatus = Effect.fn("Hooks.globalHookStatus")(function* (location) {
|
|
22
|
-
const
|
|
23
|
-
if (global.code !== 0 && global.code !== 1) {
|
|
24
|
-
return yield* new SetupFailure({ message: `cannot read global core.hooksPath: ${global.stderr.trim()}` });
|
|
25
|
-
}
|
|
26
|
-
const path = global.stdout.trim();
|
|
23
|
+
const path = yield* globalHooksPath(location.toplevel);
|
|
27
24
|
const hook = resolve(location.toplevel, path, "pre-commit");
|
|
28
25
|
const target = path === "" ? undefined : yield* executable(hook);
|
|
29
26
|
const own = yield* executable(location.file);
|
|
@@ -39,6 +36,11 @@ export const hookStatus = Effect.fn("Hooks.hookStatus")(function* (cwd) {
|
|
|
39
36
|
yield* Console.error(`quality: the effective hooks path ${dirname(path)} does not run Quality's pre-commit hook.\nhelp: run \`quality hooks install\` to check commits.`);
|
|
40
37
|
return yield* new GateFailed();
|
|
41
38
|
}
|
|
39
|
+
const global = yield* globalHooksPath(cwd);
|
|
40
|
+
if (global !== "" && dirname(path) !== resolve(location.toplevel, global) && dirname(path) !== forwardingPath(location.file)) {
|
|
41
|
+
yield* Console.error("quality: the repository-local core.hooksPath hides global hooks other than pre-commit.\nhelp: run `quality hooks install` to keep all global hooks running.");
|
|
42
|
+
return yield* new GateFailed();
|
|
43
|
+
}
|
|
42
44
|
yield* Console.log(`quality: git runs Quality's pre-commit hook at ${path}.`);
|
|
43
45
|
yield* globalHookStatus(location);
|
|
44
46
|
});
|
package/dist/hooks/status.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/hooks/status.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/hooks/status.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"uninstall.d.ts","sourceRoot":"","sources":["../../src/hooks/uninstall.ts"],"names":[],"mappings":"AACA,OAAO,EAAW,MAAM,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"uninstall.d.ts","sourceRoot":"","sources":["../../src/hooks/uninstall.ts"],"names":[],"mappings":"AACA,OAAO,EAAW,MAAM,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAO3C,eAAO,MAAM,aAAa,8IAiBxB,CAAC"}
|
package/dist/hooks/uninstall.js
CHANGED
|
@@ -3,6 +3,7 @@ import { Console, Effect, FileSystem } from "effect";
|
|
|
3
3
|
import { SetupFailure } from "#failure.ts";
|
|
4
4
|
import { git, gitOrFail } from "#git/command.ts";
|
|
5
5
|
import { readOptionalText } from "#inventory/filesystem.ts";
|
|
6
|
+
import { forwardingPath, removeForwarding } from "./forwarding.js";
|
|
6
7
|
import { hookLocation } from "./location.js";
|
|
7
8
|
import { isHookShim } from "./shim.js";
|
|
8
9
|
export const uninstallHook = Effect.fn("Hooks.uninstallHook")(function* (cwd) {
|
|
@@ -17,7 +18,8 @@ export const uninstallHook = Effect.fn("Hooks.uninstallHook")(function* (cwd) {
|
|
|
17
18
|
}
|
|
18
19
|
yield* Effect.mapError(fs.remove(file), (error) => new SetupFailure({ message: `cannot remove ${file}: ${error.message}` }));
|
|
19
20
|
const local = yield* git(cwd, ["config", "--local", "--get", "core.hooksPath"]);
|
|
20
|
-
|
|
21
|
+
yield* removeForwarding(file).pipe(Effect.mapError((error) => new SetupFailure({ message: error.message })));
|
|
22
|
+
if ([dirname(file), forwardingPath(file)].includes(local.stdout.trim())) {
|
|
21
23
|
yield* gitOrFail(cwd, ["config", "--local", "--unset-all", "core.hooksPath"]);
|
|
22
24
|
}
|
|
23
25
|
yield* Console.log(`quality: removed the pre-commit hook at ${file}.`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"uninstall.js","sourceRoot":"","sources":["../../src/hooks/uninstall.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAEvC,MAAM,CAAC,MAAM,aAAa,GAAG,MAAM,CAAC,EAAE,CAAC,qBAAqB,CAAC,CAAC,QAAQ,CAAC,EAAE,GAAW;IACnF,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,UAAU,CAAC,UAAU,CAAC;IACxC,MAAM,EAAE,IAAI,EAAE,GAAG,KAAK,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;IAC1C,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,gBAAgB,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,IAAI,YAAY,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IAC7H,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC5B,OAAO,KAAK,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,2CAA2C,IAAI,GAAG,CAAC,CAAC;IAC/E,CAAC;IACD,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC3B,OAAO,KAAK,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,iBAAiB,IAAI,wCAAwC,CAAC,CAAC;IAC5F,CAAC;IACD,KAAK,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,YAAY,CAAC,EAAE,OAAO,EAAE,iBAAiB,IAAI,KAAK,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;IAC7H,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,gBAAgB,CAAC,CAAC,CAAC;IAChF,IAAI,
|
|
1
|
+
{"version":3,"file":"uninstall.js","sourceRoot":"","sources":["../../src/hooks/uninstall.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnE,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAEvC,MAAM,CAAC,MAAM,aAAa,GAAG,MAAM,CAAC,EAAE,CAAC,qBAAqB,CAAC,CAAC,QAAQ,CAAC,EAAE,GAAW;IACnF,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,UAAU,CAAC,UAAU,CAAC;IACxC,MAAM,EAAE,IAAI,EAAE,GAAG,KAAK,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;IAC1C,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,gBAAgB,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,IAAI,YAAY,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IAC7H,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC5B,OAAO,KAAK,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,2CAA2C,IAAI,GAAG,CAAC,CAAC;IAC/E,CAAC;IACD,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC3B,OAAO,KAAK,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,iBAAiB,IAAI,wCAAwC,CAAC,CAAC;IAC5F,CAAC;IACD,KAAK,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,YAAY,CAAC,EAAE,OAAO,EAAE,iBAAiB,IAAI,KAAK,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;IAC7H,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,gBAAgB,CAAC,CAAC,CAAC;IAChF,KAAK,CAAC,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,YAAY,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC;IAC7G,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC;QACzE,KAAK,CAAC,CAAC,SAAS,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,SAAS,EAAE,aAAa,EAAE,gBAAgB,CAAC,CAAC,CAAC;IAC/E,CAAC;IACD,KAAK,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,2CAA2C,IAAI,GAAG,CAAC,CAAC;AACxE,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare function blockedExport(exports: unknown, subpath: string): boolean;
|
|
2
|
+
export declare function exportTargets(value: unknown): string[];
|
|
3
|
+
export declare function exportedModule(exports: unknown, local: string): boolean;
|
|
4
|
+
//# sourceMappingURL=packageExports.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"packageExports.d.ts","sourceRoot":"","sources":["../../src/imports/packageExports.ts"],"names":[],"mappings":"AAAA,wBAAgB,aAAa,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAqBxE;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,EAAE,CAKtD;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAwBvE"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
export function blockedExport(exports, subpath) {
|
|
2
|
+
if (typeof exports !== "object" || exports === null || Array.isArray(exports)) {
|
|
3
|
+
return false;
|
|
4
|
+
}
|
|
5
|
+
const entries = Object.entries(exports);
|
|
6
|
+
const exact = entries.find(([key]) => key === subpath);
|
|
7
|
+
if (exact !== undefined) {
|
|
8
|
+
return exact[1] === null;
|
|
9
|
+
}
|
|
10
|
+
const matching = entries.filter(([key]) => {
|
|
11
|
+
const [prefix = "", suffix, ...rest] = key.split("*");
|
|
12
|
+
return (suffix !== undefined
|
|
13
|
+
&& rest.length === 0
|
|
14
|
+
&& subpath.length > prefix.length + suffix.length
|
|
15
|
+
&& subpath.startsWith(prefix)
|
|
16
|
+
&& subpath.endsWith(suffix));
|
|
17
|
+
});
|
|
18
|
+
const best = matching.toSorted(([left], [right]) => right.indexOf("*") - left.indexOf("*") || right.length - left.length)[0];
|
|
19
|
+
return best?.[1] === null;
|
|
20
|
+
}
|
|
21
|
+
export function exportTargets(value) {
|
|
22
|
+
if (typeof value === "string") {
|
|
23
|
+
return [value];
|
|
24
|
+
}
|
|
25
|
+
return typeof value === "object" && value !== null ? Object.values(value).flatMap(exportTargets) : [];
|
|
26
|
+
}
|
|
27
|
+
export function exportedModule(exports, local) {
|
|
28
|
+
if (typeof exports !== "object" || exports === null) {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
const paths = [
|
|
32
|
+
`./${local}`,
|
|
33
|
+
`./${local.replace(/^src\//u, "dist/").replace(/\.(?<mode>[cm]?)tsx?$/u, ".$<mode>js")}`,
|
|
34
|
+
`./${local.replace(/^src\//u, "dist/").replace(/\.(?<mode>[cm]?)tsx?$/u, ".d.$<mode>ts")}`,
|
|
35
|
+
];
|
|
36
|
+
return Object.entries(exports).some(([key, value]) => exportTargets(value).some((target) => paths.some((path) => {
|
|
37
|
+
const [prefix = "", suffix] = target.split("*");
|
|
38
|
+
if (suffix === undefined) {
|
|
39
|
+
return path === target && !blockedExport(exports, key);
|
|
40
|
+
}
|
|
41
|
+
if (!(path.startsWith(prefix) && path.endsWith(suffix)) || path.length <= prefix.length + suffix.length) {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
const star = path.slice(prefix.length, path.length - suffix.length);
|
|
45
|
+
return !blockedExport(exports, key.replace("*", star));
|
|
46
|
+
})));
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=packageExports.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"packageExports.js","sourceRoot":"","sources":["../../src/imports/packageExports.ts"],"names":[],"mappings":"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"}
|
package/dist/rules/builtIn.d.ts
CHANGED
|
@@ -30,9 +30,7 @@ export declare const builtInRules: readonly [import("../rule.ts").Rule<"structur
|
|
|
30
30
|
readonly suites?: readonly string[];
|
|
31
31
|
}>, import("../rule.ts").Rule<"tests/story-setup", undefined>, import("../rule.ts").Rule<"tests/story-specs", undefined>, import("../rule.ts").Rule<"boundaries/queue-sql", undefined>, import("../rule.ts").Rule<"boundaries/auth-sql", {
|
|
32
32
|
readonly owners?: readonly string[];
|
|
33
|
-
}>, import("../rule.ts").Rule<"tests/no-expect-in-support", undefined>, import("../rule.ts").Rule<"tests/spec-helpers", undefined>, import("../rule.ts").Rule<"
|
|
34
|
-
readonly modules?: readonly string[];
|
|
35
|
-
}>, import("../rule.ts").Rule<"boundaries/http-status", {
|
|
33
|
+
}>, import("../rule.ts").Rule<"tests/no-expect-in-support", undefined>, import("../rule.ts").Rule<"tests/spec-helpers", undefined>, import("../rule.ts").Rule<"boundaries/http-status", {
|
|
36
34
|
readonly edge?: readonly string[];
|
|
37
35
|
}>, import("../rule.ts").Rule<"tests/named-values", undefined>, import("../rule.ts").Rule<"tests/no-limit-imports", undefined>];
|
|
38
36
|
//# sourceMappingURL=builtIn.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"builtIn.d.ts","sourceRoot":"","sources":["../../src/rules/builtIn.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"builtIn.d.ts","sourceRoot":"","sources":["../../src/rules/builtIn.ts"],"names":[],"mappings":"AA0CA,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+HA0Cf,CAAC"}
|
package/dist/rules/builtIn.js
CHANGED
|
@@ -25,7 +25,6 @@ import { queueSql } from "#rules/stories/queueSql.ts";
|
|
|
25
25
|
import { storySetup } from "#rules/stories/setup.ts";
|
|
26
26
|
import { specHelpers } from "#rules/stories/specHelpers.ts";
|
|
27
27
|
import { storySpecs } from "#rules/stories/specs.ts";
|
|
28
|
-
import { supportImports } from "#rules/stories/supportImports.ts";
|
|
29
28
|
import { biomeOverrides } from "#rules/suppressions/biomeOverrides.ts";
|
|
30
29
|
import { biomeRecommended } from "#rules/suppressions/biomeRecommended.ts";
|
|
31
30
|
import { noDoubleCast } from "#rules/suppressions/noDoubleCast.ts";
|
|
@@ -79,7 +78,6 @@ export const builtInRules = [
|
|
|
79
78
|
authSql,
|
|
80
79
|
noExpect,
|
|
81
80
|
specHelpers,
|
|
82
|
-
supportImports,
|
|
83
81
|
httpStatus,
|
|
84
82
|
namedValues,
|
|
85
83
|
noLimitImports,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"builtIn.js","sourceRoot":"","sources":["../../src/rules/builtIn.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAC3D,OAAO,EAAE,QAAQ,EAAE,MAAM,6BAA6B,CAAC;AACvD,OAAO,EAAE,mBAAmB,EAAE,MAAM,wCAAwC,CAAC;AAC7E,OAAO,EAAE,OAAO,EAAE,MAAM,4BAA4B,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,oCAAoC,CAAC;AACrE,OAAO,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAC;AACjE,OAAO,EAAE,MAAM,EAAE,MAAM,2BAA2B,CAAC;AACnD,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAC5D,OAAO,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AACpE,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AACxD,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACxD,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC7D,OAAO,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAC1D,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACjE,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAE,OAAO,EAAE,MAAM,2BAA2B,CAAC;AACpD,OAAO,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AAC5D,OAAO,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAC;AAClE,OAAO,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAC;AACtD,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AAC5D,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"builtIn.js","sourceRoot":"","sources":["../../src/rules/builtIn.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bundleInputs.d.ts","sourceRoot":"","sources":["../../../src/rules/imports/bundleInputs.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"bundleInputs.d.ts","sourceRoot":"","sources":["../../../src/rules/imports/bundleInputs.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,OAAO,EAAa,KAAK,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAQzE,wBAAgB,YAAY,CAAC,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,EAAE,eAAe,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO;;;EA0BrI"}
|
|
@@ -1,55 +1,9 @@
|
|
|
1
1
|
import { posix } from "node:path";
|
|
2
2
|
import { outgoing } from "#imports/fences/reach.ts";
|
|
3
|
+
import { blockedExport, exportedModule, exportTargets } from "#imports/packageExports.ts";
|
|
3
4
|
import { packageOf } from "#imports/workspace.ts";
|
|
4
|
-
function blocked(exports, subpath) {
|
|
5
|
-
if (typeof exports !== "object" || exports === null || Array.isArray(exports)) {
|
|
6
|
-
return false;
|
|
7
|
-
}
|
|
8
|
-
const entries = Object.entries(exports);
|
|
9
|
-
const exact = entries.find(([key]) => key === subpath);
|
|
10
|
-
if (exact !== undefined) {
|
|
11
|
-
return exact[1] === null;
|
|
12
|
-
}
|
|
13
|
-
const matching = entries.filter(([key]) => {
|
|
14
|
-
const [prefix = "", suffix, ...rest] = key.split("*");
|
|
15
|
-
return (suffix !== undefined
|
|
16
|
-
&& rest.length === 0
|
|
17
|
-
&& subpath.length > prefix.length + suffix.length
|
|
18
|
-
&& subpath.startsWith(prefix)
|
|
19
|
-
&& subpath.endsWith(suffix));
|
|
20
|
-
});
|
|
21
|
-
const best = matching.toSorted(([left], [right]) => right.indexOf("*") - left.indexOf("*") || right.length - left.length)[0];
|
|
22
|
-
return best?.[1] === null;
|
|
23
|
-
}
|
|
24
|
-
function targets(value) {
|
|
25
|
-
if (typeof value === "string") {
|
|
26
|
-
return [value];
|
|
27
|
-
}
|
|
28
|
-
return typeof value === "object" && value !== null ? Object.values(value).flatMap(targets) : [];
|
|
29
|
-
}
|
|
30
|
-
function exported(exports, local) {
|
|
31
|
-
if (typeof exports !== "object" || exports === null) {
|
|
32
|
-
return false;
|
|
33
|
-
}
|
|
34
|
-
const paths = [
|
|
35
|
-
`./${local}`,
|
|
36
|
-
`./${local.replace(/^src\//u, "dist/").replace(/\.(?<mode>[cm]?)tsx?$/u, ".$<mode>js")}`,
|
|
37
|
-
`./${local.replace(/^src\//u, "dist/").replace(/\.(?<mode>[cm]?)tsx?$/u, ".d.$<mode>ts")}`,
|
|
38
|
-
];
|
|
39
|
-
return Object.entries(exports).some(([key, value]) => targets(value).some((target) => paths.some((path) => {
|
|
40
|
-
const [prefix = "", suffix] = target.split("*");
|
|
41
|
-
if (suffix === undefined) {
|
|
42
|
-
return path === target && !blocked(exports, key);
|
|
43
|
-
}
|
|
44
|
-
if (!(path.startsWith(prefix) && path.endsWith(suffix)) || path.length <= prefix.length + suffix.length) {
|
|
45
|
-
return false;
|
|
46
|
-
}
|
|
47
|
-
const star = path.slice(prefix.length, path.length - suffix.length);
|
|
48
|
-
return !blocked(exports, key.replace("*", star));
|
|
49
|
-
})));
|
|
50
|
-
}
|
|
51
5
|
function executable(manifest, local) {
|
|
52
|
-
const binTargets =
|
|
6
|
+
const binTargets = exportTargets(Reflect.get(manifest, "bin"));
|
|
53
7
|
const built = local.replace(/^src\//u, "dist/").replace(/\.(?<mode>[cm]?)tsx?$/u, ".$<mode>js");
|
|
54
8
|
return binTargets.some((target) => typeof target === "string" && [local, built].includes(posix.normalize(target)));
|
|
55
9
|
}
|
|
@@ -58,8 +12,8 @@ export function bundleInputs(graph, owner, manifest, developmentFile) {
|
|
|
58
12
|
const privateFiles = new Set(modules.filter((path) => {
|
|
59
13
|
const local = posix.relative(owner.directory, path);
|
|
60
14
|
return (local.startsWith("src/")
|
|
61
|
-
&&
|
|
62
|
-
&& !
|
|
15
|
+
&& blockedExport(Reflect.get(manifest, "exports"), `./${local.slice(4)}`)
|
|
16
|
+
&& !exportedModule(Reflect.get(manifest, "exports"), local));
|
|
63
17
|
}));
|
|
64
18
|
const queue = modules.filter((path) => executable(manifest, posix.relative(owner.directory, path)) || !(developmentFile(path) || privateFiles.has(path)));
|
|
65
19
|
const runtime = new Set(queue);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bundleInputs.js","sourceRoot":"","sources":["../../../src/rules/imports/bundleInputs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAEpD,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"bundleInputs.js","sourceRoot":"","sources":["../../../src/rules/imports/bundleInputs.ts"],"names":[],"mappings":"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"}
|
package/docs/debt-and-hooks.md
CHANGED
|
@@ -79,8 +79,8 @@ help: fix what the output above reports, then commit again; `quality hooks pre-c
|
|
|
79
79
|
|
|
80
80
|
The hook is a short script in the repository's common git directory, `.git/hooks/pre-commit`, which every worktree of the repository shares. It changes to the root of the worktree that commits, then to the config's folder in it, and runs that worktree's quality with that worktree's config, so a branch that changes the checks commits under its own rules. It runs quality the way `hooks install` ran: when that was a file inside the repository, such as a workspace's own source, it runs `node` with the same Node options on that file; otherwise it runs `node_modules/.bin/quality` in the config's folder. When that file does not exist, as in a new worktree whose dependencies are not installed yet, the hook blocks the commit and says to install them. A config file not named `quality.config.ts` is passed with `--config`.
|
|
81
81
|
|
|
82
|
-
`hooks install` sets repository-local `core.hooksPath`
|
|
82
|
+
`hooks install` sets repository-local `core.hooksPath` and prints that path. Without a global hooks path, it uses the common git directory's `hooks` folder. With a global `core.hooksPath`, it uses `hooks/quality`, whose forwarding hooks preserve every Git hook in the global directory, including hooks added later, with its arguments, stdin and exit status. It never changes global or system git configuration. At commit time, Quality's pre-commit shim reads global `core.hooksPath` and runs its executable `pre-commit` hook from the committing worktree's root, before Quality's checks. Relative global paths resolve from that root; a global path pointing to Quality's own hook is not chained. A re-entry marker prevents a global hook that chains `.git/hooks/pre-commit` from running Quality recursively. Quality still runs when the global hook fails, and either failure blocks the commit. Installation reports whether there is an executable global pre-commit hook to chain.
|
|
83
83
|
|
|
84
|
-
`quality hooks status` reports whether git's effective hooks path runs an executable Quality pre-commit hook, and exits 1 with an installation hint when it does not. It also reports global chaining when Quality is active.
|
|
84
|
+
`quality hooks status` reports whether git's effective hooks path runs an executable Quality pre-commit hook, and exits 1 with an installation hint when it does not. It also reports global chaining when Quality is active. A local hooks path that runs Quality but hides other global hooks fails status with a `quality hooks install` repair hint; reinstalling replaces that layout with forwarding hooks.
|
|
85
85
|
|
|
86
|
-
A pre-commit hook that quality did not write is kept: `hooks install` warns, says how to call quality from it, and exits 0 without setting the hooks path. `--force` replaces it. `quality hooks uninstall` removes quality's hook and keeps any other. It removes the repository-local hooks path only when that setting still points to Quality's hooks
|
|
86
|
+
A pre-commit hook that quality did not write is kept: `hooks install` warns, says how to call quality from it, and exits 0 without setting the hooks path. `--force` replaces it. `quality hooks uninstall` removes quality's hook and keeps any other. It removes Quality's forwarding hooks. It removes the repository-local hooks path only when that setting still points to one of Quality's hooks directories, letting global hooks take effect again.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shivaedev/quality",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.33.0",
|
|
4
4
|
"description": "Repository quality gate with typed rules, a baseline of existing debt and a registry of reasoned exceptions",
|
|
5
5
|
"homepage": "https://github.com/ShivaeDev/platform/tree/main/packages/quality#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -68,13 +68,13 @@
|
|
|
68
68
|
"@types/node": "24.19.2",
|
|
69
69
|
"@typescript/native": "npm:typescript@7.0.2",
|
|
70
70
|
"effect": "4.0.4",
|
|
71
|
-
"vitest": "5.0.
|
|
71
|
+
"vitest": "5.0.4"
|
|
72
72
|
},
|
|
73
73
|
"peerDependencies": {
|
|
74
74
|
"@effect/platform-node": "4.0.4",
|
|
75
75
|
"@effect/platform-node-shared": "4.0.4",
|
|
76
76
|
"effect": "4.0.4",
|
|
77
|
-
"vitest": "5.0.
|
|
77
|
+
"vitest": "5.0.4"
|
|
78
78
|
},
|
|
79
79
|
"peerDependenciesMeta": {
|
|
80
80
|
"vitest": {
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { dirname, join } from "node:path";
|
|
2
|
+
import { Effect, FileSystem } from "effect";
|
|
3
|
+
import { SetupFailure } from "#failure.ts";
|
|
4
|
+
import { git } from "#git/command.ts";
|
|
5
|
+
import { readOptionalText } from "#inventory/filesystem.ts";
|
|
6
|
+
import { shellWords } from "./command.ts";
|
|
7
|
+
import { isHookShim, OWNER } from "./shim.ts";
|
|
8
|
+
|
|
9
|
+
const HOOKS = [
|
|
10
|
+
"applypatch-msg",
|
|
11
|
+
"pre-applypatch",
|
|
12
|
+
"post-applypatch",
|
|
13
|
+
"pre-commit",
|
|
14
|
+
"pre-merge-commit",
|
|
15
|
+
"prepare-commit-msg",
|
|
16
|
+
"commit-msg",
|
|
17
|
+
"post-commit",
|
|
18
|
+
"pre-rebase",
|
|
19
|
+
"post-checkout",
|
|
20
|
+
"post-merge",
|
|
21
|
+
"pre-push",
|
|
22
|
+
"pre-receive",
|
|
23
|
+
"update",
|
|
24
|
+
"proc-receive",
|
|
25
|
+
"post-receive",
|
|
26
|
+
"post-update",
|
|
27
|
+
"reference-transaction",
|
|
28
|
+
"push-to-checkout",
|
|
29
|
+
"pre-auto-gc",
|
|
30
|
+
"post-rewrite",
|
|
31
|
+
"sendemail-validate",
|
|
32
|
+
"fsmonitor-watchman",
|
|
33
|
+
"p4-changelist",
|
|
34
|
+
"p4-prepare-changelist",
|
|
35
|
+
"p4-post-changelist",
|
|
36
|
+
"p4-pre-submit",
|
|
37
|
+
"post-index-change",
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
export function forwardingPath(file: string): string {
|
|
41
|
+
return join(dirname(file), "quality");
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export const globalHooksPath = Effect.fn("Hooks.globalHooksPath")(function* (cwd: string) {
|
|
45
|
+
const global = yield* git(cwd, ["config", "--global", "--includes", "--type=path", "--get", "core.hooksPath"]);
|
|
46
|
+
if (global.code !== 0 && global.code !== 1) {
|
|
47
|
+
return yield* new SetupFailure({ message: `cannot read global core.hooksPath: ${global.stderr.trim()}` });
|
|
48
|
+
}
|
|
49
|
+
return global.stdout.trim();
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
function forwarder(file: string, name: string): string {
|
|
53
|
+
return [
|
|
54
|
+
"#!/bin/sh",
|
|
55
|
+
OWNER,
|
|
56
|
+
...(name === "pre-commit"
|
|
57
|
+
? [`exec ${shellWords([file])} "$@"`]
|
|
58
|
+
: [
|
|
59
|
+
'global_path="$(git config --global --includes --type=path --get core.hooksPath)"',
|
|
60
|
+
'[ -n "$global_path" ] || exit 0',
|
|
61
|
+
`global_hook="$global_path/${name}"`,
|
|
62
|
+
'if [ -x "$global_hook" ] && ! [ "$global_hook" -ef "$0" ]; then',
|
|
63
|
+
'\texec "$global_hook" "$@"',
|
|
64
|
+
"fi",
|
|
65
|
+
]),
|
|
66
|
+
"",
|
|
67
|
+
].join("\n");
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export const installForwarding = Effect.fn("Hooks.installForwarding")(function* (file: string) {
|
|
71
|
+
const fs = yield* FileSystem.FileSystem;
|
|
72
|
+
const path = forwardingPath(file);
|
|
73
|
+
yield* fs.makeDirectory(path, { recursive: true });
|
|
74
|
+
for (const name of HOOKS) {
|
|
75
|
+
const target = join(path, name);
|
|
76
|
+
const existing = yield* readOptionalText(target);
|
|
77
|
+
if (existing !== undefined && !isHookShim(existing)) {
|
|
78
|
+
return yield* new SetupFailure({ message: `cannot replace ${target}: it is not Quality's hook.` });
|
|
79
|
+
}
|
|
80
|
+
yield* fs.writeFileString(target, forwarder(file, name));
|
|
81
|
+
yield* fs.chmod(target, 0o755);
|
|
82
|
+
}
|
|
83
|
+
return path;
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
export const removeForwarding = Effect.fn("Hooks.removeForwarding")(function* (file: string) {
|
|
87
|
+
const fs = yield* FileSystem.FileSystem;
|
|
88
|
+
const path = forwardingPath(file);
|
|
89
|
+
for (const name of HOOKS) {
|
|
90
|
+
const target = join(path, name);
|
|
91
|
+
const existing = yield* readOptionalText(target);
|
|
92
|
+
if (existing !== undefined && isHookShim(existing)) {
|
|
93
|
+
yield* fs.remove(target);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
if ((yield* fs.exists(path)) && (yield* fs.readDirectory(path)).length === 0) {
|
|
97
|
+
yield* fs.remove(path, { recursive: true });
|
|
98
|
+
}
|
|
99
|
+
});
|
package/src/hooks/install.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { SetupFailure } from "#failure.ts";
|
|
|
6
6
|
import { gitOrFail } from "#git/command.ts";
|
|
7
7
|
import { readOptionalText } from "#inventory/filesystem.ts";
|
|
8
8
|
import { commandWords, hookCommand, type Launch, shellWords } from "./command.ts";
|
|
9
|
+
import { globalHooksPath, installForwarding } from "./forwarding.ts";
|
|
9
10
|
import { hookLocation } from "./location.ts";
|
|
10
11
|
import { hookShim, isHookShim } from "./shim.ts";
|
|
11
12
|
import { globalHookStatus } from "./status.ts";
|
|
@@ -61,7 +62,8 @@ export const installHook = Effect.fn("Hooks.installHook")(function* (install: Ho
|
|
|
61
62
|
const done = foreign ? `replaced ${location.file}, which was not quality's hook` : `installed the pre-commit hook at ${location.file}`;
|
|
62
63
|
yield* Console.log(`quality: ${done}; each commit runs \`${words}\`.`);
|
|
63
64
|
}
|
|
64
|
-
const hooks =
|
|
65
|
+
const hooks =
|
|
66
|
+
(yield* globalHooksPath(config.root)) === "" ? dirname(location.file) : yield* installForwarding(location.file).pipe(Effect.mapError(failed));
|
|
65
67
|
yield* gitOrFail(config.root, ["config", "--local", "core.hooksPath", hooks]);
|
|
66
68
|
yield* Console.log(`quality: set repository-local core.hooksPath to ${hooks}.`);
|
|
67
69
|
yield* globalHookStatus(location);
|
package/src/hooks/shim.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type HookCommand, shellWords } from "./command.ts";
|
|
2
2
|
|
|
3
|
-
const OWNER = "# Installed by @shivaedev/quality: `quality hooks install` writes it, `quality hooks uninstall` removes it.";
|
|
3
|
+
export const OWNER = "# Installed by @shivaedev/quality: `quality hooks install` writes it, `quality hooks uninstall` removes it.";
|
|
4
4
|
|
|
5
5
|
export function isHookShim(text: string): boolean {
|
|
6
6
|
return text.split("\n").includes(OWNER);
|
|
@@ -14,6 +14,8 @@ export function hookShim(directory: string, command: HookCommand): string {
|
|
|
14
14
|
return [
|
|
15
15
|
"#!/bin/sh",
|
|
16
16
|
OWNER,
|
|
17
|
+
'if [ -n "$QUALITY_PRE_COMMIT_ACTIVE" ]; then exit 0; fi',
|
|
18
|
+
"export QUALITY_PRE_COMMIT_ACTIVE=1",
|
|
17
19
|
'top="$(git rev-parse --show-toplevel)" || exit 2',
|
|
18
20
|
'if [ -n "$GIT_DIR" ] && [ -z "$GIT_WORK_TREE" ]; then export GIT_WORK_TREE="$top"; fi',
|
|
19
21
|
'cd "$top" || exit 2',
|
package/src/hooks/status.ts
CHANGED
|
@@ -3,8 +3,9 @@ import { access, realpath } from "node:fs/promises";
|
|
|
3
3
|
import { dirname, resolve } from "node:path";
|
|
4
4
|
import { Console, Effect } from "effect";
|
|
5
5
|
import { GateFailed, SetupFailure } from "#failure.ts";
|
|
6
|
-
import {
|
|
6
|
+
import { gitOrFail } from "#git/command.ts";
|
|
7
7
|
import { readOptionalText } from "#inventory/filesystem.ts";
|
|
8
|
+
import { forwardingPath, globalHooksPath } from "./forwarding.ts";
|
|
8
9
|
import { type HookLocation, hookLocation } from "./location.ts";
|
|
9
10
|
import { isHookShim } from "./shim.ts";
|
|
10
11
|
|
|
@@ -20,11 +21,7 @@ function executable(file: string) {
|
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
export const globalHookStatus = Effect.fn("Hooks.globalHookStatus")(function* (location: HookLocation) {
|
|
23
|
-
const
|
|
24
|
-
if (global.code !== 0 && global.code !== 1) {
|
|
25
|
-
return yield* new SetupFailure({ message: `cannot read global core.hooksPath: ${global.stderr.trim()}` });
|
|
26
|
-
}
|
|
27
|
-
const path = global.stdout.trim();
|
|
24
|
+
const path = yield* globalHooksPath(location.toplevel);
|
|
28
25
|
const hook = resolve(location.toplevel, path, "pre-commit");
|
|
29
26
|
const target = path === "" ? undefined : yield* executable(hook);
|
|
30
27
|
const own = yield* executable(location.file);
|
|
@@ -45,6 +42,13 @@ export const hookStatus = Effect.fn("Hooks.hookStatus")(function* (cwd: string)
|
|
|
45
42
|
);
|
|
46
43
|
return yield* new GateFailed();
|
|
47
44
|
}
|
|
45
|
+
const global = yield* globalHooksPath(cwd);
|
|
46
|
+
if (global !== "" && dirname(path) !== resolve(location.toplevel, global) && dirname(path) !== forwardingPath(location.file)) {
|
|
47
|
+
yield* Console.error(
|
|
48
|
+
"quality: the repository-local core.hooksPath hides global hooks other than pre-commit.\nhelp: run `quality hooks install` to keep all global hooks running.",
|
|
49
|
+
);
|
|
50
|
+
return yield* new GateFailed();
|
|
51
|
+
}
|
|
48
52
|
yield* Console.log(`quality: git runs Quality's pre-commit hook at ${path}.`);
|
|
49
53
|
yield* globalHookStatus(location);
|
|
50
54
|
});
|
package/src/hooks/uninstall.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { Console, Effect, FileSystem } from "effect";
|
|
|
3
3
|
import { SetupFailure } from "#failure.ts";
|
|
4
4
|
import { git, gitOrFail } from "#git/command.ts";
|
|
5
5
|
import { readOptionalText } from "#inventory/filesystem.ts";
|
|
6
|
+
import { forwardingPath, removeForwarding } from "./forwarding.ts";
|
|
6
7
|
import { hookLocation } from "./location.ts";
|
|
7
8
|
import { isHookShim } from "./shim.ts";
|
|
8
9
|
|
|
@@ -18,7 +19,8 @@ export const uninstallHook = Effect.fn("Hooks.uninstallHook")(function* (cwd: st
|
|
|
18
19
|
}
|
|
19
20
|
yield* Effect.mapError(fs.remove(file), (error) => new SetupFailure({ message: `cannot remove ${file}: ${error.message}` }));
|
|
20
21
|
const local = yield* git(cwd, ["config", "--local", "--get", "core.hooksPath"]);
|
|
21
|
-
|
|
22
|
+
yield* removeForwarding(file).pipe(Effect.mapError((error) => new SetupFailure({ message: error.message })));
|
|
23
|
+
if ([dirname(file), forwardingPath(file)].includes(local.stdout.trim())) {
|
|
22
24
|
yield* gitOrFail(cwd, ["config", "--local", "--unset-all", "core.hooksPath"]);
|
|
23
25
|
}
|
|
24
26
|
yield* Console.log(`quality: removed the pre-commit hook at ${file}.`);
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
export function blockedExport(exports: unknown, subpath: string): boolean {
|
|
2
|
+
if (typeof exports !== "object" || exports === null || Array.isArray(exports)) {
|
|
3
|
+
return false;
|
|
4
|
+
}
|
|
5
|
+
const entries = Object.entries(exports);
|
|
6
|
+
const exact = entries.find(([key]) => key === subpath);
|
|
7
|
+
if (exact !== undefined) {
|
|
8
|
+
return exact[1] === null;
|
|
9
|
+
}
|
|
10
|
+
const matching = entries.filter(([key]) => {
|
|
11
|
+
const [prefix = "", suffix, ...rest] = key.split("*");
|
|
12
|
+
return (
|
|
13
|
+
suffix !== undefined
|
|
14
|
+
&& rest.length === 0
|
|
15
|
+
&& subpath.length > prefix.length + suffix.length
|
|
16
|
+
&& subpath.startsWith(prefix)
|
|
17
|
+
&& subpath.endsWith(suffix)
|
|
18
|
+
);
|
|
19
|
+
});
|
|
20
|
+
const best = matching.toSorted(([left], [right]) => right.indexOf("*") - left.indexOf("*") || right.length - left.length)[0];
|
|
21
|
+
return best?.[1] === null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function exportTargets(value: unknown): string[] {
|
|
25
|
+
if (typeof value === "string") {
|
|
26
|
+
return [value];
|
|
27
|
+
}
|
|
28
|
+
return typeof value === "object" && value !== null ? Object.values(value).flatMap(exportTargets) : [];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function exportedModule(exports: unknown, local: string): boolean {
|
|
32
|
+
if (typeof exports !== "object" || exports === null) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
const paths = [
|
|
36
|
+
`./${local}`,
|
|
37
|
+
`./${local.replace(/^src\//u, "dist/").replace(/\.(?<mode>[cm]?)tsx?$/u, ".$<mode>js")}`,
|
|
38
|
+
`./${local.replace(/^src\//u, "dist/").replace(/\.(?<mode>[cm]?)tsx?$/u, ".d.$<mode>ts")}`,
|
|
39
|
+
];
|
|
40
|
+
return Object.entries(exports).some(([key, value]) =>
|
|
41
|
+
exportTargets(value).some((target) =>
|
|
42
|
+
paths.some((path) => {
|
|
43
|
+
const [prefix = "", suffix] = target.split("*");
|
|
44
|
+
if (suffix === undefined) {
|
|
45
|
+
return path === target && !blockedExport(exports, key);
|
|
46
|
+
}
|
|
47
|
+
if (!(path.startsWith(prefix) && path.endsWith(suffix)) || path.length <= prefix.length + suffix.length) {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
const star = path.slice(prefix.length, path.length - suffix.length);
|
|
51
|
+
return !blockedExport(exports, key.replace("*", star));
|
|
52
|
+
}),
|
|
53
|
+
),
|
|
54
|
+
);
|
|
55
|
+
}
|
package/src/rules/builtIn.ts
CHANGED
|
@@ -25,7 +25,6 @@ import { queueSql } from "#rules/stories/queueSql.ts";
|
|
|
25
25
|
import { storySetup } from "#rules/stories/setup.ts";
|
|
26
26
|
import { specHelpers } from "#rules/stories/specHelpers.ts";
|
|
27
27
|
import { storySpecs } from "#rules/stories/specs.ts";
|
|
28
|
-
import { supportImports } from "#rules/stories/supportImports.ts";
|
|
29
28
|
import { biomeOverrides } from "#rules/suppressions/biomeOverrides.ts";
|
|
30
29
|
import { biomeRecommended } from "#rules/suppressions/biomeRecommended.ts";
|
|
31
30
|
import { noDoubleCast } from "#rules/suppressions/noDoubleCast.ts";
|
|
@@ -80,7 +79,6 @@ export const builtInRules = [
|
|
|
80
79
|
authSql,
|
|
81
80
|
noExpect,
|
|
82
81
|
specHelpers,
|
|
83
|
-
supportImports,
|
|
84
82
|
httpStatus,
|
|
85
83
|
namedValues,
|
|
86
84
|
noLimitImports,
|
|
@@ -1,66 +1,11 @@
|
|
|
1
1
|
import { posix } from "node:path";
|
|
2
2
|
import { outgoing } from "#imports/fences/reach.ts";
|
|
3
3
|
import type { ImportGraph } from "#imports/graph.ts";
|
|
4
|
+
import { blockedExport, exportedModule, exportTargets } from "#imports/packageExports.ts";
|
|
4
5
|
import { packageOf, type WorkspacePackage } from "#imports/workspace.ts";
|
|
5
6
|
|
|
6
|
-
function blocked(exports: unknown, subpath: string): boolean {
|
|
7
|
-
if (typeof exports !== "object" || exports === null || Array.isArray(exports)) {
|
|
8
|
-
return false;
|
|
9
|
-
}
|
|
10
|
-
const entries = Object.entries(exports);
|
|
11
|
-
const exact = entries.find(([key]) => key === subpath);
|
|
12
|
-
if (exact !== undefined) {
|
|
13
|
-
return exact[1] === null;
|
|
14
|
-
}
|
|
15
|
-
const matching = entries.filter(([key]) => {
|
|
16
|
-
const [prefix = "", suffix, ...rest] = key.split("*");
|
|
17
|
-
return (
|
|
18
|
-
suffix !== undefined
|
|
19
|
-
&& rest.length === 0
|
|
20
|
-
&& subpath.length > prefix.length + suffix.length
|
|
21
|
-
&& subpath.startsWith(prefix)
|
|
22
|
-
&& subpath.endsWith(suffix)
|
|
23
|
-
);
|
|
24
|
-
});
|
|
25
|
-
const best = matching.toSorted(([left], [right]) => right.indexOf("*") - left.indexOf("*") || right.length - left.length)[0];
|
|
26
|
-
return best?.[1] === null;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function targets(value: unknown): string[] {
|
|
30
|
-
if (typeof value === "string") {
|
|
31
|
-
return [value];
|
|
32
|
-
}
|
|
33
|
-
return typeof value === "object" && value !== null ? Object.values(value).flatMap(targets) : [];
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function exported(exports: unknown, local: string): boolean {
|
|
37
|
-
if (typeof exports !== "object" || exports === null) {
|
|
38
|
-
return false;
|
|
39
|
-
}
|
|
40
|
-
const paths = [
|
|
41
|
-
`./${local}`,
|
|
42
|
-
`./${local.replace(/^src\//u, "dist/").replace(/\.(?<mode>[cm]?)tsx?$/u, ".$<mode>js")}`,
|
|
43
|
-
`./${local.replace(/^src\//u, "dist/").replace(/\.(?<mode>[cm]?)tsx?$/u, ".d.$<mode>ts")}`,
|
|
44
|
-
];
|
|
45
|
-
return Object.entries(exports).some(([key, value]) =>
|
|
46
|
-
targets(value).some((target) =>
|
|
47
|
-
paths.some((path) => {
|
|
48
|
-
const [prefix = "", suffix] = target.split("*");
|
|
49
|
-
if (suffix === undefined) {
|
|
50
|
-
return path === target && !blocked(exports, key);
|
|
51
|
-
}
|
|
52
|
-
if (!(path.startsWith(prefix) && path.endsWith(suffix)) || path.length <= prefix.length + suffix.length) {
|
|
53
|
-
return false;
|
|
54
|
-
}
|
|
55
|
-
const star = path.slice(prefix.length, path.length - suffix.length);
|
|
56
|
-
return !blocked(exports, key.replace("*", star));
|
|
57
|
-
}),
|
|
58
|
-
),
|
|
59
|
-
);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
7
|
function executable(manifest: object, local: string): boolean {
|
|
63
|
-
const binTargets =
|
|
8
|
+
const binTargets = exportTargets(Reflect.get(manifest, "bin"));
|
|
64
9
|
const built = local.replace(/^src\//u, "dist/").replace(/\.(?<mode>[cm]?)tsx?$/u, ".$<mode>js");
|
|
65
10
|
return binTargets.some((target) => typeof target === "string" && [local, built].includes(posix.normalize(target)));
|
|
66
11
|
}
|
|
@@ -72,8 +17,8 @@ export function bundleInputs(graph: ImportGraph, owner: WorkspacePackage, manife
|
|
|
72
17
|
const local = posix.relative(owner.directory, path);
|
|
73
18
|
return (
|
|
74
19
|
local.startsWith("src/")
|
|
75
|
-
&&
|
|
76
|
-
&& !
|
|
20
|
+
&& blockedExport(Reflect.get(manifest, "exports"), `./${local.slice(4)}`)
|
|
21
|
+
&& !exportedModule(Reflect.get(manifest, "exports"), local)
|
|
77
22
|
);
|
|
78
23
|
}),
|
|
79
24
|
);
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"supportImports.d.ts","sourceRoot":"","sources":["../../../src/rules/stories/supportImports.ts"],"names":[],"mappings":"AAUA,eAAO,MAAM,cAAc;;EAoBzB,CAAC"}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { Effect, Schema } from "effect";
|
|
2
|
-
import { importGraph } from "#imports/graph.ts";
|
|
3
|
-
import { isTestSupport } from "#naming/testName.ts";
|
|
4
|
-
import { defineRule } from "#rule.ts";
|
|
5
|
-
import { matchesAny } from "./matchesAny.js";
|
|
6
|
-
const SupportImportsOptions = Schema.Struct({
|
|
7
|
-
modules: Schema.Array(Schema.String).pipe(Schema.withDecodingDefaultKey(Effect.succeed([]))),
|
|
8
|
-
});
|
|
9
|
-
export const supportImports = defineRule({
|
|
10
|
-
check: async ({ options, ...inputs }) => {
|
|
11
|
-
if (!inputs.sources.some((file) => isTestSupport(file.path))) {
|
|
12
|
-
return [];
|
|
13
|
-
}
|
|
14
|
-
const graph = await importGraph(inputs);
|
|
15
|
-
return graph.edges
|
|
16
|
-
.filter((edge) => isTestSupport(edge.from) && edge.to.kind === "file" && !isTestSupport(edge.to.path) && !matchesAny(edge.to.path, options.modules))
|
|
17
|
-
.map((edge) => ({
|
|
18
|
-
file: edge.from,
|
|
19
|
-
line: edge.line,
|
|
20
|
-
message: `Test-support imports "${edge.specifier}" outside its allowed public surface. Drive the app through its host, schemas or queue definitions; list those public module paths in tests/support-imports.options.modules in the repository's quality config.`,
|
|
21
|
-
subject: edge.specifier,
|
|
22
|
-
}));
|
|
23
|
-
},
|
|
24
|
-
description: "Test-support reaches repository modules only through the public surface named by the repository's module allowlist.",
|
|
25
|
-
id: "tests/support-imports",
|
|
26
|
-
options: Schema.toStandardSchemaV1(SupportImportsOptions, { parseOptions: { errors: "all", onExcessProperty: "error" } }),
|
|
27
|
-
});
|
|
28
|
-
//# sourceMappingURL=supportImports.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"supportImports.js","sourceRoot":"","sources":["../../../src/rules/stories/supportImports.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACtC,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7C,MAAM,qBAAqB,GAAG,MAAM,CAAC,MAAM,CAAC;IAC3C,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,sBAAsB,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC;CAC5F,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,cAAc,GAAG,UAAU,CAAC;IACxC,KAAK,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,GAAG,MAAM,EAAE,EAAE,EAAE;QACvC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;YAC9D,OAAO,EAAE,CAAC;QACX,CAAC;QACD,MAAM,KAAK,GAAG,MAAM,WAAW,CAAC,MAAM,CAAC,CAAC;QACxC,OAAO,KAAK,CAAC,KAAK;aAChB,MAAM,CACN,CAAC,IAAI,EAAE,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,EAAE,CAAC,IAAI,KAAK,MAAM,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,CAC3I;aACA,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YACf,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,OAAO,EAAE,yBAAyB,IAAI,CAAC,SAAS,iNAAiN;YACjQ,OAAO,EAAE,IAAI,CAAC,SAAS;SACvB,CAAC,CAAC,CAAC;IACN,CAAC;IACD,WAAW,EAAE,qHAAqH;IAClI,EAAE,EAAE,uBAAuB;IAC3B,OAAO,EAAE,MAAM,CAAC,kBAAkB,CAAC,qBAAqB,EAAE,EAAE,YAAY,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,gBAAgB,EAAE,OAAO,EAAE,EAAE,CAAC;CACzH,CAAC,CAAC"}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { Effect, Schema } from "effect";
|
|
2
|
-
import { importGraph } from "#imports/graph.ts";
|
|
3
|
-
import { isTestSupport } from "#naming/testName.ts";
|
|
4
|
-
import { defineRule } from "#rule.ts";
|
|
5
|
-
import { matchesAny } from "./matchesAny.ts";
|
|
6
|
-
|
|
7
|
-
const SupportImportsOptions = Schema.Struct({
|
|
8
|
-
modules: Schema.Array(Schema.String).pipe(Schema.withDecodingDefaultKey(Effect.succeed([]))),
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
export const supportImports = defineRule({
|
|
12
|
-
check: async ({ options, ...inputs }) => {
|
|
13
|
-
if (!inputs.sources.some((file) => isTestSupport(file.path))) {
|
|
14
|
-
return [];
|
|
15
|
-
}
|
|
16
|
-
const graph = await importGraph(inputs);
|
|
17
|
-
return graph.edges
|
|
18
|
-
.filter(
|
|
19
|
-
(edge) => isTestSupport(edge.from) && edge.to.kind === "file" && !isTestSupport(edge.to.path) && !matchesAny(edge.to.path, options.modules),
|
|
20
|
-
)
|
|
21
|
-
.map((edge) => ({
|
|
22
|
-
file: edge.from,
|
|
23
|
-
line: edge.line,
|
|
24
|
-
message: `Test-support imports "${edge.specifier}" outside its allowed public surface. Drive the app through its host, schemas or queue definitions; list those public module paths in tests/support-imports.options.modules in the repository's quality config.`,
|
|
25
|
-
subject: edge.specifier,
|
|
26
|
-
}));
|
|
27
|
-
},
|
|
28
|
-
description: "Test-support reaches repository modules only through the public surface named by the repository's module allowlist.",
|
|
29
|
-
id: "tests/support-imports",
|
|
30
|
-
options: Schema.toStandardSchemaV1(SupportImportsOptions, { parseOptions: { errors: "all", onExcessProperty: "error" } }),
|
|
31
|
-
});
|