@intentius/chant-lexicon-aws 0.0.6 → 0.0.8
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/dist/integrity.json +7 -7
- package/dist/manifest.json +1 -1
- package/dist/meta.json +3701 -3701
- package/dist/rules/ext001.ts +2 -4
- package/dist/rules/s3-encryption.ts +2 -3
- package/dist/skills/chant-aws.md +72 -0
- package/dist/types/index.d.ts +57087 -57087
- package/package.json +1 -1
- package/src/codegen/__snapshots__/snapshot.test.ts.snap +18 -18
- package/src/codegen/docs.ts +59 -4
- package/src/codegen/generate.ts +1 -13
- package/src/codegen/package.ts +2 -0
- package/src/codegen/typecheck.test.ts +1 -1
- package/src/generated/index.d.ts +57087 -57087
- package/src/generated/index.ts +1351 -1351
- package/src/generated/lexicon-aws.json +3701 -3701
- package/src/import/generator.test.ts +5 -5
- package/src/import/generator.ts +4 -4
- package/src/import/roundtrip-fixtures.test.ts +2 -1
- package/src/import/roundtrip.test.ts +5 -5
- package/src/integration.test.ts +21 -21
- package/src/lint/post-synth/ext001.ts +2 -4
- package/src/lint/rules/rules.test.ts +8 -8
- package/src/lint/rules/s3-encryption.ts +2 -3
- package/src/lsp/completions.ts +2 -0
- package/src/lsp/hover.ts +2 -0
- package/src/nested-stack.ts +1 -1
- package/src/plugin.test.ts +13 -15
- package/src/plugin.ts +116 -110
- package/src/serializer.test.ts +42 -43
- package/src/serializer.ts +7 -16
- package/dist/skills/aws-cloudformation.md +0 -41
- package/src/codegen/rollback.test.ts +0 -80
- package/src/codegen/rollback.ts +0 -20
|
@@ -35,7 +35,7 @@ describe("CFGenerator", () => {
|
|
|
35
35
|
|
|
36
36
|
expect(files[0].content).toContain("import { Bucket }");
|
|
37
37
|
expect(files[0].content).toContain("export const MyBucket = new Bucket({");
|
|
38
|
-
expect(files[0].content).toContain('
|
|
38
|
+
expect(files[0].content).toContain('BucketName: "my-bucket"');
|
|
39
39
|
});
|
|
40
40
|
|
|
41
41
|
test("generates Lambda Function", () => {
|
|
@@ -58,8 +58,8 @@ describe("CFGenerator", () => {
|
|
|
58
58
|
|
|
59
59
|
expect(files[0].content).toContain("import { Function }");
|
|
60
60
|
expect(files[0].content).toContain("export const MyFunction = new Function({");
|
|
61
|
-
expect(files[0].content).toContain('
|
|
62
|
-
expect(files[0].content).toContain('
|
|
61
|
+
expect(files[0].content).toContain('FunctionName: "my-function"');
|
|
62
|
+
expect(files[0].content).toContain('Runtime: "nodejs18.x"');
|
|
63
63
|
});
|
|
64
64
|
|
|
65
65
|
test("generates Ref as variable reference", () => {
|
|
@@ -78,7 +78,7 @@ describe("CFGenerator", () => {
|
|
|
78
78
|
|
|
79
79
|
const files = generator.generate(ir);
|
|
80
80
|
|
|
81
|
-
expect(files[0].content).toContain("
|
|
81
|
+
expect(files[0].content).toContain("BucketName: Ref(BucketName)");
|
|
82
82
|
});
|
|
83
83
|
|
|
84
84
|
test("generates GetAtt as property access", () => {
|
|
@@ -102,7 +102,7 @@ describe("CFGenerator", () => {
|
|
|
102
102
|
|
|
103
103
|
const files = generator.generate(ir);
|
|
104
104
|
|
|
105
|
-
expect(files[0].content).toContain("
|
|
105
|
+
expect(files[0].content).toContain("SourceArn: SourceBucket.Arn");
|
|
106
106
|
});
|
|
107
107
|
|
|
108
108
|
test("generates Sub as tagged template", () => {
|
package/src/import/generator.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { createRequire } from "module";
|
|
1
2
|
import type { TemplateIR, ResourceIR, ParameterIR } from "@intentius/chant/import/parser";
|
|
3
|
+
const require = createRequire(import.meta.url);
|
|
2
4
|
import type { TypeScriptGenerator, GeneratedFile } from "@intentius/chant/import/generator";
|
|
3
5
|
import { topoSort } from "@intentius/chant/codegen/topo-sort";
|
|
4
6
|
import { hasIntrinsicInValue, irUsesIntrinsic, collectDependencies } from "@intentius/chant/import/ir-utils";
|
|
@@ -455,11 +457,9 @@ export class CFGenerator implements TypeScriptGenerator {
|
|
|
455
457
|
}
|
|
456
458
|
|
|
457
459
|
/**
|
|
458
|
-
*
|
|
459
|
-
* Used for resource property access (e.g., GetAtt attribute names)
|
|
460
|
-
* which matches chant's camelCase property convention.
|
|
460
|
+
* Property names use spec-native casing (PascalCase for CloudFormation).
|
|
461
461
|
*/
|
|
462
462
|
private toPropName(name: string): string {
|
|
463
|
-
return name
|
|
463
|
+
return name;
|
|
464
464
|
}
|
|
465
465
|
}
|
|
@@ -5,7 +5,8 @@ import { CFParser } from "./parser";
|
|
|
5
5
|
import { CFGenerator } from "./generator";
|
|
6
6
|
import { build } from "@intentius/chant/build";
|
|
7
7
|
import { awsSerializer } from "../serializer";
|
|
8
|
-
import
|
|
8
|
+
// Dynamic import to get all export keys for validation (not a runtime barrel import)
|
|
9
|
+
const awsLexicon = await import("../index");
|
|
9
10
|
|
|
10
11
|
const parser = new CFParser();
|
|
11
12
|
const generator = new CFGenerator();
|
|
@@ -24,7 +24,7 @@ describe("CloudFormation round-trip", () => {
|
|
|
24
24
|
const files = generator.generate(ir);
|
|
25
25
|
|
|
26
26
|
expect(files[0].content).toContain("Bucket");
|
|
27
|
-
expect(files[0].content).toContain('
|
|
27
|
+
expect(files[0].content).toContain('BucketName: "my-bucket"');
|
|
28
28
|
expect(files[0].content).toContain("export const MyBucket");
|
|
29
29
|
});
|
|
30
30
|
|
|
@@ -53,7 +53,7 @@ describe("CloudFormation round-trip", () => {
|
|
|
53
53
|
|
|
54
54
|
expect(files[0].content).toContain("Parameter");
|
|
55
55
|
expect(files[0].content).toContain("Environment");
|
|
56
|
-
expect(files[0].content).toContain("
|
|
56
|
+
expect(files[0].content).toContain("BucketName: Ref(Environment)");
|
|
57
57
|
});
|
|
58
58
|
|
|
59
59
|
test("round-trips template with Fn::Sub", () => {
|
|
@@ -115,7 +115,7 @@ describe("CloudFormation round-trip", () => {
|
|
|
115
115
|
|
|
116
116
|
expect(files[0].content).toContain("Role");
|
|
117
117
|
expect(files[0].content).toContain("Function");
|
|
118
|
-
expect(files[0].content).toContain("LambdaRole.
|
|
118
|
+
expect(files[0].content).toContain("LambdaRole.Arn");
|
|
119
119
|
});
|
|
120
120
|
|
|
121
121
|
test("round-trips complex nested properties", () => {
|
|
@@ -148,8 +148,8 @@ describe("CloudFormation round-trip", () => {
|
|
|
148
148
|
const files = generator.generate(ir);
|
|
149
149
|
|
|
150
150
|
expect(files[0].content).toContain("Function");
|
|
151
|
-
expect(files[0].content).toContain("
|
|
152
|
-
expect(files[0].content).toContain("
|
|
151
|
+
expect(files[0].content).toContain("Environment:");
|
|
152
|
+
expect(files[0].content).toContain("VpcConfig:");
|
|
153
153
|
});
|
|
154
154
|
|
|
155
155
|
test("round-trips empty template", () => {
|
package/src/integration.test.ts
CHANGED
|
@@ -17,7 +17,7 @@ describe("AWS Integration", () => {
|
|
|
17
17
|
|
|
18
18
|
test("serializes S3 bucket with properties", () => {
|
|
19
19
|
const bucket = new (Bucket as any)({
|
|
20
|
-
|
|
20
|
+
BucketName: "my-bucket",
|
|
21
21
|
});
|
|
22
22
|
|
|
23
23
|
const entities = new Map<string, Declarable>();
|
|
@@ -61,12 +61,12 @@ describe("AWS Integration", () => {
|
|
|
61
61
|
|
|
62
62
|
describe("Cross-resource references", () => {
|
|
63
63
|
test("GetAtt for bucket ARN", () => {
|
|
64
|
-
const bucket = new (Bucket as any)({
|
|
64
|
+
const bucket = new (Bucket as any)({ BucketName: "source" });
|
|
65
65
|
// Set logical name for the AttrRef
|
|
66
|
-
(bucket.
|
|
66
|
+
(bucket.Arn as Record<string, unknown>)._setLogicalName("SourceBucket");
|
|
67
67
|
|
|
68
|
-
expect(bucket.
|
|
69
|
-
expect(bucket.
|
|
68
|
+
expect(bucket.Arn.getLogicalName()).toBe("SourceBucket");
|
|
69
|
+
expect(bucket.Arn.attribute).toBe("Arn");
|
|
70
70
|
});
|
|
71
71
|
});
|
|
72
72
|
|
|
@@ -79,10 +79,10 @@ describe("AWS Integration", () => {
|
|
|
79
79
|
|
|
80
80
|
test("Lambda Function has correct entity type", () => {
|
|
81
81
|
const fn = new (Function as any)({
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
82
|
+
Runtime: "nodejs18.x",
|
|
83
|
+
Handler: "index.handler",
|
|
84
|
+
Code: { S3Bucket: "my-bucket", S3Key: "code.zip" },
|
|
85
|
+
Role: "arn:aws:iam::123456789012:role/lambda-role",
|
|
86
86
|
});
|
|
87
87
|
expect(fn.entityType).toBe("AWS::Lambda::Function");
|
|
88
88
|
expect(fn[DECLARABLE_MARKER]).toBe(true);
|
|
@@ -90,7 +90,7 @@ describe("AWS Integration", () => {
|
|
|
90
90
|
|
|
91
91
|
test("IAM Role has correct entity type", () => {
|
|
92
92
|
const role = new (Role as any)({
|
|
93
|
-
|
|
93
|
+
AssumeRolePolicyDocument: {
|
|
94
94
|
Version: "2012-10-17",
|
|
95
95
|
Statement: [],
|
|
96
96
|
},
|
|
@@ -103,27 +103,27 @@ describe("AWS Integration", () => {
|
|
|
103
103
|
describe("AttrRefs", () => {
|
|
104
104
|
test("Bucket has expected AttrRefs", () => {
|
|
105
105
|
const bucket = new (Bucket as any)({});
|
|
106
|
-
expect(bucket.
|
|
107
|
-
expect(bucket.
|
|
108
|
-
expect(bucket.
|
|
106
|
+
expect(bucket.Arn).toBeDefined();
|
|
107
|
+
expect(bucket.DomainName).toBeDefined();
|
|
108
|
+
expect(bucket.WebsiteURL).toBeDefined();
|
|
109
109
|
});
|
|
110
110
|
|
|
111
111
|
test("Lambda Function has expected AttrRefs", () => {
|
|
112
112
|
const fn = new (Function as any)({
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
113
|
+
Runtime: "nodejs18.x",
|
|
114
|
+
Handler: "index.handler",
|
|
115
|
+
Code: { S3Bucket: "bucket", S3Key: "key" },
|
|
116
|
+
Role: "role-arn",
|
|
117
117
|
});
|
|
118
|
-
expect(fn.
|
|
118
|
+
expect(fn.Arn).toBeDefined();
|
|
119
119
|
});
|
|
120
120
|
|
|
121
121
|
test("IAM Role has expected AttrRefs", () => {
|
|
122
122
|
const role = new (Role as any)({
|
|
123
|
-
|
|
123
|
+
AssumeRolePolicyDocument: {},
|
|
124
124
|
});
|
|
125
|
-
expect(role.
|
|
126
|
-
expect(role.
|
|
125
|
+
expect(role.Arn).toBeDefined();
|
|
126
|
+
expect(role.RoleId).toBeDefined();
|
|
127
127
|
});
|
|
128
128
|
});
|
|
129
129
|
});
|
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
* - required_xor: exactly one of the listed properties must exist
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
+
import { readFileSync } from "fs";
|
|
15
|
+
import { join } from "path";
|
|
14
16
|
import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
|
|
15
17
|
import { parseCFTemplate, type CFResource } from "./cf-refs";
|
|
16
18
|
|
|
@@ -34,10 +36,6 @@ interface LexiconEntry {
|
|
|
34
36
|
function loadLexiconConstraints(): Map<string, ExtensionConstraint[]> {
|
|
35
37
|
const map = new Map<string, ExtensionConstraint[]>();
|
|
36
38
|
try {
|
|
37
|
-
const { readFileSync } = require("fs");
|
|
38
|
-
const { join, dirname } = require("path");
|
|
39
|
-
const { fileURLToPath } = require("url");
|
|
40
|
-
|
|
41
39
|
// Navigate from src/lint/post-synth/ up to the package root
|
|
42
40
|
const pkgDir = join(__dirname, "..", "..", "..");
|
|
43
41
|
const lexiconPath = join(pkgDir, "src", "generated", "lexicon-aws.json");
|
|
@@ -65,7 +65,7 @@ describe("AWS Lint Rules", () => {
|
|
|
65
65
|
describe("WAW006: S3 Encryption", () => {
|
|
66
66
|
test("warns on Bucket without encryption", () => {
|
|
67
67
|
const context = createContext(`
|
|
68
|
-
const bucket = new Bucket({
|
|
68
|
+
const bucket = new Bucket({ BucketName: "my-bucket" });
|
|
69
69
|
`);
|
|
70
70
|
const diagnostics = s3EncryptionRule.check(context);
|
|
71
71
|
expect(diagnostics).toHaveLength(1);
|
|
@@ -73,22 +73,22 @@ describe("AWS Lint Rules", () => {
|
|
|
73
73
|
expect(diagnostics[0].message).toContain("encryption");
|
|
74
74
|
});
|
|
75
75
|
|
|
76
|
-
test("passes with
|
|
76
|
+
test("passes with BucketEncryption property", () => {
|
|
77
77
|
const context = createContext(`
|
|
78
78
|
const bucket = new Bucket({
|
|
79
|
-
|
|
80
|
-
|
|
79
|
+
BucketName: "my-bucket",
|
|
80
|
+
BucketEncryption: { ServerSideEncryptionConfiguration: [] },
|
|
81
81
|
});
|
|
82
82
|
`);
|
|
83
83
|
const diagnostics = s3EncryptionRule.check(context);
|
|
84
84
|
expect(diagnostics).toHaveLength(0);
|
|
85
85
|
});
|
|
86
86
|
|
|
87
|
-
test("passes with
|
|
87
|
+
test("passes with ServerSideEncryptionConfiguration property", () => {
|
|
88
88
|
const context = createContext(`
|
|
89
89
|
const bucket = new Bucket({
|
|
90
|
-
|
|
91
|
-
|
|
90
|
+
BucketName: "my-bucket",
|
|
91
|
+
ServerSideEncryptionConfiguration: [],
|
|
92
92
|
});
|
|
93
93
|
`);
|
|
94
94
|
const diagnostics = s3EncryptionRule.check(context);
|
|
@@ -97,7 +97,7 @@ describe("AWS Lint Rules", () => {
|
|
|
97
97
|
|
|
98
98
|
test("ignores non-Bucket constructors", () => {
|
|
99
99
|
const context = createContext(`
|
|
100
|
-
const fn = new Function({
|
|
100
|
+
const fn = new Function({ FunctionName: "my-fn" });
|
|
101
101
|
`);
|
|
102
102
|
const diagnostics = s3EncryptionRule.check(context);
|
|
103
103
|
expect(diagnostics).toHaveLength(0);
|
|
@@ -38,9 +38,8 @@ export const s3EncryptionRule: LintRule = {
|
|
|
38
38
|
if (ts.isObjectLiteralExpression(props)) {
|
|
39
39
|
const hasEncryption = props.properties.some((prop) => {
|
|
40
40
|
if (ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name)) {
|
|
41
|
-
return prop.name.text === "
|
|
42
|
-
prop.name.text === "
|
|
43
|
-
prop.name.text === "serverSideEncryptionConfiguration";
|
|
41
|
+
return prop.name.text === "BucketEncryption" ||
|
|
42
|
+
prop.name.text === "ServerSideEncryptionConfiguration";
|
|
44
43
|
}
|
|
45
44
|
return false;
|
|
46
45
|
});
|
package/src/lsp/completions.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { createRequire } from "module";
|
|
1
2
|
import type { CompletionContext, CompletionItem } from "@intentius/chant/lsp/types";
|
|
2
3
|
import { LexiconIndex, lexiconCompletions, type LexiconEntry } from "@intentius/chant/lsp/lexicon-providers";
|
|
4
|
+
const require = createRequire(import.meta.url);
|
|
3
5
|
|
|
4
6
|
let cachedIndex: LexiconIndex | null = null;
|
|
5
7
|
|
package/src/lsp/hover.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { createRequire } from "module";
|
|
1
2
|
import type { HoverContext, HoverInfo } from "@intentius/chant/lsp/types";
|
|
2
3
|
import { LexiconIndex, lexiconHover, type LexiconEntry } from "@intentius/chant/lsp/lexicon-providers";
|
|
4
|
+
const require = createRequire(import.meta.url);
|
|
3
5
|
|
|
4
6
|
let cachedIndex: LexiconIndex | null = null;
|
|
5
7
|
|
package/src/nested-stack.ts
CHANGED
|
@@ -85,7 +85,7 @@ export function isNestedStackInstance(value: unknown): value is NestedStackInsta
|
|
|
85
85
|
*
|
|
86
86
|
* @example
|
|
87
87
|
* ```ts
|
|
88
|
-
* const network = _.nestedStack("network", import.meta.
|
|
88
|
+
* const network = _.nestedStack("network", import.meta.dirname + "/network", {
|
|
89
89
|
* parameters: { Environment: "prod" },
|
|
90
90
|
* });
|
|
91
91
|
*
|
package/src/plugin.test.ts
CHANGED
|
@@ -96,17 +96,17 @@ describe("awsPlugin", () => {
|
|
|
96
96
|
expect(skills.length).toBeGreaterThanOrEqual(1);
|
|
97
97
|
});
|
|
98
98
|
|
|
99
|
-
test("aws
|
|
99
|
+
test("chant-aws skill has required fields", () => {
|
|
100
100
|
const skills = awsPlugin.skills!();
|
|
101
|
-
const cfnSkill = skills.find((s) => s.name === "aws
|
|
101
|
+
const cfnSkill = skills.find((s) => s.name === "chant-aws");
|
|
102
102
|
expect(cfnSkill).toBeDefined();
|
|
103
103
|
expect(cfnSkill!.description.length).toBeGreaterThan(0);
|
|
104
104
|
expect(cfnSkill!.content.length).toBeGreaterThan(0);
|
|
105
105
|
});
|
|
106
106
|
|
|
107
|
-
test("aws
|
|
107
|
+
test("chant-aws skill has triggers", () => {
|
|
108
108
|
const skills = awsPlugin.skills!();
|
|
109
|
-
const cfnSkill = skills.find((s) => s.name === "aws
|
|
109
|
+
const cfnSkill = skills.find((s) => s.name === "chant-aws")!;
|
|
110
110
|
expect(cfnSkill.triggers).toBeDefined();
|
|
111
111
|
expect(cfnSkill.triggers!.length).toBeGreaterThanOrEqual(1);
|
|
112
112
|
|
|
@@ -119,9 +119,9 @@ describe("awsPlugin", () => {
|
|
|
119
119
|
expect(contextTrigger!.value).toBe("aws");
|
|
120
120
|
});
|
|
121
121
|
|
|
122
|
-
test("aws
|
|
122
|
+
test("chant-aws skill has parameters", () => {
|
|
123
123
|
const skills = awsPlugin.skills!();
|
|
124
|
-
const cfnSkill = skills.find((s) => s.name === "aws
|
|
124
|
+
const cfnSkill = skills.find((s) => s.name === "chant-aws")!;
|
|
125
125
|
expect(cfnSkill.parameters).toBeDefined();
|
|
126
126
|
expect(cfnSkill.parameters!.length).toBeGreaterThanOrEqual(1);
|
|
127
127
|
|
|
@@ -131,9 +131,9 @@ describe("awsPlugin", () => {
|
|
|
131
131
|
expect(resourceTypeParam!.type).toBe("string");
|
|
132
132
|
});
|
|
133
133
|
|
|
134
|
-
test("aws
|
|
134
|
+
test("chant-aws skill has examples", () => {
|
|
135
135
|
const skills = awsPlugin.skills!();
|
|
136
|
-
const cfnSkill = skills.find((s) => s.name === "aws
|
|
136
|
+
const cfnSkill = skills.find((s) => s.name === "chant-aws")!;
|
|
137
137
|
expect(cfnSkill.examples).toBeDefined();
|
|
138
138
|
expect(cfnSkill.examples!.length).toBeGreaterThanOrEqual(1);
|
|
139
139
|
|
|
@@ -145,11 +145,12 @@ describe("awsPlugin", () => {
|
|
|
145
145
|
|
|
146
146
|
test("skill content is valid markdown with frontmatter", () => {
|
|
147
147
|
const skills = awsPlugin.skills!();
|
|
148
|
-
const cfnSkill = skills.find((s) => s.name === "aws
|
|
148
|
+
const cfnSkill = skills.find((s) => s.name === "chant-aws")!;
|
|
149
149
|
expect(cfnSkill.content).toContain("---");
|
|
150
|
-
expect(cfnSkill.content).toContain("
|
|
151
|
-
expect(cfnSkill.content).toContain("
|
|
152
|
-
expect(cfnSkill.content).toContain("
|
|
150
|
+
expect(cfnSkill.content).toContain("skill: chant-aws");
|
|
151
|
+
expect(cfnSkill.content).toContain("user-invocable: true");
|
|
152
|
+
expect(cfnSkill.content).toContain("chant build");
|
|
153
|
+
expect(cfnSkill.content).toContain("aws cloudformation deploy");
|
|
153
154
|
});
|
|
154
155
|
});
|
|
155
156
|
|
|
@@ -174,9 +175,6 @@ describe("awsPlugin", () => {
|
|
|
174
175
|
expect(typeof awsPlugin.package).toBe("function");
|
|
175
176
|
});
|
|
176
177
|
|
|
177
|
-
test("rollback is a function", () => {
|
|
178
|
-
expect(typeof awsPlugin.rollback).toBe("function");
|
|
179
|
-
});
|
|
180
178
|
});
|
|
181
179
|
|
|
182
180
|
// -----------------------------------------------------------------------
|