@intentius/chant 0.0.3 → 0.0.5

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 (48) hide show
  1. package/README.md +10 -351
  2. package/package.json +1 -1
  3. package/src/bench.test.ts +3 -54
  4. package/src/build.ts +14 -1
  5. package/src/cli/commands/__fixtures__/init-lexicon-output/src/plugin.ts +12 -2
  6. package/src/cli/commands/__fixtures__/init-lexicon-output/src/validate.ts +22 -18
  7. package/src/cli/commands/__snapshots__/init-lexicon.test.ts.snap +12 -2
  8. package/src/cli/commands/import.test.ts +1 -1
  9. package/src/cli/commands/init-lexicon.ts +34 -20
  10. package/src/cli/commands/init.test.ts +10 -14
  11. package/src/cli/commands/init.ts +2 -7
  12. package/src/cli/commands/lint.ts +9 -33
  13. package/src/cli/main.ts +1 -1
  14. package/src/codegen/docs-interpolation.test.ts +77 -0
  15. package/src/codegen/docs.ts +80 -5
  16. package/src/codegen/generate-registry.test.ts +1 -1
  17. package/src/codegen/generate-registry.ts +3 -3
  18. package/src/codegen/package.ts +28 -1
  19. package/src/codegen/validate.ts +16 -0
  20. package/src/discovery/collect.ts +7 -0
  21. package/src/discovery/files.ts +6 -6
  22. package/src/discovery/import.ts +1 -1
  23. package/src/index.ts +0 -1
  24. package/src/lint/engine.ts +1 -5
  25. package/src/lint/rule.ts +0 -18
  26. package/src/lint/rules/evl009-composite-no-constant.test.ts +24 -8
  27. package/src/lint/rules/evl009-composite-no-constant.ts +50 -29
  28. package/src/lint/rules/index.ts +1 -22
  29. package/src/stack-output.ts +3 -3
  30. package/src/barrel.test.ts +0 -157
  31. package/src/barrel.ts +0 -101
  32. package/src/lint/rules/barrel-import-style.test.ts +0 -80
  33. package/src/lint/rules/barrel-import-style.ts +0 -59
  34. package/src/lint/rules/enforce-barrel-import.test.ts +0 -169
  35. package/src/lint/rules/enforce-barrel-import.ts +0 -81
  36. package/src/lint/rules/enforce-barrel-ref.test.ts +0 -114
  37. package/src/lint/rules/enforce-barrel-ref.ts +0 -75
  38. package/src/lint/rules/evl006-barrel-usage.test.ts +0 -63
  39. package/src/lint/rules/evl006-barrel-usage.ts +0 -95
  40. package/src/lint/rules/evl008-unresolvable-barrel-ref.test.ts +0 -118
  41. package/src/lint/rules/evl008-unresolvable-barrel-ref.ts +0 -140
  42. package/src/lint/rules/prefer-namespace-import.test.ts +0 -102
  43. package/src/lint/rules/prefer-namespace-import.ts +0 -63
  44. package/src/lint/rules/stale-barrel-types.ts +0 -60
  45. package/src/project/scan.test.ts +0 -178
  46. package/src/project/scan.ts +0 -182
  47. package/src/project/sync.test.ts +0 -87
  48. package/src/project/sync.ts +0 -46
