@intentius/chant-lexicon-aws 0.0.4 → 0.0.6
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/README.md +9 -425
- package/dist/integrity.json +11 -11
- package/dist/manifest.json +7 -1
- package/dist/meta.json +340 -320
- package/dist/skills/aws-cloudformation.md +1 -1
- package/dist/types/index.d.ts +39 -0
- package/package.json +2 -2
- package/src/codegen/docs.ts +80 -473
- package/src/codegen/generate.ts +1 -1
- package/src/codegen/sam.ts +11 -11
- package/src/generated/index.d.ts +39 -0
- package/src/generated/index.ts +4 -0
- package/src/generated/lexicon-aws.json +340 -320
- package/src/import/generator.test.ts +117 -6
- package/src/import/generator.ts +178 -62
- package/src/import/parser.ts +1 -1
- package/src/import/roundtrip-fixtures.test.ts +72 -11
- package/src/import/roundtrip.test.ts +6 -5
- package/src/index.ts +8 -1
- package/src/intrinsics.ts +27 -0
- package/src/parameter.ts +16 -0
- package/src/plugin.test.ts +1 -1
- package/src/plugin.ts +20 -48
- package/src/serializer.test.ts +4 -19
- package/src/spec/parse.ts +2 -2
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { describe, test, expect } from "bun:test";
|
|
2
|
-
import { readdirSync, readFileSync } from "fs";
|
|
2
|
+
import { readdirSync, readFileSync, mkdtempSync, writeFileSync, mkdirSync, rmSync } from "fs";
|
|
3
3
|
import { join } from "path";
|
|
4
4
|
import { CFParser } from "./parser";
|
|
5
5
|
import { CFGenerator } from "./generator";
|
|
6
|
+
import { build } from "@intentius/chant/build";
|
|
7
|
+
import { awsSerializer } from "../serializer";
|
|
8
|
+
import * as awsLexicon from "../index";
|
|
6
9
|
|
|
7
10
|
const parser = new CFParser();
|
|
8
11
|
const generator = new CFGenerator();
|
|
@@ -10,6 +13,15 @@ const generator = new CFGenerator();
|
|
|
10
13
|
const roundtripDir = join(import.meta.dir, "../testdata/roundtrip");
|
|
11
14
|
const samDir = join(import.meta.dir, "../testdata/sam-fixtures");
|
|
12
15
|
|
|
16
|
+
/** Extract imported symbols from `import { A, B } from "..."` statements */
|
|
17
|
+
function extractImportedSymbols(code: string): string[] {
|
|
18
|
+
const match = code.match(/import\s*\{([^}]+)\}\s*from\s*/);
|
|
19
|
+
if (!match) return [];
|
|
20
|
+
return match[1].split(",").map((s) => s.trim()).filter(Boolean);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const lexiconExports = new Set(Object.keys(awsLexicon));
|
|
24
|
+
|
|
13
25
|
describe("CF roundtrip fixtures", () => {
|
|
14
26
|
const fixtures = readdirSync(roundtripDir).filter((f) => f.endsWith(".json"));
|
|
15
27
|
|
|
@@ -24,32 +36,81 @@ describe("CF roundtrip fixtures", () => {
|
|
|
24
36
|
// Verify at least one file was generated
|
|
25
37
|
expect(files.length).toBeGreaterThanOrEqual(1);
|
|
26
38
|
|
|
27
|
-
// Verify
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
);
|
|
31
|
-
expect(hasBarrel).toBe(true);
|
|
39
|
+
// Verify main file exists
|
|
40
|
+
const hasMain = files.some((f) => f.path === "main.ts");
|
|
41
|
+
expect(hasMain).toBe(true);
|
|
32
42
|
|
|
33
43
|
// Verify resource count: each resource should be represented in generated output
|
|
34
44
|
const resourceNames = Object.keys(template.Resources ?? {});
|
|
35
|
-
const mainFile = files.find((f) => f.path === "main.ts"
|
|
45
|
+
const mainFile = files.find((f) => f.path === "main.ts");
|
|
36
46
|
expect(mainFile).toBeDefined();
|
|
37
47
|
|
|
38
48
|
for (const name of resourceNames) {
|
|
39
|
-
|
|
40
|
-
expect(mainFile!.content).toContain(varName);
|
|
49
|
+
expect(mainFile!.content).toContain(name);
|
|
41
50
|
}
|
|
42
51
|
|
|
43
52
|
// If parameters exist, verify they appear in the output
|
|
44
53
|
const paramNames = Object.keys(template.Parameters ?? {});
|
|
45
54
|
for (const name of paramNames) {
|
|
46
|
-
|
|
47
|
-
|
|
55
|
+
expect(mainFile!.content).toContain(name);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Verify all imported symbols actually exist as exports from the lexicon
|
|
59
|
+
const importedSymbols = extractImportedSymbols(mainFile!.content);
|
|
60
|
+
for (const sym of importedSymbols) {
|
|
61
|
+
expect(lexiconExports.has(sym)).toBe(true);
|
|
48
62
|
}
|
|
49
63
|
});
|
|
50
64
|
}
|
|
51
65
|
});
|
|
52
66
|
|
|
67
|
+
describe("parameters.json build roundtrip", () => {
|
|
68
|
+
test("generated code builds and produces correct CF Parameters", async () => {
|
|
69
|
+
const content = readFileSync(join(roundtripDir, "parameters.json"), "utf-8");
|
|
70
|
+
const source = JSON.parse(content);
|
|
71
|
+
|
|
72
|
+
const ir = parser.parse(content);
|
|
73
|
+
const files = generator.generate(ir);
|
|
74
|
+
const mainFile = files.find((f) => f.path === "main.ts")!;
|
|
75
|
+
|
|
76
|
+
// Write generated code to a temp directory inside the monorepo (so workspace packages resolve)
|
|
77
|
+
const dir = mkdtempSync(join(import.meta.dir, "../../.roundtrip-tmp-"));
|
|
78
|
+
try {
|
|
79
|
+
const srcDir = join(dir, "src");
|
|
80
|
+
mkdirSync(srcDir);
|
|
81
|
+
|
|
82
|
+
// Write generated code as-is (direct imports)
|
|
83
|
+
writeFileSync(join(srcDir, "main.ts"), mainFile.content);
|
|
84
|
+
|
|
85
|
+
// Build and verify
|
|
86
|
+
const result = await build(srcDir, [awsSerializer]);
|
|
87
|
+
expect(result.errors).toHaveLength(0);
|
|
88
|
+
|
|
89
|
+
const template = JSON.parse(result.outputs.get("aws")!);
|
|
90
|
+
|
|
91
|
+
// Verify parameters round-tripped correctly (names preserved as-is)
|
|
92
|
+
for (const [name, param] of Object.entries(source.Parameters ?? {})) {
|
|
93
|
+
const p = param as Record<string, unknown>;
|
|
94
|
+
expect(template.Parameters[name]).toBeDefined();
|
|
95
|
+
expect(template.Parameters[name].Type).toBe(p.Type);
|
|
96
|
+
if (p.Description) {
|
|
97
|
+
expect(template.Parameters[name].Description).toBe(p.Description);
|
|
98
|
+
}
|
|
99
|
+
if (p.Default !== undefined) {
|
|
100
|
+
expect(template.Parameters[name].Default).toBe(p.Default);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Verify resources round-tripped (names preserved as-is)
|
|
105
|
+
for (const name of Object.keys(source.Resources ?? {})) {
|
|
106
|
+
expect(template.Resources[name]).toBeDefined();
|
|
107
|
+
}
|
|
108
|
+
} finally {
|
|
109
|
+
rmSync(dir, { recursive: true, force: true });
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
|
|
53
114
|
describe("SAM roundtrip fixtures", () => {
|
|
54
115
|
const fixtures = readdirSync(samDir).filter(
|
|
55
116
|
(f) => f.endsWith(".yaml") || f.endsWith(".yml"),
|
|
@@ -25,6 +25,7 @@ describe("CloudFormation round-trip", () => {
|
|
|
25
25
|
|
|
26
26
|
expect(files[0].content).toContain("Bucket");
|
|
27
27
|
expect(files[0].content).toContain('bucketName: "my-bucket"');
|
|
28
|
+
expect(files[0].content).toContain("export const MyBucket");
|
|
28
29
|
});
|
|
29
30
|
|
|
30
31
|
test("round-trips template with parameters", () => {
|
|
@@ -51,8 +52,8 @@ describe("CloudFormation round-trip", () => {
|
|
|
51
52
|
const files = generator.generate(ir);
|
|
52
53
|
|
|
53
54
|
expect(files[0].content).toContain("Parameter");
|
|
54
|
-
expect(files[0].content).toContain("
|
|
55
|
-
expect(files[0].content).toContain("bucketName:
|
|
55
|
+
expect(files[0].content).toContain("Environment");
|
|
56
|
+
expect(files[0].content).toContain("bucketName: Ref(Environment)");
|
|
56
57
|
});
|
|
57
58
|
|
|
58
59
|
test("round-trips template with Fn::Sub", () => {
|
|
@@ -114,7 +115,7 @@ describe("CloudFormation round-trip", () => {
|
|
|
114
115
|
|
|
115
116
|
expect(files[0].content).toContain("Role");
|
|
116
117
|
expect(files[0].content).toContain("Function");
|
|
117
|
-
expect(files[0].content).toContain("
|
|
118
|
+
expect(files[0].content).toContain("LambdaRole.arn");
|
|
118
119
|
});
|
|
119
120
|
|
|
120
121
|
test("round-trips complex nested properties", () => {
|
|
@@ -147,8 +148,8 @@ describe("CloudFormation round-trip", () => {
|
|
|
147
148
|
const files = generator.generate(ir);
|
|
148
149
|
|
|
149
150
|
expect(files[0].content).toContain("Function");
|
|
150
|
-
expect(files[0].content).toContain("environment");
|
|
151
|
-
expect(files[0].content).toContain("vpcConfig");
|
|
151
|
+
expect(files[0].content).toContain("environment:");
|
|
152
|
+
expect(files[0].content).toContain("vpcConfig:");
|
|
152
153
|
});
|
|
153
154
|
|
|
154
155
|
test("round-trips empty template", () => {
|
package/src/index.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
// Parameter
|
|
2
|
+
export { Parameter } from "./parameter";
|
|
3
|
+
|
|
1
4
|
// Serializer
|
|
2
5
|
export { awsSerializer } from "./serializer";
|
|
3
6
|
|
|
@@ -5,11 +8,13 @@ export { awsSerializer } from "./serializer";
|
|
|
5
8
|
export { nestedStack, isNestedStackInstance, NestedStackOutputRef, isNestedStackOutputRef, NESTED_STACK_MARKER } from "./nested-stack";
|
|
6
9
|
export type { NestedStackOptions, NestedStackInstance } from "./nested-stack";
|
|
7
10
|
|
|
8
|
-
// Re-export core child project and
|
|
11
|
+
// Re-export core child project, stack output, and lexicon output primitives
|
|
9
12
|
export { isChildProject, CHILD_PROJECT_MARKER } from "@intentius/chant/child-project";
|
|
10
13
|
export type { ChildProjectInstance } from "@intentius/chant/child-project";
|
|
11
14
|
export { stackOutput, isStackOutput, STACK_OUTPUT_MARKER } from "@intentius/chant/stack-output";
|
|
12
15
|
export type { StackOutput } from "@intentius/chant/stack-output";
|
|
16
|
+
export { output, isLexiconOutput } from "@intentius/chant/lexicon-output";
|
|
17
|
+
export type { LexiconOutput } from "@intentius/chant/lexicon-output";
|
|
13
18
|
|
|
14
19
|
// Plugin
|
|
15
20
|
export { awsPlugin } from "./plugin";
|
|
@@ -24,6 +29,7 @@ export {
|
|
|
24
29
|
Select,
|
|
25
30
|
Split,
|
|
26
31
|
Base64,
|
|
32
|
+
GetAZs,
|
|
27
33
|
SubIntrinsic,
|
|
28
34
|
RefIntrinsic,
|
|
29
35
|
GetAttIntrinsic,
|
|
@@ -32,6 +38,7 @@ export {
|
|
|
32
38
|
SelectIntrinsic,
|
|
33
39
|
SplitIntrinsic,
|
|
34
40
|
Base64Intrinsic,
|
|
41
|
+
GetAZsIntrinsic,
|
|
35
42
|
} from "./intrinsics";
|
|
36
43
|
|
|
37
44
|
// Pseudo-parameters
|
package/src/intrinsics.ts
CHANGED
|
@@ -221,3 +221,30 @@ export class Base64Intrinsic implements Intrinsic {
|
|
|
221
221
|
export function Base64(value: string | Intrinsic): Base64Intrinsic {
|
|
222
222
|
return new Base64Intrinsic(value);
|
|
223
223
|
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Fn::GetAZs intrinsic function
|
|
227
|
+
* Returns a list of Availability Zones for a region
|
|
228
|
+
*/
|
|
229
|
+
export class GetAZsIntrinsic implements Intrinsic {
|
|
230
|
+
readonly [INTRINSIC_MARKER] = true as const;
|
|
231
|
+
private region: string | Intrinsic;
|
|
232
|
+
|
|
233
|
+
constructor(region: string | Intrinsic = "") {
|
|
234
|
+
this.region = region;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
toJSON(): { "Fn::GetAZs": unknown } {
|
|
238
|
+
const regionValue = typeof this.region === "string"
|
|
239
|
+
? this.region
|
|
240
|
+
: (this.region as Intrinsic & { toJSON(): unknown }).toJSON();
|
|
241
|
+
return { "Fn::GetAZs": regionValue };
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Create a GetAZs intrinsic
|
|
247
|
+
*/
|
|
248
|
+
export function GetAZs(region?: string | Intrinsic): GetAZsIntrinsic {
|
|
249
|
+
return new GetAZsIntrinsic(region);
|
|
250
|
+
}
|
package/src/parameter.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { DECLARABLE_MARKER, type CoreParameter } from "@intentius/chant/declarable";
|
|
2
|
+
|
|
3
|
+
export class Parameter implements CoreParameter {
|
|
4
|
+
readonly [DECLARABLE_MARKER] = true as const;
|
|
5
|
+
readonly lexicon = "aws";
|
|
6
|
+
readonly entityType = "AWS::CloudFormation::Parameter";
|
|
7
|
+
readonly parameterType: string;
|
|
8
|
+
readonly description?: string;
|
|
9
|
+
readonly defaultValue?: unknown;
|
|
10
|
+
|
|
11
|
+
constructor(type: string, options?: { description?: string; defaultValue?: unknown }) {
|
|
12
|
+
this.parameterType = type;
|
|
13
|
+
this.description = options?.description;
|
|
14
|
+
this.defaultValue = options?.defaultValue;
|
|
15
|
+
}
|
|
16
|
+
}
|
package/src/plugin.test.ts
CHANGED
|
@@ -36,7 +36,7 @@ describe("awsPlugin", () => {
|
|
|
36
36
|
|
|
37
37
|
test("returns intrinsics", () => {
|
|
38
38
|
const intrinsics = awsPlugin.intrinsics!();
|
|
39
|
-
expect(intrinsics.length).toBe(
|
|
39
|
+
expect(intrinsics.length).toBe(9);
|
|
40
40
|
const names = intrinsics.map((i) => i.name);
|
|
41
41
|
expect(names).toContain("Sub");
|
|
42
42
|
expect(names).toContain("Ref");
|
package/src/plugin.ts
CHANGED
|
@@ -35,6 +35,7 @@ export const awsPlugin: LexiconPlugin = {
|
|
|
35
35
|
{ name: "Select", description: "Fn::Select — select value by index" },
|
|
36
36
|
{ name: "Split", description: "Fn::Split — split string by delimiter" },
|
|
37
37
|
{ name: "Base64", description: "Fn::Base64 — encode to Base64" },
|
|
38
|
+
{ name: "GetAZs", description: "Fn::GetAZs — list Availability Zones" },
|
|
38
39
|
];
|
|
39
40
|
},
|
|
40
41
|
|
|
@@ -53,7 +54,6 @@ export const awsPlugin: LexiconPlugin = {
|
|
|
53
54
|
|
|
54
55
|
initTemplates(): Record<string, string> {
|
|
55
56
|
return {
|
|
56
|
-
"_.ts": `export * from "./config";\n`,
|
|
57
57
|
"config.ts": `/**
|
|
58
58
|
* Shared bucket configuration — encryption, versioning, public access
|
|
59
59
|
*/
|
|
@@ -92,29 +92,29 @@ export const versioningEnabled = new aws.VersioningConfiguration({
|
|
|
92
92
|
* Data bucket — primary storage with encryption and versioning
|
|
93
93
|
*/
|
|
94
94
|
|
|
95
|
-
import
|
|
96
|
-
import
|
|
95
|
+
import { Bucket, Sub, AWS } from "@intentius/chant-lexicon-aws";
|
|
96
|
+
import { versioningEnabled, bucketEncryption, publicAccessBlock } from "./config";
|
|
97
97
|
|
|
98
|
-
export const dataBucket = new
|
|
99
|
-
bucketName:
|
|
100
|
-
versioningConfiguration:
|
|
101
|
-
bucketEncryption:
|
|
102
|
-
publicAccessBlockConfiguration:
|
|
98
|
+
export const dataBucket = new Bucket({
|
|
99
|
+
bucketName: Sub\`\${AWS.StackName}-data\`,
|
|
100
|
+
versioningConfiguration: versioningEnabled,
|
|
101
|
+
bucketEncryption: bucketEncryption,
|
|
102
|
+
publicAccessBlockConfiguration: publicAccessBlock,
|
|
103
103
|
});
|
|
104
104
|
`,
|
|
105
105
|
"logs-bucket.ts": `/**
|
|
106
106
|
* Logs bucket — log delivery with encryption and versioning
|
|
107
107
|
*/
|
|
108
108
|
|
|
109
|
-
import
|
|
110
|
-
import
|
|
109
|
+
import { Bucket, Sub, AWS } from "@intentius/chant-lexicon-aws";
|
|
110
|
+
import { versioningEnabled, bucketEncryption, publicAccessBlock } from "./config";
|
|
111
111
|
|
|
112
|
-
export const logsBucket = new
|
|
113
|
-
bucketName:
|
|
112
|
+
export const logsBucket = new Bucket({
|
|
113
|
+
bucketName: Sub\`\${AWS.StackName}-logs\`,
|
|
114
114
|
accessControl: "LogDeliveryWrite",
|
|
115
|
-
versioningConfiguration:
|
|
116
|
-
bucketEncryption:
|
|
117
|
-
publicAccessBlockConfiguration:
|
|
115
|
+
versioningConfiguration: versioningEnabled,
|
|
116
|
+
bucketEncryption: bucketEncryption,
|
|
117
|
+
publicAccessBlockConfiguration: publicAccessBlock,
|
|
118
118
|
});
|
|
119
119
|
`,
|
|
120
120
|
};
|
|
@@ -184,18 +184,9 @@ export const logsBucket = new aws.Bucket({
|
|
|
184
184
|
|
|
185
185
|
async validate(options?: { verbose?: boolean }): Promise<void> {
|
|
186
186
|
const { validate } = await import("./validate");
|
|
187
|
+
const { printValidationResult } = await import("@intentius/chant/codegen/validate");
|
|
187
188
|
const result = await validate();
|
|
188
|
-
|
|
189
|
-
for (const check of result.checks) {
|
|
190
|
-
const status = check.ok ? "OK" : "FAIL";
|
|
191
|
-
const msg = check.error ? ` — ${check.error}` : "";
|
|
192
|
-
console.error(` [${status}] ${check.name}${msg}`);
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
if (!result.success) {
|
|
196
|
-
throw new Error("Validation failed");
|
|
197
|
-
}
|
|
198
|
-
console.error("All validation checks passed.");
|
|
189
|
+
printValidationResult(result);
|
|
199
190
|
},
|
|
200
191
|
|
|
201
192
|
async coverage(options?: { verbose?: boolean; minOverall?: number }): Promise<void> {
|
|
@@ -226,34 +217,15 @@ export const logsBucket = new aws.Bucket({
|
|
|
226
217
|
|
|
227
218
|
async package(options?: { verbose?: boolean; force?: boolean }): Promise<void> {
|
|
228
219
|
const { packageLexicon } = await import("./codegen/package");
|
|
229
|
-
const {
|
|
220
|
+
const { writeBundleSpec } = await import("@intentius/chant/codegen/package");
|
|
230
221
|
const { join, dirname } = await import("path");
|
|
231
222
|
const { fileURLToPath } = await import("url");
|
|
232
223
|
|
|
233
224
|
const { spec, stats } = await packageLexicon({ verbose: options?.verbose, force: options?.force });
|
|
234
225
|
|
|
235
|
-
// Write manifest and artifacts to dist/
|
|
236
226
|
const pkgDir = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
237
227
|
const distDir = join(pkgDir, "dist");
|
|
238
|
-
|
|
239
|
-
mkdirSync(join(distDir, "rules"), { recursive: true });
|
|
240
|
-
mkdirSync(join(distDir, "skills"), { recursive: true });
|
|
241
|
-
|
|
242
|
-
writeFileSync(join(distDir, "manifest.json"), JSON.stringify(spec.manifest, null, 2));
|
|
243
|
-
writeFileSync(join(distDir, "meta.json"), spec.registry);
|
|
244
|
-
writeFileSync(join(distDir, "types", "index.d.ts"), spec.typesDTS);
|
|
245
|
-
|
|
246
|
-
for (const [name, content] of spec.rules) {
|
|
247
|
-
writeFileSync(join(distDir, "rules", name), content);
|
|
248
|
-
}
|
|
249
|
-
for (const [name, content] of spec.skills) {
|
|
250
|
-
writeFileSync(join(distDir, "skills", name), content);
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
// Write integrity.json if available
|
|
254
|
-
if (spec.integrity) {
|
|
255
|
-
writeFileSync(join(distDir, "integrity.json"), JSON.stringify(spec.integrity, null, 2));
|
|
256
|
-
}
|
|
228
|
+
writeBundleSpec(spec, distDir);
|
|
257
229
|
|
|
258
230
|
console.error(`Packaged ${stats.resources} resources, ${stats.ruleCount} rules, ${stats.skillCount} skills`);
|
|
259
231
|
|
|
@@ -348,7 +320,7 @@ description: AWS CloudFormation best practices and common patterns
|
|
|
348
320
|
3. **Use least-privilege IAM** — Avoid \`*\` in IAM policy actions and resources
|
|
349
321
|
4. **Enable versioning** — Turn on \`VersioningConfiguration\` for data buckets
|
|
350
322
|
5. **Use Sub for dynamic names** — \`Sub\\\`\\\${AWS::StackName}-suffix\\\`\` for unique naming
|
|
351
|
-
6. **Share config via
|
|
323
|
+
6. **Share config via direct imports** — Put common settings in a config file and import directly
|
|
352
324
|
`,
|
|
353
325
|
triggers: [
|
|
354
326
|
{ type: "file-pattern", value: "**/*.aws.ts" },
|
package/src/serializer.test.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { describe, test, expect } from "bun:test";
|
|
2
2
|
import { awsSerializer } from "./serializer";
|
|
3
3
|
import { AttrRef } from "@intentius/chant/attrref";
|
|
4
|
-
import { DECLARABLE_MARKER, type Declarable
|
|
4
|
+
import { DECLARABLE_MARKER, type Declarable } from "@intentius/chant/declarable";
|
|
5
5
|
import { LexiconOutput } from "@intentius/chant/lexicon-output";
|
|
6
6
|
import { Sub } from "./intrinsics";
|
|
7
7
|
import { AWS } from "./pseudo";
|
|
@@ -10,6 +10,7 @@ import { stackOutput } from "@intentius/chant/stack-output";
|
|
|
10
10
|
import { createResource } from "@intentius/chant/runtime";
|
|
11
11
|
import type { SerializerResult } from "@intentius/chant/serializer";
|
|
12
12
|
import type { BuildResult } from "@intentius/chant/build";
|
|
13
|
+
import { Parameter } from "./parameter";
|
|
13
14
|
|
|
14
15
|
// Mock S3 Bucket for testing
|
|
15
16
|
class MockBucket implements Declarable {
|
|
@@ -25,22 +26,6 @@ class MockBucket implements Declarable {
|
|
|
25
26
|
}
|
|
26
27
|
}
|
|
27
28
|
|
|
28
|
-
// Mock Parameter for testing
|
|
29
|
-
class MockParameter implements CoreParameter {
|
|
30
|
-
readonly [DECLARABLE_MARKER] = true as const;
|
|
31
|
-
readonly lexicon = "aws";
|
|
32
|
-
readonly entityType = "AWS::CloudFormation::Parameter";
|
|
33
|
-
readonly parameterType: string;
|
|
34
|
-
readonly description?: string;
|
|
35
|
-
readonly defaultValue?: unknown;
|
|
36
|
-
|
|
37
|
-
constructor(type: string, options: { description?: string; defaultValue?: unknown } = {}) {
|
|
38
|
-
this.parameterType = type;
|
|
39
|
-
this.description = options.description;
|
|
40
|
-
this.defaultValue = options.defaultValue;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
29
|
describe("awsSerializer", () => {
|
|
45
30
|
test("has correct name", () => {
|
|
46
31
|
expect(awsSerializer.name).toBe("aws");
|
|
@@ -84,7 +69,7 @@ describe("awsSerializer.serialize", () => {
|
|
|
84
69
|
|
|
85
70
|
test("serializes parameters", () => {
|
|
86
71
|
const entities = new Map<string, Declarable>();
|
|
87
|
-
entities.set("Environment", new
|
|
72
|
+
entities.set("Environment", new Parameter("String", {
|
|
88
73
|
description: "Environment name",
|
|
89
74
|
defaultValue: "dev",
|
|
90
75
|
}));
|
|
@@ -139,7 +124,7 @@ describe("awsSerializer.serialize", () => {
|
|
|
139
124
|
|
|
140
125
|
test("handles resources and parameters together", () => {
|
|
141
126
|
const entities = new Map<string, Declarable>();
|
|
142
|
-
entities.set("Env", new
|
|
127
|
+
entities.set("Env", new Parameter("String"));
|
|
143
128
|
entities.set("MyBucket", new MockBucket({ bucketName: "bucket" }));
|
|
144
129
|
|
|
145
130
|
const output = awsSerializer.serialize(entities);
|
package/src/spec/parse.ts
CHANGED
|
@@ -36,7 +36,7 @@ export interface ParsedAttribute {
|
|
|
36
36
|
|
|
37
37
|
export interface ParsedPropertyType {
|
|
38
38
|
name: string;
|
|
39
|
-
|
|
39
|
+
specType: string;
|
|
40
40
|
properties: ParsedProperty[];
|
|
41
41
|
}
|
|
42
42
|
|
|
@@ -130,7 +130,7 @@ export function parseCFNSchema(data: string | Buffer): SchemaParseResult {
|
|
|
130
130
|
}
|
|
131
131
|
propertyTypes.push({
|
|
132
132
|
name: `${shortName}_${defName}`,
|
|
133
|
-
|
|
133
|
+
specType: defName,
|
|
134
134
|
properties: defProps,
|
|
135
135
|
});
|
|
136
136
|
}
|