import * as path from "path" ; import * as fs from "fs-extra" ; import { Construct , IConstruct } from 'constructs' ; import * as cdk from "aws-cdk-lib" ; import * as lambda from "aws-cdk-lib/aws-lambda" ; import { FunctionProps , Function as Fn } from "./Function" ; import { App } from "./App" ; import { isConstruct } from "./Construct" ; import { Permissions } from "./util/permission" ; export type StackProps = cdk . StackProps ; export class Stack extends cdk . Stack { public readonly stage : string ; public readonly defaultFunctionProps : FunctionProps [ ] ; private readonly metadata : cdk . CfnResource ; constructor ( scope , id , props ? ) { const root = scope . node . root as App ; const stackId = root . logicalPrefixedName ( id ) ; Stack . checkForPropsIsConstruct ( id , props ) ; Stack . checkForEnvInProps ( id , props ) ; super ( scope , stackId , { ... props , env : { account : process . env . CDK_DEFAULT_ACCOUNT , region : root . region , } , } ) ; this . stage = root . stage ; this . defaultFunctionProps = root . defaultFunctionProps . map ( ( dfp ) => typeof dfp === "function" ? dfp ( this ) : dfp ) ; this . metadata = this . createMetadataResource ( ) ; } public setDefaultFunctionProps ( props ) { const fns = this . getAllFunctions ( ) ; if ( fns . length > 0 ) throw new Error ( "Default function props for the stack must be set before any functions have been added. Use 'addDefaultFunctionEnv' or 'addDefaultFunctionPermissions' instead to add more default properties." ) ; this . defaultFunctionProps . push ( props ) ; } public addDefaultFunctionPermissions ( permissions ) { this . defaultFunctionProps . push ( { permissions , } ) ; } public addDefaultFunctionEnv ( environment ) { this . defaultFunctionProps . push ( { environment , } ) ; } public addDefaultFunctionLayers ( layers ) { this . defaultFunctionProps . push ( { layers , } ) ; } public getAllFunctions ( ) { return this . doGetAllFunctions ( this ) ; } private doGetAllFunctions ( construct ) { const results = [ ] ; for ( const child of construct . node . children ) { if ( child instanceof Fn ) results . push ( child ) ; results . push ( ... this . doGetAllFunctions ( child ) ) ; } return results ; } public addOutputs ( outputs ) { Object . keys ( outputs ) . forEach ( ( key ) => { const value = outputs [ key ] ; if ( value === undefined ) { throw new Error ( `The stack output "${ key }" is undefined` ) ; } else if ( typeof value === "string" ) { new cdk . CfnOutput ( this , key , { value } ) ; } else { new cdk . CfnOutput ( this , key , value ) ; } } ) ; } public addConstructsMetadata ( metadata ) { this . metadata . addMetadata ( "sst:constructs" , metadata ) ; } private createMetadataResource ( ) { const res = new cdk . CfnResource ( this , "SSTMetadata" , { type : "AWS::CDK::Metadata" , } ) ; const packageJson = fs . readJsonSync ( path . join ( __dirname , ".." , "package.json" ) ) ; res . addMetadata ( "sst:version" , packageJson . version ) ; return res ; } private static checkForPropsIsConstruct ( id , props ? ) { if ( props && isConstruct ( props ) ) { throw new Error ( `Expected an associative array as the stack props while initializing "${ id }" stack. Received a construct instead.` ) ; } } private static checkForEnvInProps ( id , props ? ) { if ( props && props . env ) { let envS = "" ; try { envS = " (" + JSON . stringify ( props . env ) + ")" ; } catch ( e ) { } throw new Error ( `Do not set the "env" prop while initializing "${ id }" stack${ envS }. Use the "AWS_PROFILE" environment variable and "--region" CLI option instead.` ) ; } } }