@intentius/chant 0.1.8 → 0.1.10
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/package.json +1 -1
- package/src/lint/rule-loader.test.ts +28 -0
- package/src/lint/rule-loader.ts +41 -8
package/package.json
CHANGED
|
@@ -64,4 +64,32 @@ describe("rule-loader", () => {
|
|
|
64
64
|
// Clean up
|
|
65
65
|
rmSync(join(RULES_DIR, "my-rule.test.ts"));
|
|
66
66
|
});
|
|
67
|
+
|
|
68
|
+
test("walks up to find .chant/rules/ when invoked from a sub-stack dir", async () => {
|
|
69
|
+
// Project layout: TEST_DIR (root, has .chant/rules/) -> src/east/.
|
|
70
|
+
const subStack = join(TEST_DIR, "src", "east");
|
|
71
|
+
mkdirSync(subStack, { recursive: true });
|
|
72
|
+
writeFileSync(join(TEST_DIR, "package.json"), `{ "name": "fixture" }`);
|
|
73
|
+
try {
|
|
74
|
+
const rules = await loadLocalRules(subStack);
|
|
75
|
+
expect(rules).toHaveLength(1);
|
|
76
|
+
expect(rules[0].id).toBe("LOCAL001");
|
|
77
|
+
} finally {
|
|
78
|
+
rmSync(join(TEST_DIR, "src"), { recursive: true });
|
|
79
|
+
rmSync(join(TEST_DIR, "package.json"));
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test("stops at the nearest package.json — does not climb into an unrelated parent", async () => {
|
|
84
|
+
const innerProject = join(TEST_DIR, "inner-proj");
|
|
85
|
+
const innerSubDir = join(innerProject, "src");
|
|
86
|
+
mkdirSync(innerSubDir, { recursive: true });
|
|
87
|
+
writeFileSync(join(innerProject, "package.json"), `{ "name": "inner" }`);
|
|
88
|
+
try {
|
|
89
|
+
const rules = await loadLocalRules(innerSubDir);
|
|
90
|
+
expect(rules).toHaveLength(0);
|
|
91
|
+
} finally {
|
|
92
|
+
rmSync(innerProject, { recursive: true });
|
|
93
|
+
}
|
|
94
|
+
});
|
|
67
95
|
});
|
package/src/lint/rule-loader.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { readdirSync, existsSync } from "fs";
|
|
2
|
-
import { join, resolve } from "path";
|
|
2
|
+
import { dirname, join, resolve } from "path";
|
|
3
3
|
import type { LintRule } from "./rule";
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -19,18 +19,51 @@ function isLintRule(value: unknown): value is LintRule {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
|
-
*
|
|
22
|
+
* Walk up from `from` until a directory containing `.chant/rules/` is found,
|
|
23
|
+
* or until we hit the filesystem root or a non-project boundary (a directory
|
|
24
|
+
* with `package.json` but no `.chant/rules/` — that's the project root and
|
|
25
|
+
* we stop there even if no rules dir exists).
|
|
23
26
|
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
27
|
+
* Returns the absolute path to the discovered `.chant/rules/` directory, or
|
|
28
|
+
* null if none was found before we crossed the project root.
|
|
29
|
+
*/
|
|
30
|
+
function findRulesDir(from: string): string | null {
|
|
31
|
+
let cur = resolve(from);
|
|
32
|
+
while (true) {
|
|
33
|
+
const candidate = join(cur, ".chant", "rules");
|
|
34
|
+
if (existsSync(candidate)) {
|
|
35
|
+
return candidate;
|
|
36
|
+
}
|
|
37
|
+
// Stop at the nearest project root (where package.json sits) — going
|
|
38
|
+
// above it would pick up rules belonging to an unrelated parent project.
|
|
39
|
+
if (existsSync(join(cur, "package.json"))) {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
const parent = dirname(cur);
|
|
43
|
+
if (parent === cur) {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
cur = parent;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Load local lint rules from `.chant/rules/`.
|
|
52
|
+
*
|
|
53
|
+
* Scans for `.ts` files in the nearest `.chant/rules/` directory found by
|
|
54
|
+
* walking up from `projectDir` (stopping at the closest `package.json` to
|
|
55
|
+
* avoid leaking into unrelated parent projects). Dynamically imports each
|
|
56
|
+
* file and collects all exports that conform to the LintRule interface.
|
|
26
57
|
*
|
|
27
|
-
* @param projectDir -
|
|
28
|
-
*
|
|
58
|
+
* @param projectDir - Directory the lint command was invoked against. May be
|
|
59
|
+
* a sub-stack of a larger project — the rules dir is
|
|
60
|
+
* resolved by walking up.
|
|
61
|
+
* @returns Array of LintRule objects found in the discovered `.chant/rules/`.
|
|
29
62
|
*/
|
|
30
63
|
export async function loadLocalRules(projectDir: string): Promise<LintRule[]> {
|
|
31
|
-
const rulesDir =
|
|
64
|
+
const rulesDir = findRulesDir(projectDir);
|
|
32
65
|
|
|
33
|
-
if (
|
|
66
|
+
if (rulesDir === null) {
|
|
34
67
|
return [];
|
|
35
68
|
}
|
|
36
69
|
|