@aws-cdk/core
Version:
AWS Cloud Development Kit Core Library
769 lines • 108 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.rootPathTo = exports.Stack = void 0;
const fs = require("fs");
const path = require("path");
const cxschema = require("@aws-cdk/cloud-assembly-schema");
const cxapi = require("@aws-cdk/cx-api");
const constructs_1 = require("constructs");
const annotations_1 = require("./annotations");
const app_1 = require("./app");
const arn_1 = require("./arn");
const cfn_element_1 = require("./cfn-element");
const cfn_fn_1 = require("./cfn-fn");
const cfn_pseudo_1 = require("./cfn-pseudo");
const cfn_resource_1 = require("./cfn-resource");
const context_provider_1 = require("./context-provider");
const feature_flags_1 = require("./feature-flags");
const cloudformation_lang_1 = require("./private/cloudformation-lang");
const logical_id_1 = require("./private/logical-id");
const resolve_1 = require("./private/resolve");
const uniqueid_1 = require("./private/uniqueid");
// v2 - keep this import as a separate section to reduce merge conflict when forward merging with the v2 branch.
// eslint-disable-next-line
const construct_compat_1 = require("./construct-compat");
const STACK_SYMBOL = Symbol.for('@aws-cdk/core.Stack');
const MY_STACK_CACHE = Symbol.for('@aws-cdk/core.Stack.myStack');
const VALID_STACK_NAME_REGEX = /^[A-Za-z][A-Za-z0-9-]*$/;
/**
* A root construct which represents a single CloudFormation stack.
*/
class Stack extends construct_compat_1.Construct {
/**
* Creates a new stack.
*
* @param scope Parent of this stack, usually an `App` or a `Stage`, but could be any construct.
* @param id The construct ID of this stack.
* @param props Stack properties.
*/
constructor(scope, id, props = {}) {
var _a, _b;
// For unit test scope and id are optional for stacks, but we still want an App
// as the parent because apps implement much of the synthesis logic.
scope = scope !== null && scope !== void 0 ? scope : new app_1.App({
autoSynth: false,
outdir: fs_1.FileSystem.mkdtemp('cdk-test-app-'),
});
// "Default" is a "hidden id" from a `node.uniqueId` perspective
id = id !== null && id !== void 0 ? id : 'Default';
super(scope, id);
this._missingContext = new Array();
this._stackDependencies = {};
this.templateOptions = {};
Object.defineProperty(this, STACK_SYMBOL, { value: true });
this._logicalIds = new logical_id_1.LogicalIDs();
const { account, region, environment } = this.parseEnvironment(props.env);
this.account = account;
this.region = region;
this.environment = environment;
this.terminationProtection = props.terminationProtection;
if (props.description !== undefined) {
// Max length 1024 bytes
// Typically 2 bytes per character, may be more for more exotic characters
if (props.description.length > 512) {
throw new Error(`Stack description must be <= 1024 bytes. Received description: '${props.description}'`);
}
this.templateOptions.description = props.description;
}
this._stackName = props.stackName !== undefined ? props.stackName : this.generateStackName();
this.tags = new tag_manager_1.TagManager(cfn_resource_1.TagType.KEY_VALUE, 'aws:cdk:stack', props.tags);
if (!VALID_STACK_NAME_REGEX.test(this.stackName)) {
throw new Error(`Stack name must match the regular expression: ${VALID_STACK_NAME_REGEX.toString()}, got '${this.stackName}'`);
}
// the preferred behavior is to generate a unique id for this stack and use
// it as the artifact ID in the assembly. this allows multiple stacks to use
// the same name. however, this behavior is breaking for 1.x so it's only
// applied under a feature flag which is applied automatically for new
// projects created using `cdk init`.
//
// Also use the new behavior if we are using the new CI/CD-ready synthesizer; that way
// people only have to flip one flag.
const featureFlags = feature_flags_1.FeatureFlags.of(this);
const stackNameDupeContext = featureFlags.isEnabled(cxapi.ENABLE_STACK_NAME_DUPLICATES_CONTEXT);
const newStyleSynthesisContext = featureFlags.isEnabled(cxapi.NEW_STYLE_STACK_SYNTHESIS_CONTEXT);
this.artifactId = (stackNameDupeContext || newStyleSynthesisContext)
? this.generateStackArtifactId()
: this.stackName;
this.templateFile = `${this.artifactId}.template.json`;
// Not for nested stacks
this._versionReportingEnabled = ((_a = props.analyticsReporting) !== null && _a !== void 0 ? _a : this.node.tryGetContext(cxapi.ANALYTICS_REPORTING_ENABLED_CONTEXT))
&& !this.nestedStackParent;
this.synthesizer = (_b = props.synthesizer) !== null && _b !== void 0 ? _b : (newStyleSynthesisContext
? new stack_synthesizers_1.DefaultStackSynthesizer()
: new stack_synthesizers_1.LegacyStackSynthesizer());
this.synthesizer.bind(this);
}
/**
* Return whether the given object is a Stack.
*
* We do attribute detection since we can't reliably use 'instanceof'.
*/
static isStack(x) {
return x !== null && typeof (x) === 'object' && STACK_SYMBOL in x;
}
/**
* Looks up the first stack scope in which `construct` is defined.
*
* Fails if there is no stack up the tree.
*
* @param construct The construct to start the search from.
*/
static of(construct) {
// we want this to be as cheap as possible. cache this result by mutating
// the object. anecdotally, at the time of this writing, @aws-cdk/core unit
// tests hit this cache 1,112 times, @aws-cdk/aws-cloudformation unit tests
// hit this 2,435 times).
const cache = construct[MY_STACK_CACHE];
if (cache) {
return cache;
}
else {
const value = _lookup(construct);
Object.defineProperty(construct, MY_STACK_CACHE, {
enumerable: false,
writable: false,
configurable: false,
value,
});
return value;
}
function _lookup(c) {
var _a, _b;
if (Stack.isStack(c)) {
return c;
}
const _scope = constructs_1.Node.of(c).scope;
if (stage_1.Stage.isStage(c) || !_scope) {
throw new Error(`${(_b = (_a = construct.constructor) === null || _a === void 0 ? void 0 : _a.name) !== null && _b !== void 0 ? _b : 'Construct'} at '${constructs_1.Node.of(construct).path}' should be created in the scope of a Stack, but no Stack found`);
}
return _lookup(_scope);
}
}
/**
* Resolve a tokenized value in the context of the current stack.
*/
resolve(obj) {
return resolve_1.resolve(obj, {
scope: this,
prefix: [],
resolver: cloudformation_lang_1.CLOUDFORMATION_TOKEN_RESOLVER,
preparing: false,
});
}
/**
* Convert an object, potentially containing tokens, to a JSON string.
*/
toJsonString(obj, space) {
return cloudformation_lang_1.CloudFormationLang.toJSON(obj, space).toString();
}
/**
* Indicate that a context key was expected.
*
* Contains instructions which will be emitted into the cloud assembly on how
* the key should be supplied.
*
* @param report The set of parameters needed to obtain the context.
*/
reportMissingContext(report) {
if (!Object.values(cxschema.ContextProvider).includes(report.provider)) {
throw new Error(`Unknown context provider requested in: ${JSON.stringify(report)}`);
}
this._missingContext.push(report);
}
/**
* Rename a generated logical identities.
*
* To modify the naming scheme strategy, extend the `Stack` class and
* override the `allocateLogicalId` method.
*/
renameLogicalId(oldId, newId) {
this._logicalIds.addRename(oldId, newId);
}
/**
* Allocates a stack-unique CloudFormation-compatible logical identity for a specific resource.
*
* This method is called when a `CfnElement` is created and used to render the
* initial logical identity of resources. Logical ID renames are applied at
* this stage.
*
* This method uses the protected method `allocateLogicalId` to render the
* logical ID for an element. To modify the naming scheme, extend the `Stack`
* class and override this method.
*
* @param element The CloudFormation element for which a logical identity is needed.
*/
getLogicalId(element) {
const logicalId = this.allocateLogicalId(element);
return this._logicalIds.applyRename(logicalId);
}
/**
* Add a dependency between this stack and another stack.
*
* This can be used to define dependencies between any two stacks within an
* app, and also supports nested stacks.
*/
addDependency(target, reason) {
deps_1.addDependency(this, target, reason);
}
/**
* Return the stacks this stack depends on.
*/
get dependencies() {
return Object.values(this._stackDependencies).map(x => x.stack);
}
/**
* The concrete CloudFormation physical stack name.
*
* This is either the name defined explicitly in the `stackName` prop or
* allocated based on the stack's location in the construct tree. Stacks that
* are directly defined under the app use their construct `id` as their stack
* name. Stacks that are defined deeper within the tree will use a hashed naming
* scheme based on the construct path to ensure uniqueness.
*
* If you wish to obtain the deploy-time AWS::StackName intrinsic,
* you can use `Aws.stackName` directly.
*/
get stackName() {
return this._stackName;
}
/**
* The partition in which this stack is defined.
*/
get partition() {
// Always return a non-scoped partition intrinsic. These will usually
// be used to construct an ARN, but there are no cross-partition
// calls anyway.
return cfn_pseudo_1.Aws.PARTITION;
}
/**
* The Amazon domain suffix for the region in which this stack is defined.
*/
get urlSuffix() {
// Since URL Suffix always follows partition, it is unscoped like partition is.
return cfn_pseudo_1.Aws.URL_SUFFIX;
}
/**
* The ID of the stack.
*
* @example
*
* After resolving, looks like arn:aws:cloudformation:us-west-2:123456789012:stack/teststack/51af3dc0-da77-11e4-872e-1234567db123
*/
get stackId() {
return new cfn_pseudo_1.ScopedAws(this).stackId;
}
/**
* Returns the list of notification Amazon Resource Names (ARNs) for the current stack.
*/
get notificationArns() {
return new cfn_pseudo_1.ScopedAws(this).notificationArns;
}
/**
* Indicates if this is a nested stack, in which case `parentStack` will include a reference to it's parent.
*/
get nested() {
return this.nestedStackResource !== undefined;
}
/**
* Creates an ARN from components.
*
* If `partition`, `region` or `account` are not specified, the stack's
* partition, region and account will be used.
*
* If any component is the empty string, an empty string will be inserted
* into the generated ARN at the location that component corresponds to.
*
* The ARN will be formatted as follows:
*
* arn:{partition}:{service}:{region}:{account}:{resource}{sep}}{resource-name}
*
* The required ARN pieces that are omitted will be taken from the stack that
* the 'scope' is attached to. If all ARN pieces are supplied, the supplied scope
* can be 'undefined'.
*/
formatArn(components) {
return arn_1.Arn.format(components, this);
}
/**
* Given an ARN, parses it and returns components.
*
* If the ARN is a concrete string, it will be parsed and validated. The
* separator (`sep`) will be set to '/' if the 6th component includes a '/',
* in which case, `resource` will be set to the value before the '/' and
* `resourceName` will be the rest. In case there is no '/', `resource` will
* be set to the 6th components and `resourceName` will be set to the rest
* of the string.
*
* If the ARN includes tokens (or is a token), the ARN cannot be validated,
* since we don't have the actual value yet at the time of this function
* call. You will have to know the separator and the type of ARN. The
* resulting `ArnComponents` object will contain tokens for the
* subexpressions of the ARN, not string literals. In this case this
* function cannot properly parse the complete final resourceName (path) out
* of ARNs that use '/' to both separate the 'resource' from the
* 'resourceName' AND to subdivide the resourceName further. For example, in
* S3 ARNs:
*
* arn:aws:s3:::my_corporate_bucket/path/to/exampleobject.png
*
* After parsing the resourceName will not contain
* 'path/to/exampleobject.png' but simply 'path'. This is a limitation
* because there is no slicing functionality in CloudFormation templates.
*
* @param arn The ARN string to parse.
* @param sepIfToken The separator used to separate resource from resourceName.
* @param hasName Whether there is a name component in the ARN at all.
* @returns an ArnComponents object which allows access to the various
* components of the ARN.
*/
parseArn(arn, sepIfToken = '/', hasName = true) {
return arn_1.Arn.parse(arn, sepIfToken, hasName);
}
/**
* Returns the list of AZs that are available in the AWS environment (account/region) associated with this stack.
*
* If the stack is environment-agnostic (either account and/or region are
* tokens), this property will return an array with 2 tokens that will resolve
* at deploy-time to the first two availability zones returned from CloudFormation's
* `Fn::GetAZs` intrinsic function.
*
* If they are not available in the context, returns a set of dummy values and
* reports them as missing, and let the CLI resolve them by calling EC2
* `DescribeAvailabilityZones` on the target environment.
*
* To specify a different strategy for selecting availability zones override this method.
*/
get availabilityZones() {
// if account/region are tokens, we can't obtain AZs through the context
// provider, so we fallback to use Fn::GetAZs. the current lowest common
// denominator is 2 AZs across all AWS regions.
const agnostic = token_1.Token.isUnresolved(this.account) || token_1.Token.isUnresolved(this.region);
if (agnostic) {
return this.node.tryGetContext(cxapi.AVAILABILITY_ZONE_FALLBACK_CONTEXT_KEY) || [
cfn_fn_1.Fn.select(0, cfn_fn_1.Fn.getAzs()),
cfn_fn_1.Fn.select(1, cfn_fn_1.Fn.getAzs()),
];
}
const value = context_provider_1.ContextProvider.getValue(this, {
provider: cxschema.ContextProvider.AVAILABILITY_ZONE_PROVIDER,
dummyValue: ['dummy1a', 'dummy1b', 'dummy1c'],
}).value;
if (!Array.isArray(value)) {
throw new Error(`Provider ${cxschema.ContextProvider.AVAILABILITY_ZONE_PROVIDER} expects a list`);
}
return value;
}
/**
* (deprecated) Register a file asset on this Stack.
*
* @deprecated Use `stack.synthesizer.addFileAsset()` if you are calling,
* and a different IStackSynthesizer class if you are implementing.
*/
addFileAsset(asset) {
return this.synthesizer.addFileAsset(asset);
}
/**
* (deprecated) Register a docker image asset on this Stack.
*
* @deprecated Use `stack.synthesizer.addDockerImageAsset()` if you are calling,
* and a different `IStackSynthesizer` class if you are implementing.
*/
addDockerImageAsset(asset) {
return this.synthesizer.addDockerImageAsset(asset);
}
/**
* If this is a nested stack, returns it's parent stack.
*/
get nestedStackParent() {
return this.nestedStackResource && Stack.of(this.nestedStackResource);
}
/**
* (deprecated) Returns the parent of a nested stack.
*
* @deprecated use `nestedStackParent`
*/
get parentStack() {
return this.nestedStackParent;
}
/**
* Add a Transform to this stack. A Transform is a macro that AWS CloudFormation uses to process your template.
*
* Duplicate values are removed when stack is synthesized.
*
* @param transform The transform to add.
* @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/transform-section-structure.html
* @example
*
* addTransform('AWS::Serverless-2016-10-31')
*/
addTransform(transform) {
if (!this.templateOptions.transforms) {
this.templateOptions.transforms = [];
}
this.templateOptions.transforms.push(transform);
}
/**
* Called implicitly by the `addDependency` helper function in order to
* realize a dependency between two top-level stacks at the assembly level.
*
* Use `stack.addDependency` to define the dependency between any two stacks,
* and take into account nested stack relationships.
*
* @internal
*/
_addAssemblyDependency(target, reason) {
// defensive: we should never get here for nested stacks
if (this.nested || target.nested) {
throw new Error('Cannot add assembly-level dependencies for nested stacks');
}
reason = reason || 'dependency added using stack.addDependency()';
const cycle = target.stackDependencyReasons(this);
if (cycle !== undefined) {
// eslint-disable-next-line max-len
throw new Error(`'${target.node.path}' depends on '${this.node.path}' (${cycle.join(', ')}). Adding this dependency (${reason}) would create a cyclic reference.`);
}
let dep = this._stackDependencies[target.node.uniqueId];
if (!dep) {
dep = this._stackDependencies[target.node.uniqueId] = {
stack: target,
reasons: [],
};
}
dep.reasons.push(reason);
if (process.env.CDK_DEBUG_DEPS) {
// eslint-disable-next-line no-console
console.error(`[CDK_DEBUG_DEPS] stack "${this.node.path}" depends on "${target.node.path}" because: ${reason}`);
}
}
/**
* Synthesizes the cloudformation template into a cloud assembly.
* @internal
*/
_synthesizeTemplate(session) {
// In principle, stack synthesis is delegated to the
// StackSynthesis object.
//
// However, some parts of synthesis currently use some private
// methods on Stack, and I don't really see the value in refactoring
// this right now, so some parts still happen here.
const builder = session.assembly;
const template = this._toCloudFormation();
// write the CloudFormation template as a JSON file
const outPath = path.join(builder.outdir, this.templateFile);
fs.writeFileSync(outPath, JSON.stringify(template, undefined, 2));
for (const ctx of this._missingContext) {
builder.addMissing(ctx);
}
}
/**
* Returns the naming scheme used to allocate logical IDs.
*
* By default, uses
* the `HashedAddressingScheme` but this method can be overridden to customize
* this behavior.
*
* In order to make sure logical IDs are unique and stable, we hash the resource
* construct tree path (i.e. toplevel/secondlevel/.../myresource) and add it as
* a suffix to the path components joined without a separator (CloudFormation
* IDs only allow alphanumeric characters).
*
* The result will be:
*
* <path.join('')><md5(path.join('/')>
* "human" "hash"
*
* If the "human" part of the ID exceeds 240 characters, we simply trim it so
* the total ID doesn't exceed CloudFormation's 255 character limit.
*
* We only take 8 characters from the md5 hash (0.000005 chance of collision).
*
* Special cases:
*
* - If the path only contains a single component (i.e. it's a top-level
* resource), we won't add the hash to it. The hash is not needed for
* disamiguation and also, it allows for a more straightforward migration an
* existing CloudFormation template to a CDK stack without logical ID changes
* (or renames).
* - For aesthetic reasons, if the last components of the path are the same
* (i.e. `L1/L2/Pipeline/Pipeline`), they will be de-duplicated to make the
* resulting human portion of the ID more pleasing: `L1L2Pipeline<HASH>`
* instead of `L1L2PipelinePipeline<HASH>`
* - If a component is named "Default" it will be omitted from the path. This
* allows refactoring higher level abstractions around constructs without affecting
* the IDs of already deployed resources.
* - If a component is named "Resource" it will be omitted from the user-visible
* path, but included in the hash. This reduces visual noise in the human readable
* part of the identifier.
*
* @param cfnElement The element for which the logical ID is allocated.
*/
allocateLogicalId(cfnElement) {
const scopes = cfnElement.node.scopes;
const stackIndex = scopes.indexOf(cfnElement.stack);
const pathComponents = scopes.slice(stackIndex + 1).map(x => x.node.id);
return uniqueid_1.makeUniqueId(pathComponents);
}
/**
* Validate stack name
*
* CloudFormation stack names can include dashes in addition to the regular identifier
* character classes, and we don't allow one of the magic markers.
*
* @internal
*/
_validateId(name) {
if (name && !VALID_STACK_NAME_REGEX.test(name)) {
throw new Error(`Stack name must match the regular expression: ${VALID_STACK_NAME_REGEX.toString()}, got '${name}'`);
}
}
/**
* Returns the CloudFormation template for this stack by traversing
* the tree and invoking _toCloudFormation() on all Entity objects.
*
* @internal
*/
_toCloudFormation() {
let transform;
if (this.templateOptions.transform) {
// eslint-disable-next-line max-len
annotations_1.Annotations.of(this).addWarning('This stack is using the deprecated `templateOptions.transform` property. Consider switching to `addTransform()`.');
this.addTransform(this.templateOptions.transform);
}
if (this.templateOptions.transforms) {
if (this.templateOptions.transforms.length === 1) { // Extract single value
transform = this.templateOptions.transforms[0];
}
else { // Remove duplicate values
transform = Array.from(new Set(this.templateOptions.transforms));
}
}
const template = {
Description: this.templateOptions.description,
Transform: transform,
AWSTemplateFormatVersion: this.templateOptions.templateFormatVersion,
Metadata: this.templateOptions.metadata,
};
const elements = cfnElements(this);
const fragments = elements.map(e => this.resolve(e._toCloudFormation()));
// merge in all CloudFormation fragments collected from the tree
for (const fragment of fragments) {
merge(template, fragment);
}
// resolve all tokens and remove all empties
const ret = this.resolve(template) || {};
this._logicalIds.assertAllRenamesApplied();
return ret;
}
/**
* (deprecated) Deprecated.
*
* @returns reference itself without any change
* @see https://github.com/aws/aws-cdk/pull/7187
* @deprecated cross reference handling has been moved to `App.prepare()`.
*/
prepareCrossReference(_sourceStack, reference) {
return reference;
}
/**
* Determine the various stack environment attributes.
*
*/
parseEnvironment(env = {}) {
var _a, _b, _c, _d;
// if an environment property is explicitly specified when the stack is
// created, it will be used. if not, use tokens for account and region.
//
// (They do not need to be anchored to any construct like resource attributes
// are, because we'll never Export/Fn::ImportValue them -- the only situation
// in which Export/Fn::ImportValue would work is if the value are the same
// between producer and consumer anyway, so we can just assume that they are).
const containingAssembly = stage_1.Stage.of(this);
const account = (_b = (_a = env.account) !== null && _a !== void 0 ? _a : containingAssembly === null || containingAssembly === void 0 ? void 0 : containingAssembly.account) !== null && _b !== void 0 ? _b : cfn_pseudo_1.Aws.ACCOUNT_ID;
const region = (_d = (_c = env.region) !== null && _c !== void 0 ? _c : containingAssembly === null || containingAssembly === void 0 ? void 0 : containingAssembly.region) !== null && _d !== void 0 ? _d : cfn_pseudo_1.Aws.REGION;
// this is the "aws://" env specification that will be written to the cloud assembly
// manifest. it will use "unknown-account" and "unknown-region" to indicate
// environment-agnosticness.
const envAccount = !token_1.Token.isUnresolved(account) ? account : cxapi.UNKNOWN_ACCOUNT;
const envRegion = !token_1.Token.isUnresolved(region) ? region : cxapi.UNKNOWN_REGION;
return {
account,
region,
environment: cxapi.EnvironmentUtils.format(envAccount, envRegion),
};
}
/**
* Check whether this stack has a (transitive) dependency on another stack
*
* Returns the list of reasons on the dependency path, or undefined
* if there is no dependency.
*/
stackDependencyReasons(other) {
if (this === other) {
return [];
}
for (const dep of Object.values(this._stackDependencies)) {
const ret = dep.stack.stackDependencyReasons(other);
if (ret !== undefined) {
return [...dep.reasons, ...ret];
}
}
return undefined;
}
/**
* Calculate the stack name based on the construct path
*
* The stack name is the name under which we'll deploy the stack,
* and incorporates containing Stage names by default.
*
* Generally this looks a lot like how logical IDs are calculated.
* The stack name is calculated based on the construct root path,
* as follows:
*
* - Path is calculated with respect to containing App or Stage (if any)
* - If the path is one component long just use that component, otherwise
* combine them with a hash.
*
* Since the hash is quite ugly and we'd like to avoid it if possible -- but
* we can't anymore in the general case since it has been written into legacy
* stacks. The introduction of Stages makes it possible to make this nicer however.
* When a Stack is nested inside a Stage, we use the path components below the
* Stage, and prefix the path components of the Stage before it.
*/
generateStackName() {
const assembly = stage_1.Stage.of(this);
const prefix = (assembly && assembly.stageName) ? `${assembly.stageName}-` : '';
return `${prefix}${this.generateStackId(assembly)}`;
}
/**
* The artifact ID for this stack
*
* Stack artifact ID is unique within the App's Cloud Assembly.
*/
generateStackArtifactId() {
return this.generateStackId(this.node.root);
}
/**
* Generate an ID with respect to the given container construct.
*/
generateStackId(container) {
const rootPath = rootPathTo(this, container);
const ids = rootPath.map(c => constructs_1.Node.of(c).id);
// In unit tests our Stack (which is the only component) may not have an
// id, so in that case just pretend it's "Stack".
if (ids.length === 1 && !ids[0]) {
throw new Error('unexpected: stack id must always be defined');
}
return makeStackName(ids);
}
}
exports.Stack = Stack;
function merge(template, fragment) {
for (const section of Object.keys(fragment)) {
const src = fragment[section];
// create top-level section if it doesn't exist
const dest = template[section];
if (!dest) {
template[section] = src;
}
else {
template[section] = mergeSection(section, dest, src);
}
}
}
function mergeSection(section, val1, val2) {
switch (section) {
case 'Description':
return `${val1}\n${val2}`;
case 'AWSTemplateFormatVersion':
if (val1 != null && val2 != null && val1 !== val2) {
throw new Error(`Conflicting CloudFormation template versions provided: '${val1}' and '${val2}`);
}
return val1 !== null && val1 !== void 0 ? val1 : val2;
case 'Transform':
return mergeSets(val1, val2);
default:
return mergeObjectsWithoutDuplicates(section, val1, val2);
}
}
function mergeSets(val1, val2) {
const array1 = val1 == null ? [] : (Array.isArray(val1) ? val1 : [val1]);
const array2 = val2 == null ? [] : (Array.isArray(val2) ? val2 : [val2]);
for (const value of array2) {
if (!array1.includes(value)) {
array1.push(value);
}
}
return array1.length === 1 ? array1[0] : array1;
}
function mergeObjectsWithoutDuplicates(section, dest, src) {
if (typeof dest !== 'object') {
throw new Error(`Expecting ${JSON.stringify(dest)} to be an object`);
}
if (typeof src !== 'object') {
throw new Error(`Expecting ${JSON.stringify(src)} to be an object`);
}
// add all entities from source section to destination section
for (const id of Object.keys(src)) {
if (id in dest) {
throw new Error(`section '${section}' already contains '${id}'`);
}
dest[id] = src[id];
}
return dest;
}
/**
* Collect all CfnElements from a Stack.
*
* @param node Root node to collect all CfnElements from
* @param into Array to append CfnElements to
* @returns The same array as is being collected into
*/
function cfnElements(node, into = []) {
if (cfn_element_1.CfnElement.isCfnElement(node)) {
into.push(node);
}
for (const child of constructs_1.Node.of(node).children) {
// Don't recurse into a substack
if (Stack.isStack(child)) {
continue;
}
cfnElements(child, into);
}
return into;
}
/**
* Return the construct root path of the given construct relative to the given ancestor
*
* If no ancestor is given or the ancestor is not found, return the entire root path.
*/
function rootPathTo(construct, ancestor) {
const scopes = constructs_1.Node.of(construct).scopes;
for (let i = scopes.length - 2; i >= 0; i--) {
if (scopes[i] === ancestor) {
return scopes.slice(i + 1);
}
}
return scopes;
}
exports.rootPathTo = rootPathTo;
/**
* makeUniqueId, specialized for Stack names
*
* Stack names may contain '-', so we allow that character if the stack name
* has only one component. Otherwise we fall back to the regular "makeUniqueId"
* behavior.
*/
function makeStackName(components) {
if (components.length === 1) {
return components[0];
}
return uniqueid_1.makeUniqueId(components);
}
// These imports have to be at the end to prevent circular imports
const deps_1 = require("./deps");
const stack_synthesizers_1 = require("./stack-synthesizers");
const stage_1 = require("./stage");
const tag_manager_1 = require("./tag-manager");
const token_1 = require("./token");
const fs_1 = require("./fs");
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3RhY2suanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJzdGFjay50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSx5QkFBeUI7QUFDekIsNkJBQTZCO0FBQzdCLDJEQUEyRDtBQUMzRCx5Q0FBeUM7QUFDekMsMkNBQXlEO0FBQ3pELCtDQUE0QztBQUM1QywrQkFBNEI7QUFDNUIsK0JBQTJDO0FBRTNDLCtDQUEyQztBQUMzQyxxQ0FBOEI7QUFDOUIsNkNBQThDO0FBQzlDLGlEQUFzRDtBQUV0RCx5REFBcUQ7QUFFckQsbURBQStDO0FBQy9DLHVFQUFrRztBQUNsRyxxREFBa0Q7QUFDbEQsK0NBQTRDO0FBQzVDLGlEQUFrRDtBQUVsRCxnSEFBZ0g7QUFDaEgsMkJBQTJCO0FBQzNCLHlEQUFnRTtBQUVoRSxNQUFNLFlBQVksR0FBRyxNQUFNLENBQUMsR0FBRyxDQUFDLHFCQUFxQixDQUFDLENBQUM7QUFDdkQsTUFBTSxjQUFjLEdBQUcsTUFBTSxDQUFDLEdBQUcsQ0FBQyw2QkFBNkIsQ0FBQyxDQUFDO0FBRWpFLE1BQU0sc0JBQXNCLEdBQUcseUJBQXlCLENBQUM7Ozs7QUFxSHpELE1BQWEsS0FBTSxTQUFRLDRCQUFhOzs7Ozs7OztJQTBMdEMsWUFBbUIsS0FBaUIsRUFBRSxFQUFXLEVBQUUsUUFBb0IsRUFBRTs7UUFDdkUsK0VBQStFO1FBQy9FLG9FQUFvRTtRQUNwRSxLQUFLLEdBQUcsS0FBSyxhQUFMLEtBQUssY0FBTCxLQUFLLEdBQUksSUFBSSxTQUFHLENBQUM7WUFDdkIsU0FBUyxFQUFFLEtBQUs7WUFDaEIsTUFBTSxFQUFFLGVBQVUsQ0FBQyxPQUFPLENBQUMsZUFBZSxDQUFDO1NBQzVDLENBQUMsQ0FBQztRQUVILGdFQUFnRTtRQUNoRSxFQUFFLEdBQUcsRUFBRSxhQUFGLEVBQUUsY0FBRixFQUFFLEdBQUksU0FBUyxDQUFDO1FBRXJCLEtBQUssQ0FBQyxLQUFLLEVBQUUsRUFBRSxDQUFDLENBQUM7UUFFakIsSUFBSSxDQUFDLGVB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