Multi-Stack Projects
chant supports splitting a project across multiple stacks in two ways:
- Directory-based partitioning — a core chant feature. Subdirectories under
src/each become a separate stack, with cross-stack references resolved automatically. - Child projects (nested stacks) — a lexicon-specific feature. A parent file references a child project directory explicitly (e.g. AWS
nestedStack()), and the child’sstackOutput()declarations wire the cross-stack references.
Both modes emit separately-deployable output files. Pick based on how you want the pieces to relate at deploy time: partitioning gives you independent stacks with no orchestrator; child projects give you a parent that references and can deploy the children as a unit.
Directory-based partitioning
Section titled “Directory-based partitioning”Organize src/ into subdirectories — each becomes its own stack in dist/. Resources in one stack can reference resources in another through normal TypeScript imports; chant translates those into provider-native cross-stack exports/imports (CloudFormation Export/ImportValue, and so on).
my-project/├── chant.config.ts├── src/│ ├── networking/│ │ ├── vpc.ts│ │ └── subnets.ts│ ├── compute/│ │ ├── api-function.ts│ │ └── worker-function.ts│ └── storage/│ ├── data-table.ts│ └── assets-bucket.ts└── dist/ ├── networking.json ├── compute.json ├── storage.json └── manifest.jsonSee Multi-Stack Output for how chant detects single-stack vs. multi-stack mode and the full set of cross-stack primitives.
Child projects (nested stacks)
Section titled “Child projects (nested stacks)”A child project is a subdirectory that builds to a separately-valid template, referenced explicitly from a parent file. Unlike partitioning, the parent is aware of the child — it uses a lexicon-specific function like nestedStack() and can pass parameters in and read outputs back. Cross-stack values are declared explicitly with stackOutput().
For example, the AWS lexicon’s nestedStack() function references a child project directory and produces separate CloudFormation child templates:
my-project/├── src/│ ├── app.ts ← parent resources + nestedStack() call│ └── network/ ← child project│ ├── vpc.ts│ ├── security.ts│ └── outputs.ts ← stackOutput() declarations└── dist/ ├── template.json ← parent (AWS::CloudFormation::Stack) └── network.template.json ← child (standalone template)/** * Cross-stack outputs — values the parent can reference */
import { stackOutput } from "@intentius/chant";import { vpc, subnet } from "./vpc";import { lambdaSg } from "./security";
export const vpcId = stackOutput(vpc.VpcId, { description: "VPC ID",});export const subnetId = stackOutput(subnet.SubnetId, { description: "Public subnet ID",});export const lambdaSgId = stackOutput(lambdaSg.GroupId, { description: "Lambda security group ID",});/** * App layer — Lambda function in the parent template that references * the network nested stack's outputs via cross-stack references */
import { Function, Sub, AWS, Ref, nestedStack } from "@intentius/chant-lexicon-aws";
// nestedStack() references a child project directoryconst network = nestedStack("network", import.meta.dirname + "/network", { parameters: { Environment: "prod" },});
export const handler = new Function({ FunctionName: Sub`${AWS.StackName}-handler`, Runtime: "nodejs20.x", Handler: "index.handler", Role: Ref("LambdaExecutionRole"), Code: { ZipFile: "exports.handler = async () => ({ statusCode: 200 });" }, VpcConfig: { SubnetIds: [network.outputs.subnetId], SecurityGroupIds: [network.outputs.lambdaSgId], },});
// Re-export so discovery picks it up as an entityexport { network };The child can be built independently (chant build src/network/), and the parent build produces multiple template files. See your lexicon’s documentation for details — for AWS, see the Nested Stacks guide.