Composite Rules
Composite rules check Composite() factory definitions. They keep constants and data transforms out of the factory, the name matching its variable, and prop types taken from the lexicon.
EVL009: No Extractable Constants
Section titled “EVL009: No Extractable Constants”Object literals and arrays-of-objects inside a Composite factory that don’t reference props or sibling members are extractable constants. Move them to a separate file (e.g. defaults.ts) and import directly.
| Severity | Warning |
| Category | Style |
// ❌ Triggers EVL009const LambdaApi = Composite<Props>((props) => { const role = new _.Role({ assumeRolePolicyDocument: { Version: "2012-10-17", Statement: [{ Effect: "Allow", Principal: { Service: "lambda.amazonaws.com" }, Action: "sts:AssumeRole" }], }, }); return { role };}, "LambdaApi");// ✅ Fixed — extract to defaults.ts, import directlyconst LambdaApi = Composite<Props>((props) => { const role = new Role({ assumeRolePolicyDocument: lambdaTrustPolicy, }); return { role };}, "LambdaApi");Simple string, number, and boolean literals are allowed inline. Only object literals and arrays containing objects are flagged.
EVL010: No Data Transformation
Section titled “EVL010: No Data Transformation”Composite factories should build resources from props and leave data transformation to the caller. Call array methods like .map(), .filter(), .reduce(), and .flatMap() before passing props to the composite.
| Severity | Warning |
| Category | Style |
// ❌ Triggers EVL010const LambdaApi = Composite<Props>((props) => { const role = new _.Role({ policies: props.policyStatements.map(s => new _.Role_Policy({ policyDocument: s }) ), }); return { role };}, "LambdaApi");// ✅ Fixed — caller constructs Role_Policy[] before passingconst LambdaApi = Composite<Props>((props) => { const role = new _.Role({ policies: props.policies, }); return { role };}, "LambdaApi");COR017: Composite Name Must Match Variable
Section titled “COR017: Composite Name Must Match Variable”The second argument to Composite() must be a string literal that matches the const variable name. This name is used in resource expansion (e.g. healthApiRole, healthApiFunc) and error messages.
| Severity | Error |
| Category | Correctness |
| Auto-fix | Yes |
// ❌ Triggers COR017 — name mismatchconst LambdaApi = Composite<Props>((props) => { return { role: new _.Role({}) };}, "MyFunction");
// ❌ Triggers COR017 — missing nameconst LambdaApi = Composite<Props>((props) => { return { role: new _.Role({}) };});// ✅ Fixedconst LambdaApi = Composite<Props>((props) => { return { role: new _.Role({}) };}, "LambdaApi");COR018: Prefer Lexicon Property Types
Section titled “COR018: Prefer Lexicon Property Types”Composite prop interfaces should use lexicon property types instead of locally-declared interfaces. If the lexicon already provides a matching type (e.g. Role_Policy), use InstanceType<typeof _.Role_Policy> instead of defining a custom interface.
| Severity | Info |
| Category | Style |
// ❌ Triggers COR018 — local interface duplicates lexicon typeinterface PolicyStatement { effect: string; action: string[]; resource: string[];}
interface LambdaApiProps { policies?: PolicyStatement[];}
const LambdaApi = Composite<LambdaApiProps>((props) => { // ...}, "LambdaApi");// ✅ Fixed — use lexicon property typeinterface LambdaApiProps { policies?: InstanceType<typeof _.Role_Policy>[];}
const LambdaApi = Composite<LambdaApiProps>((props) => { // ...}, "LambdaApi");