diff --git a/packages/@aws-cdk/core/lib/asset-staging.ts b/packages/@aws-cdk/core/lib/asset-staging.ts index ee1f2bafc6a04..502c0c10b0364 100644 --- a/packages/@aws-cdk/core/lib/asset-staging.ts +++ b/packages/@aws-cdk/core/lib/asset-staging.ts @@ -555,7 +555,7 @@ function calculateCacheKey(props: A): string { * Recursively sort object keys */ function sortObject(object: { [key: string]: any }): { [key: string]: any } { - if (typeof object !== 'object' || object instanceof Array) { + if (typeof object !== 'object' || Array.isArray(object)) { return object; } const ret: { [key: string]: any } = {}; diff --git a/packages/@aws-cdk/core/lib/private/cloudformation-lang.ts b/packages/@aws-cdk/core/lib/private/cloudformation-lang.ts index 82d2809255806..8c8321d7ebfa0 100644 --- a/packages/@aws-cdk/core/lib/private/cloudformation-lang.ts +++ b/packages/@aws-cdk/core/lib/private/cloudformation-lang.ts @@ -167,6 +167,7 @@ function tokenAwareStringify(root: any, space: number, ctx: IResolveContext) { if (Array.isArray(obj)) { return renderCollection('[', ']', obj, recurse); } + // eslint-disable-next-line no-instanceof/no-instanceof if (typeof obj === 'object' && obj != null && !(obj instanceof Date)) { // Treat as an intrinsic if this LOOKS like a CFN intrinsic (`{ Ref: ... }`) // AND it's the result of a token resolution. Otherwise, we just treat this diff --git a/packages/@aws-cdk/core/lib/private/resolve.ts b/packages/@aws-cdk/core/lib/private/resolve.ts index e809763b72e64..15ac3a9e09347 100644 --- a/packages/@aws-cdk/core/lib/private/resolve.ts +++ b/packages/@aws-cdk/core/lib/private/resolve.ts @@ -184,6 +184,7 @@ export function resolve(obj: any, options: IResolveOptions): any { // primitives - as-is // + // eslint-disable-next-line no-instanceof/no-instanceof if (typeof(obj) !== 'object' || obj instanceof Date) { return obj; } diff --git a/packages/@aws-cdk/core/lib/private/synthesis.ts b/packages/@aws-cdk/core/lib/private/synthesis.ts index 52738c1cf9cca..2515b26544271 100644 --- a/packages/@aws-cdk/core/lib/private/synthesis.ts +++ b/packages/@aws-cdk/core/lib/private/synthesis.ts @@ -182,7 +182,7 @@ function synthesizeTree(root: IConstruct, builder: cxapi.CloudAssemblyBuilder, v if (Stack.isStack(construct)) { construct.synthesizer.synthesize(session); - } else if (construct instanceof TreeMetadata) { + } else if (TreeMetadata.isTreeMetadata(construct)) { construct._synthesizeTree(session); } else { const custom = getCustomSynthesis(construct); diff --git a/packages/@aws-cdk/core/lib/private/tree-metadata.ts b/packages/@aws-cdk/core/lib/private/tree-metadata.ts index 8421fd3933aea..b5dbfa2fce171 100644 --- a/packages/@aws-cdk/core/lib/private/tree-metadata.ts +++ b/packages/@aws-cdk/core/lib/private/tree-metadata.ts @@ -10,6 +10,8 @@ import { ConstructInfo, constructInfoFromConstruct } from './runtime-info'; const FILE_PATH = 'tree.json'; +const TREE_METADATA_SYMBOL = Symbol.for('@aws-cdk/core.TreeMetadata'); + /** * Construct that is automatically attached to the top-level `App`. * This generates, as part of synthesis, a file containing the construct tree and the metadata for each node in the tree. @@ -17,6 +19,13 @@ const FILE_PATH = 'tree.json'; * */ export class TreeMetadata extends Construct { + /** + * Return whether the given object is a TreeMetadata. + */ + public static isTreeMetadata(x: any): x is TreeMetadata { + return x !== null && typeof(x) === 'object' && TREE_METADATA_SYMBOL in x; + } + constructor(scope: Construct) { super(scope, 'Tree'); } @@ -98,3 +107,12 @@ interface Node { */ readonly constructInfo?: ConstructInfo; } + +/** + * Mark all instances of 'TreeMetadata'. + */ +Object.defineProperty(TreeMetadata.prototype, TREE_METADATA_SYMBOL, { + value: true, + enumerable: false, + writable: false, +}); diff --git a/packages/@aws-cdk/core/lib/runtime.ts b/packages/@aws-cdk/core/lib/runtime.ts index 84edb8f6e5728..e8cfd91a14085 100644 --- a/packages/@aws-cdk/core/lib/runtime.ts +++ b/packages/@aws-cdk/core/lib/runtime.ts @@ -231,6 +231,7 @@ export function validateBoolean(x: any): ValidationResult { } export function validateDate(x: any): ValidationResult { + // eslint-disable-next-line no-instanceof/no-instanceof if (canInspect(x) && !(x instanceof Date)) { return new ValidationResult(`${JSON.stringify(x)} should be a Date`); } diff --git a/packages/@aws-cdk/core/test/stack-synthesis/clicreds-synthesis.test.ts b/packages/@aws-cdk/core/test/stack-synthesis/clicreds-synthesis.test.ts index 3fab5ef7e0b5f..19a00351fef76 100644 --- a/packages/@aws-cdk/core/test/stack-synthesis/clicreds-synthesis.test.ts +++ b/packages/@aws-cdk/core/test/stack-synthesis/clicreds-synthesis.test.ts @@ -239,7 +239,7 @@ function evalCFN(value: any) { } function isAssetManifest(x: cxapi.CloudArtifact): x is cxapi.AssetManifestArtifact { - return x instanceof cxapi.AssetManifestArtifact; + return cxapi.AssetManifestArtifact.isAssetManifestArtifact(x); } function getAssetManifest(asm: cxapi.CloudAssembly): cxapi.AssetManifestArtifact { diff --git a/packages/@aws-cdk/core/test/stack-synthesis/new-style-synthesis.test.ts b/packages/@aws-cdk/core/test/stack-synthesis/new-style-synthesis.test.ts index 4ced5a1e9afeb..ddf747aa0b03c 100644 --- a/packages/@aws-cdk/core/test/stack-synthesis/new-style-synthesis.test.ts +++ b/packages/@aws-cdk/core/test/stack-synthesis/new-style-synthesis.test.ts @@ -393,7 +393,7 @@ function evalCFN(value: any) { } function isAssetManifest(x: cxapi.CloudArtifact): x is cxapi.AssetManifestArtifact { - return x instanceof cxapi.AssetManifestArtifact; + return cxapi.AssetManifestArtifact.isAssetManifestArtifact(x); } function getAssetManifest(asm: cxapi.CloudAssembly): cxapi.AssetManifestArtifact { diff --git a/packages/@aws-cdk/core/test/stage.test.ts b/packages/@aws-cdk/core/test/stage.test.ts index 6c7f27c8cfec4..14f47d42e275b 100644 --- a/packages/@aws-cdk/core/test/stage.test.ts +++ b/packages/@aws-cdk/core/test/stage.test.ts @@ -50,7 +50,7 @@ describe('stage', () => { // THEN -- app manifest contains a nested cloud assembly const appAsm = app.synth(); - const artifact = appAsm.artifacts.find(x => x instanceof cxapi.NestedCloudAssemblyArtifact); + const artifact = appAsm.artifacts.find(x => cxapi.NestedCloudAssemblyArtifact.isNestedCloudAssemblyArtifact(x)); expect(artifact).toBeDefined(); diff --git a/packages/@aws-cdk/cx-api/lib/artifacts/cloudformation-artifact.ts b/packages/@aws-cdk/cx-api/lib/artifacts/cloudformation-artifact.ts index f601131d9f532..a9bd5dc46edf8 100644 --- a/packages/@aws-cdk/cx-api/lib/artifacts/cloudformation-artifact.ts +++ b/packages/@aws-cdk/cx-api/lib/artifacts/cloudformation-artifact.ts @@ -5,7 +5,16 @@ import { CloudArtifact } from '../cloud-artifact'; import type { CloudAssembly } from '../cloud-assembly'; import { Environment, EnvironmentUtils } from '../environment'; +const CLOUDFORMATION_STACK_ARTIFACT_SYMBOL = Symbol.for('@aws-cdk/cx-api.CloudFormationStackArtifact'); + export class CloudFormationStackArtifact extends CloudArtifact { + /** + * Return whether the given object is a CloudFormationStackArtifact. + */ + public static isCloudFormationStackArtifact(x: any): x is CloudFormationStackArtifact { + return x !== null && typeof(x) === 'object' && CLOUDFORMATION_STACK_ARTIFACT_SYMBOL in x; + } + /** * The file name of the template. */ @@ -183,3 +192,12 @@ export class CloudFormationStackArtifact extends CloudArtifact { return ret; } } + +/** + * Mark all instances of 'CloudFormationStackArtifact'. + */ +Object.defineProperty(CloudFormationStackArtifact.prototype, CLOUDFORMATION_STACK_ARTIFACT_SYMBOL, { + value: true, + enumerable: false, + writable: false, +}); \ No newline at end of file diff --git a/packages/@aws-cdk/cx-api/lib/artifacts/nested-cloud-assembly-artifact.ts b/packages/@aws-cdk/cx-api/lib/artifacts/nested-cloud-assembly-artifact.ts index 61684316949d4..f604b1a4ba19f 100644 --- a/packages/@aws-cdk/cx-api/lib/artifacts/nested-cloud-assembly-artifact.ts +++ b/packages/@aws-cdk/cx-api/lib/artifacts/nested-cloud-assembly-artifact.ts @@ -3,10 +3,19 @@ import * as cxschema from '@aws-cdk/cloud-assembly-schema'; import { CloudArtifact } from '../cloud-artifact'; import type { CloudAssembly } from '../cloud-assembly'; +const NESTED_CLOUD_ASSEMBLY_ARTIFACT_SYMBOL = Symbol.for('@aws-cdk/cx-api.NestedCloudAssemblyArtifact'); + /** * Asset manifest is a description of a set of assets which need to be built and published */ export class NestedCloudAssemblyArtifact extends CloudArtifact { + /** + * Return whether the given object is a NestedCloudAssemblyArtifact. + */ + public static isNestedCloudAssemblyArtifact(x: any): x is NestedCloudAssemblyArtifact { + return x !== null && typeof(x) === 'object' && NESTED_CLOUD_ASSEMBLY_ARTIFACT_SYMBOL in x; + } + /** * The relative directory name of the asset manifest */ @@ -40,4 +49,13 @@ export interface NestedCloudAssemblyArtifact { readonly nestedAssembly: CloudAssembly; // Declared in a different file -} \ No newline at end of file +} + +/** + * Mark all instances of 'NestedCloudAssemblyArtifact'. + */ +Object.defineProperty(NestedCloudAssemblyArtifact.prototype, NESTED_CLOUD_ASSEMBLY_ARTIFACT_SYMBOL, { + value: true, + enumerable: false, + writable: false, +}); \ No newline at end of file diff --git a/packages/@aws-cdk/cx-api/lib/artifacts/tree-cloud-artifact.ts b/packages/@aws-cdk/cx-api/lib/artifacts/tree-cloud-artifact.ts index 689f3468ca252..85f3edeb7564a 100644 --- a/packages/@aws-cdk/cx-api/lib/artifacts/tree-cloud-artifact.ts +++ b/packages/@aws-cdk/cx-api/lib/artifacts/tree-cloud-artifact.ts @@ -2,7 +2,16 @@ import * as cxschema from '@aws-cdk/cloud-assembly-schema'; import { CloudArtifact } from '../cloud-artifact'; import { CloudAssembly } from '../cloud-assembly'; +const TREE_CLOUD_ARTIFACT_SYMBOL = Symbol.for('@aws-cdk/cx-api.TreeCloudArtifact'); + export class TreeCloudArtifact extends CloudArtifact { + /** + * Return whether the given object is a TreeCloudArtifact. + */ + public static isTreeCloudArtifact(x: any): x is TreeCloudArtifact { + return x !== null && typeof(x) === 'object' && TREE_CLOUD_ARTIFACT_SYMBOL in x; + } + public readonly file: string; constructor(assembly: CloudAssembly, name: string, artifact: cxschema.ArtifactManifest) { @@ -14,4 +23,13 @@ export class TreeCloudArtifact extends CloudArtifact { } this.file = properties.file; } -} \ No newline at end of file +} + +/** + * Mark all instances of 'TreeCloudArtifact'. + */ +Object.defineProperty(TreeCloudArtifact.prototype, TREE_CLOUD_ARTIFACT_SYMBOL, { + value: true, + enumerable: false, + writable: false, +}); diff --git a/packages/@aws-cdk/cx-api/lib/cloud-assembly.ts b/packages/@aws-cdk/cx-api/lib/cloud-assembly.ts index 70f4ecc850f36..61f6e19905e92 100644 --- a/packages/@aws-cdk/cx-api/lib/cloud-assembly.ts +++ b/packages/@aws-cdk/cx-api/lib/cloud-assembly.ts @@ -80,7 +80,7 @@ export class CloudAssembly { * @returns a `CloudFormationStackArtifact` object. */ public getStackByName(stackName: string): CloudFormationStackArtifact { - const artifacts = this.artifacts.filter(a => a instanceof CloudFormationStackArtifact && a.stackName === stackName); + const artifacts = this.artifacts.filter(a => CloudFormationStackArtifact.isCloudFormationStackArtifact(a) && a.stackName === stackName); if (!artifacts || artifacts.length === 0) { throw new Error(`Unable to find stack with stack name "${stackName}"`); } @@ -115,7 +115,7 @@ export class CloudAssembly { throw new Error(`Unable to find artifact with id "${artifactId}"`); } - if (!(artifact instanceof CloudFormationStackArtifact)) { + if (!CloudFormationStackArtifact.isCloudFormationStackArtifact(artifact)) { throw new Error(`Artifact ${artifactId} is not a CloudFormation stack`); } @@ -154,7 +154,7 @@ export class CloudAssembly { throw new Error(`Unable to find artifact with id "${artifactId}"`); } - if (!(artifact instanceof NestedCloudAssemblyArtifact)) { + if (!NestedCloudAssemblyArtifact.isNestedCloudAssemblyArtifact(artifact)) { throw new Error(`Found artifact '${artifactId}' but it's not a nested cloud assembly`); } @@ -184,7 +184,7 @@ export class CloudAssembly { } const tree = trees[0]; - if (!(tree instanceof TreeCloudArtifact)) { + if (!TreeCloudArtifact.isTreeCloudArtifact(tree)) { throw new Error('"Tree" artifact is not of expected type'); } @@ -195,22 +195,14 @@ export class CloudAssembly { * @returns all the CloudFormation stack artifacts that are included in this assembly. */ public get stacks(): CloudFormationStackArtifact[] { - return this.artifacts.filter(isCloudFormationStackArtifact); - - function isCloudFormationStackArtifact(x: any): x is CloudFormationStackArtifact { - return x instanceof CloudFormationStackArtifact; - } + return this.artifacts.filter(CloudFormationStackArtifact.isCloudFormationStackArtifact); } /** * The nested assembly artifacts in this assembly */ public get nestedAssemblies(): NestedCloudAssemblyArtifact[] { - return this.artifacts.filter(isNestedCloudAssemblyArtifact); - - function isNestedCloudAssemblyArtifact(x: any): x is NestedCloudAssemblyArtifact { - return x instanceof NestedCloudAssemblyArtifact; - } + return this.artifacts.filter(NestedCloudAssemblyArtifact.isNestedCloudAssemblyArtifact); } private validateDeps() { diff --git a/tools/@aws-cdk/cfn2ts/lib/genspec.ts b/tools/@aws-cdk/cfn2ts/lib/genspec.ts index 7f63778097d59..d14cf225f241b 100644 --- a/tools/@aws-cdk/cfn2ts/lib/genspec.ts +++ b/tools/@aws-cdk/cfn2ts/lib/genspec.ts @@ -111,7 +111,7 @@ export class Attribute { * Example: AWS::EC2 -> ec2 */ export function packageName(module: SpecName | string): string { - if (module instanceof SpecName) { + if (SpecName.isSpecName(module)) { module = module.module; } diff --git a/tools/@aws-cdk/cfn2ts/lib/spec-utils.ts b/tools/@aws-cdk/cfn2ts/lib/spec-utils.ts index 7637bc199439a..366c293f15893 100644 --- a/tools/@aws-cdk/cfn2ts/lib/spec-utils.ts +++ b/tools/@aws-cdk/cfn2ts/lib/spec-utils.ts @@ -1,12 +1,21 @@ import { schema } from '@aws-cdk/cfnspec'; import { joinIf } from './util'; +const SPEC_NAME_SYMBOL = Symbol.for('@aws-cdk/cfn2ts.SpecName'); + /** * Name of an object in the CloudFormation spec * * This refers to a Resource, parsed from a string like 'AWS::S3::Bucket'. */ export class SpecName { + /** + * Return whether the given object is a SpecName. + */ + public static isSpecName(x: any): x is SpecName { + return x !== null && typeof(x) === 'object' && SPEC_NAME_SYMBOL in x; + } + /** * Parse a string representing a name from the CloudFormation spec to a CfnName object */ @@ -36,6 +45,7 @@ export class SpecName { } } + /** * Name of a property type or attribute in the CloudFormation spec. * @@ -121,3 +131,12 @@ function primitiveScalarTypeNames(spec: schema.ScalarProperty): string[] { } return []; } + +/** + * Mark all instances of 'SpecName'. + */ +Object.defineProperty(SpecName.prototype, SPEC_NAME_SYMBOL, { + value: true, + enumerable: false, + writable: false, +});