@@ -1,182 +0,0 @@
1
- import { readdirSync, readFileSync } from "node:fs";
2
- import { join, basename, relative } from "node:path";
3
- import ts from "typescript";
4
-
5
- export interface ProjectExport {
6
- name: string;
7
- file: string;
8
- className: string;
9
- }
10
-
11
- export interface ProjectScan {
12
- barrelPath: string;
13
- lexiconPackage: string;
14
- exports: ProjectExport[];
15
- }
16
-
17
- /**
18
- * Extract the lexicon package from export * declarations in the barrel file.
19
- * Finds the first `export * from "..."` where the module specifier is not a relative path.
20
- */
21
- function extractLexiconPackage(sourceFile: ts.SourceFile): string {
22
- for (const stmt of sourceFile.statements) {
23
- if (
24
- ts.isExportDeclaration(stmt) &&
25
- !stmt.exportClause &&
26
- stmt.moduleSpecifier &&
27
- ts.isStringLiteral(stmt.moduleSpecifier)
28
- ) {
29
- const spec = stmt.moduleSpecifier.text;
30
- if (!spec.startsWith(".")) {
31
- return spec;
32
- }
33
- }
34
- }
35
- return "";
36
- }
37
-
38
- /**
39
- * Infer class name from a variable declaration's initializer or type annotation.
40
- *
41
- * Patterns:
42
- * - `export const x = new aws.Bucket(...)` → "Bucket"
43
- * - `export const x = new _.Bucket(...)` → "Bucket"
44
- * - `export const x = new Bucket(...)` → "Bucket"
45
- * - `export const x: aws.Code = ...` → "Code"
46
- * - `export const x: _.Code = ...` → "Code"
47
- */
48
- function inferClassName(decl: ts.VariableDeclaration): string {
49
- // Check initializer: new X.ClassName(...) or new ClassName(...)
50
- if (decl.initializer && ts.isNewExpression(decl.initializer)) {
51
- const expr = decl.initializer.expression;
52
- if (ts.isPropertyAccessExpression(expr)) {
53
- return expr.name.text;
54
- }
55
- if (ts.isIdentifier(expr)) {
56
- return expr.text;
57
- }
58
- }
59
-
60
- // Check type annotation: X.ClassName or ClassName
61
- if (decl.type) {
62
- if (ts.isTypeReferenceNode(decl.type)) {
63
- const typeName = decl.type.typeName;
64
- if (ts.isQualifiedName(typeName)) {
65
- return typeName.right.text;
66
- }
67
- if (ts.isIdentifier(typeName)) {
68
- return typeName.text;
69
- }
70
- }
71
- }
72
-
73
- return "";
74
- }
75
-
76
- /**
77
- * Extract named exports from a source file.
78
- */
79
- function extractExports(
80
- sourceFile: ts.SourceFile,
81
- filePath: string,
82
- ): ProjectExport[] {
83
- const results: ProjectExport[] = [];
84
-
85
- for (const stmt of sourceFile.statements) {
86
- // export const x = ...
87
- if (
88
- ts.isVariableStatement(stmt) &&
89
- stmt.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword)
90
- ) {
91
- for (const decl of stmt.declarationList.declarations) {
92
- if (ts.isIdentifier(decl.name)) {
93
- results.push({
94
- name: decl.name.text,
95
- file: filePath,
96
- className: inferClassName(decl),
97
- });
98
- }
99
- }
100
- }
101
-
102
- // export function x() {}
103
- if (
104
- ts.isFunctionDeclaration(stmt) &&
105
- stmt.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword) &&
106
- stmt.name
107
- ) {
108
- results.push({
109
- name: stmt.name.text,
110
- file: filePath,
111
- className: "",
112
- });
113
- }
114
-
115
- // export class X {}
116
- if (
117
- ts.isClassDeclaration(stmt) &&
118
- stmt.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword) &&
119
- stmt.name
120
- ) {
121
- results.push({
122
- name: stmt.name.text,
123
- file: filePath,
124
- className: stmt.name.text,
125
- });
126
- }
127
- }
128
-
129
- return results;
130
- }
131
-
132
- /**
133
- * Scan a project directory to identify the barrel file, lexicon package,
134
- * and all exports with their inferred types.
135
- */
136
- export function scanProject(dir: string): ProjectScan {
137
- const barrelPath = join(dir, "_.ts");
138
-
139
- let barrelContent: string;
140
- try {
141
- barrelContent = readFileSync(barrelPath, "utf-8");
142
- } catch {
143
- throw new Error(`No barrel file found at ${barrelPath}`);
144
- }
145
-
146
- const barrelSource = ts.createSourceFile(
147
- "_.ts",
148
- barrelContent,
149
- ts.ScriptTarget.Latest,
150
- true,
151
- );
152
-
153
- const lexiconPackage = extractLexiconPackage(barrelSource);
154
-
155
- // Scan sibling .ts files
156
- const entries = readdirSync(dir, { withFileTypes: true });
157
- const exports: ProjectExport[] = [];
158
-
159
- for (const entry of entries) {
160
- if (!entry.isFile()) continue;
161
- if (!entry.name.endsWith(".ts")) continue;
162
- if (entry.name === "_.ts") continue;
163
- if (entry.name.startsWith("_")) continue;
164
- if (entry.name.endsWith(".test.ts") || entry.name.endsWith(".spec.ts"))
165
- continue;
166
- if (entry.name.endsWith(".d.ts")) continue;
167
-
168
- const filePath = "./" + entry.name.replace(/\.ts$/, "");
169
- const fullPath = join(dir, entry.name);
170
- const content = readFileSync(fullPath, "utf-8");
171
- const sourceFile = ts.createSourceFile(
172
- entry.name,
173
- content,
174
- ts.ScriptTarget.Latest,
175
- true,
176
- );
177
-
178
- exports.push(...extractExports(sourceFile, filePath));
179
- }
180
-
181
- return { barrelPath, lexiconPackage, exports };
182
- }
@@ -1,87 +0,0 @@
1
- import { describe, test, expect } from "bun:test";
2
- import { generateBarrelTypes, syncProject } from "./sync";
3
- import { withTestDir } from "@intentius/chant-test-utils";
4
- import { writeFile, readFile } from "node:fs/promises";
5
- import { join } from "node:path";
6
- import type { ProjectScan } from "./scan";
7
-
8
- describe("generateBarrelTypes", () => {
9
- test("generates valid .d.ts with import and typed $", () => {
10
- const scan: ProjectScan = {
11
- barrelPath: "/project/_.ts",
12
- lexiconPackage: "@intentius/chant-lexicon-testdom",
13
- exports: [
14
- { name: "dataBucket", file: "./data-bucket", className: "Bucket" },
15
- { name: "logsBucket", file: "./logs-bucket", className: "Bucket" },
16
- { name: "functionRole", file: "./iam", className: "Role" },
17
- ],
18
- };
19
-
20
- const result = generateBarrelTypes(scan);
21
- expect(result).toContain(
22
- 'import type * as _lexicon from "@intentius/chant-lexicon-testdom";',
23
- );
24
- expect(result).toContain("dataBucket: _lexicon.Bucket;");
25
- expect(result).toContain("logsBucket: _lexicon.Bucket;");
26
- expect(result).toContain("functionRole: _lexicon.Role;");
27
- expect(result).toContain("export declare const $: typeof _barrel;");
28
- });
29
-
30
- test("uses unknown for unresolvable types", () => {
31
- const scan: ProjectScan = {
32
- barrelPath: "/project/_.ts",
33
- lexiconPackage: "@intentius/chant-lexicon-testdom",
34
- exports: [
35
- { name: "dataBucket", file: "./data-bucket", className: "Bucket" },
36
- { name: "greeting", file: "./utils", className: "" },
37
- ],
38
- };
39
-
40
- const result = generateBarrelTypes(scan);
41
- expect(result).toContain("dataBucket: _lexicon.Bucket;");
42
- expect(result).toContain("greeting: unknown;");
43
- });
44
-
45
- test("handles scan with no lexicon package", () => {
46
- const scan: ProjectScan = {
47
- barrelPath: "/project/_.ts",
48
- lexiconPackage: "",
49
- exports: [
50
- { name: "bucket", file: "./storage", className: "Bucket" },
51
- ],
52
- };
53
-
54
- const result = generateBarrelTypes(scan);
55
- expect(result).not.toContain("import type");
56
- expect(result).toContain("bucket: Bucket;");
57
- });
58
- });
59
-
60
- describe("syncProject", () => {
61
- test("writes _.d.ts to directory", async () => {
62
- await withTestDir(async (dir) => {
63
- await writeFile(
64
- join(dir, "_.ts"),
65
- `export * from "@intentius/chant-lexicon-testdom";\n`,
66
- );
67
- await writeFile(
68
- join(dir, "storage.ts"),
69
- [
70
- `import * as td from "@intentius/chant-lexicon-testdom";`,
71
- `export const dataBucket = new td.Bucket({});`,
72
- `export const logsBucket = new td.Bucket({});`,
73
- ].join("\n"),
74
- );
75
-
76
- syncProject(dir);
77
-
78
- const dtsContent = await readFile(join(dir, "_.d.ts"), "utf-8");
79
- expect(dtsContent).toContain(
80
- 'import type * as _lexicon from "@intentius/chant-lexicon-testdom";',
81
- );
82
- expect(dtsContent).toContain("dataBucket: _lexicon.Bucket;");
83
- expect(dtsContent).toContain("logsBucket: _lexicon.Bucket;");
84
- expect(dtsContent).toContain("export declare const $: typeof _barrel;");
85
- });
86
- });
87
- });
@@ -1,46 +0,0 @@
1
- import { writeFileSync } from "node:fs";
2
- import { join } from "node:path";
3
- import { scanProject, type ProjectScan } from "./scan";
4
-
5
- /**
6
- * Generate a _.d.ts file content from a project scan.
7
- * Produces typed declarations for the barrel's $ proxy.
8
- */
9
- export function generateBarrelTypes(scan: ProjectScan): string {
10
- const lines: string[] = [];
11
-
12
- lines.push("// _.d.ts (auto-generated by chant lint)");
13
-
14
- if (scan.lexiconPackage) {
15
- lines.push(`import type * as _lexicon from "${scan.lexiconPackage}";`);
16
- }
17
-
18
- lines.push("declare const _barrel: {");
19
-
20
- for (const exp of scan.exports) {
21
- let typeName: string;
22
- if (!exp.className) {
23
- typeName = "unknown";
24
- } else if (scan.lexiconPackage) {
25
- typeName = `_lexicon.${exp.className}`;
26
- } else {
27
- typeName = exp.className;
28
- }
29
- lines.push(` ${exp.name}: ${typeName};`);
30
- }
31
-
32
- lines.push("};");
33
- lines.push("export declare const $: typeof _barrel;");
34
- lines.push("");
35
-
36
- return lines.join("\n");
37
- }
38
-
39
- /**
40
- * Scan a project directory and write the _.d.ts file.
41
- */
42
- export function syncProject(dir: string): void {
43
- const scan = scanProject(dir);
44
- const content = generateBarrelTypes(scan);
45
- writeFileSync(join(dir, "_.d.ts"), content, "utf-8");
46
- }