@intentius/chant-lexicon-aws 0.19.1 → 0.21.0

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/src/plugin.ts CHANGED
@@ -52,17 +52,36 @@ export const awsPlugin: LexiconPlugin = {
52
52
  return discoverLintRules(rulesDir, import.meta.url);
53
53
  },
54
54
 
55
+ /**
56
+ * chant #1044 — every plain-call intrinsic below carries
57
+ * `foldsAsCall: true`, opting its CALL form into `chant build --fold`.
58
+ *
59
+ * Audited one at a time against the criterion in `IntrinsicDef.foldsAsCall`
60
+ * (core's lexicon.ts): the call must be a pure function of its arguments
61
+ * that builds a deterministic data envelope. Each of these constructs its
62
+ * `*Intrinsic` class and stores the arguments verbatim (../intrinsics.ts) —
63
+ * no I/O, no environment, no module state, and no CloudFormation semantics
64
+ * evaluated locally: `Base64("x")` emits `{"Fn::Base64": "x"}` for the
65
+ * deployment to encode, it does not encode anything here. Calling one while
66
+ * folding is therefore indistinguishable from calling it during a real run
67
+ * of the file.
68
+ *
69
+ * `Sub` is the one intrinsic NOT opted in, and cannot be: it is authored as
70
+ * a tagged template (`isTag: true`), which already folds through
71
+ * `foldTaggedTemplate`. `Sub(...)` as a plain call is not its authoring
72
+ * form, and `chant dev check-lexicon` rejects a registration claiming both.
73
+ */
55
74
  intrinsics(): IntrinsicDef[] {
56
75
  return [
57
- { name: "Sub", description: "Fn::Sub template string interpolation" },
58
- { name: "Ref", description: "Reference a parameter or resource" },
59
- { name: "GetAtt", description: "Fn::GetAtt — get resource attribute" },
60
- { name: "If", description: "Fn::If — conditional value" },
61
- { name: "Join", description: "Fn::Join — join values with delimiter" },
62
- { name: "Select", description: "Fn::Select — select value by index" },
63
- { name: "Split", description: "Fn::Split — split string by delimiter" },
64
- { name: "Base64", description: "Fn::Base64 — encode to Base64" },
65
- { name: "GetAZs", description: "Fn::GetAZs — list Availability Zones" },
76
+ { name: "Sub", description: "Fn::Sub template string interpolation", isTag: true },
77
+ { name: "Ref", description: "Reference a parameter or resource", isTag: false, foldsAsCall: true },
78
+ { name: "GetAtt", description: "Fn::GetAtt — get resource attribute", isTag: false, foldsAsCall: true },
79
+ { name: "If", description: "Fn::If — conditional value", isTag: false, foldsAsCall: true },
80
+ { name: "Join", description: "Fn::Join — join values with delimiter", isTag: false, foldsAsCall: true },
81
+ { name: "Select", description: "Fn::Select — select value by index", isTag: false, foldsAsCall: true },
82
+ { name: "Split", description: "Fn::Split — split string by delimiter", isTag: false, foldsAsCall: true },
83
+ { name: "Base64", description: "Fn::Base64 — encode to Base64", isTag: false, foldsAsCall: true },
84
+ { name: "GetAZs", description: "Fn::GetAZs — list Availability Zones", isTag: false, foldsAsCall: true },
66
85
  ];
67
86
  },
68
87
 
package/src/serializer.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { Declarable, CoreParameter } from "@intentius/chant/declarable";
2
- import { isPropertyDeclarable } from "@intentius/chant/declarable";
2
+ import { isPropertyDeclarable, isResourceDeclarable } from "@intentius/chant/declarable";
3
3
  import type { Serializer, SerializerResult, SerializeContext } from "@intentius/chant/serializer";
4
4
  import { ownershipEntries, type OwnershipMarker } from "@intentius/chant/ownership";
5
5
  import { AWS_TAG_OWNERSHIP_KEYS } from "./ownership";
@@ -82,7 +82,7 @@ function cfnVisitor(entityNames: Map<Declarable, string>): SerializerVisitor {
82
82
  attrRef: (name, attr) => ({ "Fn::GetAtt": [name, attr] }),
83
83
  resourceRef: (name) => ({ Ref: name }),
84
84
  propertyDeclarable: (entity, walk) => {
85
- if (!("props" in entity) || typeof entity.props !== "object" || entity.props === null) {
85
+ if (!isResourceDeclarable(entity) || typeof entity.props !== "object" || entity.props === null) {
86
86
  return undefined;
87
87
  }
88
88
  const props = entity.props as Record<string, unknown>;
@@ -136,7 +136,7 @@ function toProperties(
136
136
  entity: Declarable,
137
137
  entityNames: Map<Declarable, string>
138
138
  ): Record<string, unknown> | undefined {
139
- if (!("props" in entity) || typeof entity.props !== "object" || entity.props === null) {
139
+ if (!isResourceDeclarable(entity) || typeof entity.props !== "object" || entity.props === null) {
140
140
  return undefined;
141
141
  }
142
142
 
@@ -281,7 +281,7 @@ function serializeToTemplate(
281
281
  };
282
282
 
283
283
  // Read resource-level attributes from the second constructor arg
284
- const attrs = ("attributes" in entity && typeof entity.attributes === "object" && entity.attributes !== null)
284
+ const attrs = (isResourceDeclarable(entity) && typeof entity.attributes === "object" && entity.attributes !== null)
285
285
  ? entity.attributes as Record<string, unknown>
286
286
  : undefined;
287
287