@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.
@@ -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('bucketName: "my-bucket"');
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('functionName: "my-function"');
62
- expect(files[0].content).toContain('runtime: "nodejs18.x"');
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("bucketName: Ref(BucketName)");
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("sourceArn: SourceBucket.arn");
105
+ expect(files[0].content).toContain("SourceArn: SourceBucket.Arn");
106
106
  });
107
107
 
108
108
  test("generates Sub as tagged template", () => {
@@ -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
- * Convert a property name to camelCase (lowercase first char).
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.charAt(0).toLowerCase() + name.slice(1);
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 * as awsLexicon from "../index";
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('bucketName: "my-bucket"');
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("bucketName: Ref(Environment)");
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.arn");
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("environment:");
152
- expect(files[0].content).toContain("vpcConfig:");
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", () => {
@@ -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
- bucketName: "my-bucket",
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)({ bucketName: "source" });
64
+ const bucket = new (Bucket as any)({ BucketName: "source" });
65
65
  // Set logical name for the AttrRef
66
- (bucket.arn as Record<string, unknown>)._setLogicalName("SourceBucket");
66
+ (bucket.Arn as Record<string, unknown>)._setLogicalName("SourceBucket");
67
67
 
68
- expect(bucket.arn.getLogicalName()).toBe("SourceBucket");
69
- expect(bucket.arn.attribute).toBe("Arn");
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
- runtime: "nodejs18.x",
83
- handler: "index.handler",
84
- code: { s3Bucket: "my-bucket", s3Key: "code.zip" },
85
- role: "arn:aws:iam::123456789012:role/lambda-role",
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
- assumeRolePolicyDocument: {
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.arn).toBeDefined();
107
- expect(bucket.domainName).toBeDefined();
108
- expect(bucket.websiteURL).toBeDefined();
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
- runtime: "nodejs18.x",
114
- handler: "index.handler",
115
- code: { s3Bucket: "bucket", s3Key: "key" },
116
- role: "role-arn",
113
+ Runtime: "nodejs18.x",
114
+ Handler: "index.handler",
115
+ Code: { S3Bucket: "bucket", S3Key: "key" },
116
+ Role: "role-arn",
117
117
  });
118
- expect(fn.arn).toBeDefined();
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
- assumeRolePolicyDocument: {},
123
+ AssumeRolePolicyDocument: {},
124
124
  });
125
- expect(role.arn).toBeDefined();
126
- expect(role.roleId).toBeDefined();
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({ bucketName: "my-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 bucketEncryption property", () => {
76
+ test("passes with BucketEncryption property", () => {
77
77
  const context = createContext(`
78
78
  const bucket = new Bucket({
79
- bucketName: "my-bucket",
80
- bucketEncryption: { serverSideEncryptionConfiguration: [] },
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 encryption property", () => {
87
+ test("passes with ServerSideEncryptionConfiguration property", () => {
88
88
  const context = createContext(`
89
89
  const bucket = new Bucket({
90
- bucketName: "my-bucket",
91
- encryption: "AES256",
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({ functionName: "my-fn" });
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 === "bucketEncryption" ||
42
- prop.name.text === "encryption" ||
43
- prop.name.text === "serverSideEncryptionConfiguration";
41
+ return prop.name.text === "BucketEncryption" ||
42
+ prop.name.text === "ServerSideEncryptionConfiguration";
44
43
  }
45
44
  return false;
46
45
  });
@@ -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
 
@@ -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.dir + "/network", {
88
+ * const network = _.nestedStack("network", import.meta.dirname + "/network", {
89
89
  * parameters: { Environment: "prod" },
90
90
  * });
91
91
  *
@@ -96,17 +96,17 @@ describe("awsPlugin", () => {
96
96
  expect(skills.length).toBeGreaterThanOrEqual(1);
97
97
  });
98
98
 
99
- test("aws-cloudformation skill has required fields", () => {
99
+ test("chant-aws skill has required fields", () => {
100
100
  const skills = awsPlugin.skills!();
101
- const cfnSkill = skills.find((s) => s.name === "aws-cloudformation");
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-cloudformation skill has triggers", () => {
107
+ test("chant-aws skill has triggers", () => {
108
108
  const skills = awsPlugin.skills!();
109
- const cfnSkill = skills.find((s) => s.name === "aws-cloudformation")!;
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-cloudformation skill has parameters", () => {
122
+ test("chant-aws skill has parameters", () => {
123
123
  const skills = awsPlugin.skills!();
124
- const cfnSkill = skills.find((s) => s.name === "aws-cloudformation")!;
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-cloudformation skill has examples", () => {
134
+ test("chant-aws skill has examples", () => {
135
135
  const skills = awsPlugin.skills!();
136
- const cfnSkill = skills.find((s) => s.name === "aws-cloudformation")!;
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-cloudformation")!;
148
+ const cfnSkill = skills.find((s) => s.name === "chant-aws")!;
149
149
  expect(cfnSkill.content).toContain("---");
150
- expect(cfnSkill.content).toContain("# AWS CloudFormation with Chant");
151
- expect(cfnSkill.content).toContain("## Common Resource Types");
152
- expect(cfnSkill.content).toContain("## Best Practices");
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
  // -----------------------------------------------------------------------