From 9f4578363c248c0c88cff2ae5be1fed7a373ec07 Mon Sep 17 00:00:00 2001 From: Elad Ben-Israel Date: Thu, 3 Jan 2019 22:16:49 +0200 Subject: [PATCH] chore: awslint (#1468) Introducing awslint: a linter for the AWS Construct Library APIs. The goal of the linter is to enforce the API design guidelines across the AWS Construct Library. This commit includes a set of initial rules for the guidelines defined under design/aws-guidelines.md. - The `awslint` npm script has been added to all modules through pkglint. - All violations have been captured through "exclude"s in `package.json` files so we can continue to lint from here. --- CONTRIBUTING.md | 22 + design/aws-guidelines.md | 444 ++++++++++ packages/@aws-cdk/alexa-ask/package.json | 3 +- packages/@aws-cdk/app-delivery/package.json | 3 +- packages/@aws-cdk/assets-docker/package.json | 3 +- packages/@aws-cdk/assets/package.json | 3 +- packages/@aws-cdk/aws-amazonmq/package.json | 3 +- packages/@aws-cdk/aws-apigateway/package.json | 8 +- .../aws-applicationautoscaling/package.json | 3 +- packages/@aws-cdk/aws-appstream/package.json | 3 +- packages/@aws-cdk/aws-appsync/package.json | 3 +- packages/@aws-cdk/aws-athena/package.json | 3 +- .../@aws-cdk/aws-autoscaling-api/package.json | 3 +- .../aws-autoscaling-common/package.json | 3 +- .../@aws-cdk/aws-autoscaling/package.json | 10 +- .../aws-autoscalingplans/package.json | 3 +- packages/@aws-cdk/aws-batch/package.json | 3 +- packages/@aws-cdk/aws-budgets/package.json | 3 +- .../aws-certificatemanager/package.json | 3 +- packages/@aws-cdk/aws-cloud9/package.json | 3 +- .../@aws-cdk/aws-cloudformation/package.json | 9 +- packages/@aws-cdk/aws-cloudfront/package.json | 3 +- packages/@aws-cdk/aws-cloudtrail/package.json | 3 +- packages/@aws-cdk/aws-cloudwatch/package.json | 3 +- packages/@aws-cdk/aws-codebuild/package.json | 3 +- packages/@aws-cdk/aws-codecommit/package.json | 3 +- .../@aws-cdk/aws-codedeploy-api/package.json | 3 +- packages/@aws-cdk/aws-codedeploy/package.json | 8 +- .../aws-codepipeline-api/package.json | 9 +- .../@aws-cdk/aws-codepipeline/package.json | 9 +- packages/@aws-cdk/aws-cognito/package.json | 3 +- packages/@aws-cdk/aws-config/package.json | 3 +- .../@aws-cdk/aws-datapipeline/package.json | 3 +- packages/@aws-cdk/aws-dax/package.json | 3 +- .../aws-directoryservice/package.json | 3 +- packages/@aws-cdk/aws-dlm/package.json | 3 +- packages/@aws-cdk/aws-dms/package.json | 3 +- packages/@aws-cdk/aws-dynamodb/package.json | 3 +- packages/@aws-cdk/aws-ec2/package.json | 8 +- packages/@aws-cdk/aws-ecr/package.json | 8 +- packages/@aws-cdk/aws-ecs/package.json | 11 +- packages/@aws-cdk/aws-efs/package.json | 3 +- packages/@aws-cdk/aws-eks/package.json | 3 +- .../@aws-cdk/aws-elasticache/package.json | 3 +- .../aws-elasticbeanstalk/package.json | 3 +- .../aws-elasticloadbalancing/package.json | 3 +- .../aws-elasticloadbalancingv2/package.json | 10 +- .../@aws-cdk/aws-elasticsearch/package.json | 3 +- packages/@aws-cdk/aws-emr/package.json | 3 +- packages/@aws-cdk/aws-events/package.json | 3 +- packages/@aws-cdk/aws-gamelift/package.json | 3 +- packages/@aws-cdk/aws-glue/package.json | 3 +- packages/@aws-cdk/aws-guardduty/package.json | 3 +- packages/@aws-cdk/aws-iam/package.json | 3 +- packages/@aws-cdk/aws-inspector/package.json | 3 +- packages/@aws-cdk/aws-iot/package.json | 3 +- packages/@aws-cdk/aws-iot1click/package.json | 3 +- .../@aws-cdk/aws-iotanalytics/package.json | 3 +- packages/@aws-cdk/aws-kinesis/package.json | 3 +- .../aws-kinesisanalytics/package.json | 3 +- .../@aws-cdk/aws-kinesisfirehose/package.json | 3 +- packages/@aws-cdk/aws-kms/package.json | 3 +- .../aws-lambda-event-sources/package.json | 3 +- packages/@aws-cdk/aws-lambda/package.json | 3 +- packages/@aws-cdk/aws-logs/package.json | 3 +- packages/@aws-cdk/aws-neptune/package.json | 3 +- packages/@aws-cdk/aws-opsworks/package.json | 3 +- .../@aws-cdk/aws-quickstarts/package.json | 3 +- packages/@aws-cdk/aws-rds/package.json | 3 +- packages/@aws-cdk/aws-redshift/package.json | 3 +- packages/@aws-cdk/aws-route53/package.json | 9 +- .../@aws-cdk/aws-route53resolver/package.json | 3 +- .../@aws-cdk/aws-s3-deployment/package.json | 3 +- .../aws-s3-notifications/package.json | 3 +- packages/@aws-cdk/aws-s3/package.json | 12 +- packages/@aws-cdk/aws-sagemaker/package.json | 3 +- packages/@aws-cdk/aws-sdb/package.json | 3 +- .../@aws-cdk/aws-secretsmanager/package.json | 3 +- packages/@aws-cdk/aws-serverless/package.json | 3 +- .../@aws-cdk/aws-servicecatalog/package.json | 3 +- .../aws-servicediscovery/package.json | 3 +- packages/@aws-cdk/aws-ses/package.json | 3 +- packages/@aws-cdk/aws-sns/package.json | 3 +- packages/@aws-cdk/aws-sqs/package.json | 8 +- packages/@aws-cdk/aws-ssm/package.json | 3 +- .../@aws-cdk/aws-stepfunctions/package.json | 8 +- packages/@aws-cdk/aws-waf/package.json | 3 +- .../@aws-cdk/aws-wafregional/package.json | 3 +- packages/@aws-cdk/aws-workspaces/package.json | 3 +- packages/@aws-cdk/cdk/package.json | 10 +- packages/@aws-cdk/cx-api/package.json | 3 +- packages/@aws-cdk/runtime-values/package.json | 3 +- tools/awslint/.gitignore | 95 +++ tools/awslint/.npmignore | 5 + tools/awslint/LICENSE | 201 +++++ tools/awslint/NOTICE | 2 + tools/awslint/README.md | 130 +++ tools/awslint/bin/awslint | 2 + tools/awslint/bin/awslint.ts | 254 ++++++ tools/awslint/lib/cfn-resources.ts | 83 ++ tools/awslint/lib/index.ts | 2 + tools/awslint/lib/linter.ts | 234 +++++ tools/awslint/lib/rules/construct.ts | 51 ++ tools/awslint/lib/rules/index.ts | 3 + tools/awslint/lib/rules/module.ts | 31 + tools/awslint/lib/rules/resource.ts | 180 ++++ tools/awslint/lib/util.ts | 11 + tools/awslint/load.sh | 32 + tools/awslint/package-lock.json | 796 ++++++++++++++++++ tools/awslint/package.json | 48 ++ tools/awslint/tsconfig.json | 28 + tools/awslint/tslint.yaml | 53 ++ tools/cdk-build-tools/bin/cdk-awslint | 2 + tools/cdk-build-tools/lib/compile.ts | 2 + tools/cdk-build-tools/package.json | 6 +- tools/pkglint/lib/rules.ts | 16 +- 116 files changed, 2999 insertions(+), 96 deletions(-) create mode 100644 design/aws-guidelines.md create mode 100644 tools/awslint/.gitignore create mode 100644 tools/awslint/.npmignore create mode 100644 tools/awslint/LICENSE create mode 100644 tools/awslint/NOTICE create mode 100644 tools/awslint/README.md create mode 100755 tools/awslint/bin/awslint create mode 100644 tools/awslint/bin/awslint.ts create mode 100644 tools/awslint/lib/cfn-resources.ts create mode 100644 tools/awslint/lib/index.ts create mode 100644 tools/awslint/lib/linter.ts create mode 100644 tools/awslint/lib/rules/construct.ts create mode 100644 tools/awslint/lib/rules/index.ts create mode 100644 tools/awslint/lib/rules/module.ts create mode 100644 tools/awslint/lib/rules/resource.ts create mode 100644 tools/awslint/lib/util.ts create mode 100755 tools/awslint/load.sh create mode 100644 tools/awslint/package-lock.json create mode 100644 tools/awslint/package.json create mode 100644 tools/awslint/tsconfig.json create mode 100644 tools/awslint/tslint.yaml create mode 100755 tools/cdk-build-tools/bin/cdk-awslint diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 749ed038e0afc..ccd508dab5efd 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -134,6 +134,28 @@ You can also do that per package: $ lr pkglint ``` +### awslint + +**awslint** is a linter for the AWS Construct Library APIs. It is executed as a +part of the build of all AWS modules in the project and enforces the [AWS +Construct Library Design Guidelines](./design/aws-guidelines.md). + +For more information about this tool, see the [awslint +README](./tools/awslint/README.md). + +Generally speaking, if you make any changes which violate an awslint rule, build +will fail with appropriate messages. All rules are documented and explained in +the [guidelines](./design/aws-guidelines.md). + +Here are a few useful commands: + + * `npm run awslint` in every module will run __awslint__ for that module. + * `npm run awslint list` prints all rules (details and rationale in the guidelines doc) + * `lerna run awslint` will run __awslint__ in all modules. + * `lerna run awslint -- -i ` will run awslint throughout the repo and + evaluate only the rule specified [awslint README](./tools/awslint/README.md) + for details on include/exclude rule patterns. + ## Development Workflows ### Full clean build diff --git a/design/aws-guidelines.md b/design/aws-guidelines.md new file mode 100644 index 0000000000000..e03a689de5a38 --- /dev/null +++ b/design/aws-guidelines.md @@ -0,0 +1,444 @@ +# AWS Construct Library Design Guidelines + +The AWS Construct Library is a rich class library of CDK constructs which +represent all resources offered by the AWS Cloud. + +The purpose of this document is to describe common guidelines for designing the +APIs in the AWS Construct Library in order to ensure a consistent and integrated +experience across the entire AWS surface area. + +As much as possible, the guidelines in this document are enforced using the +__awslint__ tool which reflects on the APIs and verifies that the APIs adhere to +the guidelines. + +When a guideline is backed by a linter rule, the rule name will be referenced +like this: `awslint|resource-class-is-construct` +and anchored with the rule name. + +For the purpose of this document we will use "**Foo**" to denote the official +name of the resource as defined in the AWS CloudFormation resource specification +(i.e. "Bucket", "Queue", "Topic", etc). This notation allows deriving names from +the official name. For example, `FooProps` would be `BucketProps`, `TopicProps`, +etc, `IFoo` would be `IBucket`, `ITopic` and so forth. + +The guidelines in this document use TypeScript (and npm package names) since +this is the source programming language used to author the library, which is +later packaged and published to all programming languages through +[jsii](https://github.com/awslabs/jsii). + +## Modules + +> awslint: module-name + +AWS resources are organized into modules based on their AWS service. For +example, the "Bucket" resource, which is offered by the Amazon S3 service will +be available under the **@aws-cdk/aws-s3** module. + +## Constructs + +Constructs are the basic building block of CDK applications. They represent +abstract cloud resources of any complexity. + +> awslint: construct-ctor + +Construct initializer (constructor) signature should always be: + +```ts +constructor(scope: cdk.Construct, id: string, props: FooProps) +``` + +> __TODO__: awslint: construct-ctor-optional-props + +If all initialization properties are optional, the `props` argument must also be +optional. + +> NOTE: This rule breaks down for the case where there are two (or more) +> potential arguments, and exactly one (or at least one) of them is required. +> Then the props will be marked as optional, but the whole object is not +> optional since there is no valid use of not specifying anything. +> +> Ideally we rather not design APIs such as this, but there might be cases where +> this is the best approach. In those cases, it is okay to "exclude" this role:scope +> in `package.json`. + +```ts +constructor(scope: cdk.Construct, id: string, props: FooProps = { }) +``` + +Each module in the AWS Construct Library includes generated constructs which +represent the "raw" CloudFormation resources. + +1. These classes are named `CfnFoo` +2. They extend `cdk.Resource` (which extends `cdk.Construct`) +3. Their constructor accepts a `props` parameter of type `CfnFooProps` +4. `CfnFooProps` represents the exact set of properties as defined in the + AWS CloudFormation resource specification. +5. They have readonly properties which represent all the runtime attributes of + the resource. + +> NOTE: there are no linting rules against this section because this layer +> is entirely generated by the __cfn2ts__ tool according to these guidelines +> so there is no need to lint against it. + +## Resource Interface + +> awslint: resource-interface + +Every AWS resource should have a resource interface `IFoo`. + +This interface represents both resources defined within the same stack (aka +"internal" or "owned") or resources that are defined in different stack/app (aka +"imported", "existing", "external" or "unowned"). Throughout this document we +shall refer to these two types of resources as **"internal"** and +**"external"**. + +> awslint: resource-interface-extends-construct + +Resource interfaces should extend `cdk.IConstruct` in order to allow consumers +to take advantage of construct capabilities such as unique IDs, paths, scopes, etc. + +> TODO: awslint: resource-ref-interface + +When resources are referenced anywhere in the API (e.g. in properties or methods +of other resources or higher level constructs), the resource interface (`IFoo`) +should be preferred over the concrete resource class (`Foo`). This will allow +users to supply either internal or external resources. + +## Resource Attributes + +Every AWS resource has a set of "physical" runtime attributes such as ARN, +physical names, URLs, etc. These attributes are commonly late-bound, which means +they can only be resolved when AWS CloudFormation actually provisions the +resource. + +Resource attributes almost always represent **string** values (URL, ARN, name). +Sometimes they might also represent a **list of strings**. + +> awslint: resource-attribute
+> awslint: resource-attribute-immutable + +All resource attributes must be represented as readonly properties of the +resource interface. The names of the attributes must correspond to the +CloudFormation resource attribute name. + +> TODO: awslint: resource-attribute-type + +Since attribute values can either be late-bound ("a promise to a string") or +concrete ("a string"), the AWS CDK has a mechanism called "tokens" which allows +codifying late-bound values into strings or string arrays. This approach was +chosen in order to dramatically simplify the type-system and ergonomics of CDK +code. As long as users treat these attributes as **opaque values** (e.g. not try +to parse them or manipulate them), they can be used interchangeably. + +As long as attribute values are not manipulated, they can still be concatenated +idiomatically. For example: + +```ts +`This is my bucket name: ${bucket.bucketName} and bucket ARN: ${bucket.bucketArn}` +``` + +Even though `bucketName` and `bucketArn` will only be resolved during +deployment, the CDK will identify those as tokens and will convert this string +into an `{ "Fn::Join" }` expression which includes the relevant intrinsic +functions. + +If needed, you can query whether an object includes unresolved tokens by using +the `cdk.unresolved(x)` function. + +Resource attributes should use a type that corresponds to the __resolved__ AWS +CloudFormation type (e.g. `string`, `string[]`). + +At the moment, attributes that represent strings, are represented as `string` in +the `CfnFoo` resource. However, other types of tokens (string arrays, numbers) +are still represented as `Token`. You can use `token.toList()` to represent a token as a +string array, and soon we will also have `toNumber()`. + +## Resource Class + +> awslint: resource-class + +Each `CfnFoo` resource must have a corresponding `Foo` high-level (L2) +class. + +> awslint: resource-class-is-construct + +Classes which represent AWS resources are constructs (they must extend the `cdk.Construct` class +directly or indirectly). + +## Resource Props + +> awslint: resource-props + +Resource constructs are initialized with a set of properties defined in an interface `FooProps`. + +Initialization properties should enable developers to define the resource in +their application. Generally, they should expose most of the surface +area of the resource. + +Initialization properties should be _required_ only if there is no sane default +that can be provided or calculated. By providing sensible and safe defaults (or +"smart defaults"), developers can get started quickly. + +## Imports + +In order to allow users to work with resources that are either internal or +external to their stack, AWS resources should provide an "import/export" +mechanism as described in this section. + +> awslint: import + +Every AWS resource class must include a static method called `import` with the +following signature: + +```ts +static import(scope: cdk.Construct, id: string, props: XxxImportProps): IXxx +``` + +This method returns an object that implements the resource interface (`IXxx`) +and represents an "imported resource". + +> awslint: import-props-interface + +The "props" argument is `XxxImportProps`, which an interface that declares +properties that allow the user to specify an external resource identity, usually +by providing one or more resource attributes such as ARN, physical name, etc. + +The import interface should have the minimum required properties, that is: if it +is possible to parse the resource name from the ARN (using `cdk.ArnUtils.parse`), +then only the ARN should be required. In cases where it +is not possible to parse the ARN (e.g. if it is a token and the resource name +might have use "/" characters), both the ARN and the name should be optional and +runtime-checks should be performed to require that at least one will be defined. +See `ecr.RepositoryAttributes` for an example. + +The recommended way to implement the `import` method is as follows: + +1. A public abstract base class called `XxxBase` which implements `IXxx` and + extends `cdk.Construct`. +2. The base class should provide as much of the implementation of `IXxx` as possible given the + context it has. In most cases, `grant` methods, `metric` methods, etc can be implemented at + at that level. +5. A private class called `ImportedXxx` which extends `XxxBase` and implements + any remaining abstract members. +4. The `import` static method should be have the following implementation: + +```ts +public static import(scope: cdk.Construct, id: string, props: XxxImportProps): IXxx { + return new ImportedXxx(scope, id, props); +} +``` + +## Exports + +> awslint: export + +All resource interfaces (`IXxx`) must declare a method called `export` with the +following signature: + +```ts +export(): XxxImportProps +``` + +This method can be used to export this resource from the current stack and use +it in another stack through an `Xxx.import` call. + +The implementation of `export` is different between internal resources (`Xxx`) and +external imported resource (`ImportedXxx` as recommended above): + +For internal resources, the `export` method should produce a CloudFormation Output +for each resource attribute, and return a set of `{ "Fn::ImportValue" }` tokens +so they can be imported to another stack. + +```ts +class Xxx extends XxxBase { + public export(): XxxImportProps { + return { + attr1: new cdk.Output(this, 'Attr1', { value: this.attr1 }).makeImportValue().toString(), + attr2: new cdk.Output(this, 'Attr2', { value: this.attr2 }).makeImportValue().toString(), + } + } +} +``` + +For external resources, we know the actual values, so basically you would want to reflect +your `props` as is: + +```ts +class ImportedXxx extends XxxBase { + constructor(scope: cdk.Construct, id: string, private readonly props: XxxImportProps) { + // ... + } + + public export() { + return this.props; + } +} +``` + +The reason we are defining `export` on the resource interface and not on the resource +class is in order to allow "composite export" scenarios, where a higher-level construct +wants to implement `export` by composing the exports of multiple resources: + +```ts +interface MyCompositeProps { + bucket: s3.IBucket; + topic: sns.ITopic; +} + +class MyComposite extends cdk.Construct { + public export(): MyCompositeImportProps { + return { + bucket: this.bucket.export(), + topic: this.topic.export() + } + } +} +``` + +In this scenario, you don't know if `bucket` or `topic` are internal or external resources, +but you still want export to work. + +## General Guidelines + +### Defaults must be documented on optional interface properties + +> TODO: awslint: interface-defaults-docs + +The `@default` documentation tag must be included on all optional properties of +interfaces. + +## Complete Example + +Here's a complete "template" for the types required when defining a resource in the +AWS construct library: + +```ts +// must extend cdk.IConstruct +// extends all interfaces that are applicable for both internal +// and external resources of this type +export interface IFoo extends cdk.IConstruct, ISomething { + + // attributes + readonly fooArn: string; + readonly fooFoo: string; + readonly fooBar: string; + + // security group connections (if applicable) + readonly connections: ec2.Connections; + + // permission grants (adds statements to the principal's policy) + grant(principal?: iam.IPrincipal, ...actions: string[]): void; + grantFoo(principal?: iam.IPrincipal): void; + grantBar(principal?: iam.IPrincipal): void; + + // resource policy (if applicable) + addToResourcePolicy(statement: iam.PolicyStatement): void; + + // role (if applicable) + addToRolePolicy(statement: iam.PolicyStatement): void; + + // pipeline (if applicable) + addToPipeline(stage: pipelineapi.IStage, name: string, props?: FooActionProps): FooAction; + + // metrics + metric(metricName: string, props?: cloudwatch.MetricCustomization): cloudwatch.Metric; + metricFoo(props?: cloudwatch.MetricCustomization): cloudwatch.Metric; + metricBar(props?: cloudwatch.MetricCustomization): cloudwatch.Metric; + + // export + export(): FooImportProps; + + // any other methods/properties that are applicable for both internal + // and external resources of this type. + // ... +} + +// base class to share implementation between internal/external resources +// it has to be public sadly. +export abstract class FooBase extends cdk.Construct implements IFoo { + + // attributes are usually still abstract at this level + public abstract readonly fooArn: string; + public abstract readonly fooBoo: string[]; + + // the "export" method is also still abstract + public abstract export(): FooAttributes; + + // grants can usually be shared + public grantYyy(principal?: iam.IPrincipal) { + // ... + } + + // metrics can usually be shared + public metricFoo(...) { ... } +} + +// extends the abstract base class and implement any interfaces that are not applicable +// for imported resources. This is quite rare usually, but can happen. +export class Foo extends FooBase implements IAnotherInterface { + + // the import method is always going to look like this. + public static import(scope: cdk.Construct, id: string, props: FooImportProps): IFoo { + return new ImportedFoo(scope, id, props); + } + + // implement resource attributes as readonly properties + public readonly fooArn: string; + public readonly fooBoo: string[]; + + // ctor's 3rd argument is always FooProps. It should be optional (`= { }`) in case + // there are no required properties. + constructor(scope: cdk.Construct, id: string, props: FooProps) { + super(scope, id); + + // you would usually add a `CfnFoo` resource at this point. + const resource = new CfnFoo(this, 'Resource', { + // ... + }); + + // proxy resource properties + this.fooArn = resource.fooArn; + this.fooBoo = resource.fooBoo; + } + + // this is how export() should be implemented on internal resources + // they would produce a stack export and return the "Fn::ImportValue" token + // for them so they can be imported to another stack. + public export(): FooAttributes { + return { + fooArn: new cdk.Output(this, 'Arn', { value: this.fooArn }).makeImportValue().toString(), // represent Fn::ImportValue as a string + fooBoo: new cdk.Output(this, 'Boo', { value: this.fooBoo }).makeImportValue().toList() // represent as string[] + // ... + } + } +} + +// an internal class (don't export it) representing the external (imported) resource +class ImportedFoo extends FooBase { + public readonly string fooArn; + public readonly string[] fooBoo; + + constructor(scope: cdk.Construct, id: string, private readonly props: FooImportProps) { + super(scope, id); + + this.fooArn = props.fooArn; + this.fooBoo = props.fooBoo; + } + + public export() { + return this.props; // just reflect props back + } +} +``` + +## Roadmap + +- [ ] IAM (`role`, `addToRolePolicy`, `addToResourcePolicy`) +- [ ] Grants (`grantXxx`) +- [ ] Metrics (`metricXxx`) +- [ ] Events (`onXxx`) +- [ ] Security Groups (`connections`) +- [ ] Pipeline Actions (`addToPipline`) +- [ ] SNS Targets +- [ ] `_asFooTarget` +- [ ] TODO: other cross AWS patterns diff --git a/packages/@aws-cdk/alexa-ask/package.json b/packages/@aws-cdk/alexa-ask/package.json index 6a83c3146f4ce..1b7912a0799e0 100644 --- a/packages/@aws-cdk/alexa-ask/package.json +++ b/packages/@aws-cdk/alexa-ask/package.json @@ -35,7 +35,8 @@ "package": "cdk-package", "pkglint": "pkglint -f", "test": "cdk-test", - "watch": "cdk-watch" + "watch": "cdk-watch", + "awslint": "cdk-awslint" }, "cdk-build": { "cloudformation": "Alexa::ASK" diff --git a/packages/@aws-cdk/app-delivery/package.json b/packages/@aws-cdk/app-delivery/package.json index 7ea537afffe62..f14ae9a9488f6 100644 --- a/packages/@aws-cdk/app-delivery/package.json +++ b/packages/@aws-cdk/app-delivery/package.json @@ -29,7 +29,8 @@ "pkglint": "pkglint -f", "test": "cdk-test", "watch": "cdk-watch", - "integ": "cdk-integ" + "integ": "cdk-integ", + "awslint": "cdk-awslint" }, "dependencies": { "@aws-cdk/aws-cloudformation": "^0.21.0", diff --git a/packages/@aws-cdk/assets-docker/package.json b/packages/@aws-cdk/assets-docker/package.json index 7d2027ec22cc8..6fad14ead16d4 100644 --- a/packages/@aws-cdk/assets-docker/package.json +++ b/packages/@aws-cdk/assets-docker/package.json @@ -34,7 +34,8 @@ "test": "cdk-test", "integ": "cdk-integ", "pkglint": "pkglint -f", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "keywords": [ "aws", diff --git a/packages/@aws-cdk/assets/package.json b/packages/@aws-cdk/assets/package.json index fbd52693c2251..72a8af48cbb64 100644 --- a/packages/@aws-cdk/assets/package.json +++ b/packages/@aws-cdk/assets/package.json @@ -34,7 +34,8 @@ "test": "cdk-test", "integ": "cdk-integ", "pkglint": "pkglint -f", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "keywords": [ "aws", diff --git a/packages/@aws-cdk/aws-amazonmq/package.json b/packages/@aws-cdk/aws-amazonmq/package.json index bd5c38399ffe7..aab237179c15b 100644 --- a/packages/@aws-cdk/aws-amazonmq/package.json +++ b/packages/@aws-cdk/aws-amazonmq/package.json @@ -35,7 +35,8 @@ "package": "cdk-package", "pkglint": "pkglint -f", "test": "cdk-test", - "watch": "cdk-watch" + "watch": "cdk-watch", + "awslint": "cdk-awslint" }, "cdk-build": { "cloudformation": "AWS::AmazonMQ" diff --git a/packages/@aws-cdk/aws-apigateway/package.json b/packages/@aws-cdk/aws-apigateway/package.json index 296c7bd7d9adc..e3492e31d6eb4 100644 --- a/packages/@aws-cdk/aws-apigateway/package.json +++ b/packages/@aws-cdk/aws-apigateway/package.json @@ -34,7 +34,8 @@ "test": "cdk-test", "integ": "cdk-integ", "pkglint": "pkglint -f", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "cdk-build": { "cloudformation": "AWS::ApiGateway" @@ -71,5 +72,10 @@ }, "engines": { "node": ">= 8.10.0" + }, + "awslint": { + "exclude": [ + "resource-attribute:@aws-cdk/aws-apigateway.IRestApi.restApiRootResourceId" + ] } } diff --git a/packages/@aws-cdk/aws-applicationautoscaling/package.json b/packages/@aws-cdk/aws-applicationautoscaling/package.json index 91688c0c41169..610eeef609f98 100644 --- a/packages/@aws-cdk/aws-applicationautoscaling/package.json +++ b/packages/@aws-cdk/aws-applicationautoscaling/package.json @@ -34,7 +34,8 @@ "test": "cdk-test", "integ": "cdk-integ", "pkglint": "pkglint -f", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "cdk-build": { "cloudformation": "AWS::ApplicationAutoScaling" diff --git a/packages/@aws-cdk/aws-appstream/package.json b/packages/@aws-cdk/aws-appstream/package.json index cddd307c7d701..284f90a5c0b93 100644 --- a/packages/@aws-cdk/aws-appstream/package.json +++ b/packages/@aws-cdk/aws-appstream/package.json @@ -35,7 +35,8 @@ "package": "cdk-package", "pkglint": "pkglint -f", "test": "cdk-test", - "watch": "cdk-watch" + "watch": "cdk-watch", + "awslint": "cdk-awslint" }, "cdk-build": { "cloudformation": "AWS::AppStream" diff --git a/packages/@aws-cdk/aws-appsync/package.json b/packages/@aws-cdk/aws-appsync/package.json index 92eb95dfb1fba..8616917b69ee2 100644 --- a/packages/@aws-cdk/aws-appsync/package.json +++ b/packages/@aws-cdk/aws-appsync/package.json @@ -34,7 +34,8 @@ "test": "cdk-test", "integ": "cdk-integ", "pkglint": "pkglint -f", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "cdk-build": { "cloudformation": "AWS::AppSync" diff --git a/packages/@aws-cdk/aws-athena/package.json b/packages/@aws-cdk/aws-athena/package.json index 9ff458bca3716..a098df2cd14b5 100644 --- a/packages/@aws-cdk/aws-athena/package.json +++ b/packages/@aws-cdk/aws-athena/package.json @@ -37,7 +37,8 @@ "test": "cdk-test", "integ": "cdk-integ", "pkglint": "pkglint -f", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "keywords": [ "aws", diff --git a/packages/@aws-cdk/aws-autoscaling-api/package.json b/packages/@aws-cdk/aws-autoscaling-api/package.json index b519fd53ddb57..5d0b8c19f571c 100644 --- a/packages/@aws-cdk/aws-autoscaling-api/package.json +++ b/packages/@aws-cdk/aws-autoscaling-api/package.json @@ -34,7 +34,8 @@ "test": "cdk-test", "integ": "cdk-integ", "pkglint": "pkglint -f", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "keywords": [ "aws", diff --git a/packages/@aws-cdk/aws-autoscaling-common/package.json b/packages/@aws-cdk/aws-autoscaling-common/package.json index 096d6d8f7e7ae..9141b40826e2a 100644 --- a/packages/@aws-cdk/aws-autoscaling-common/package.json +++ b/packages/@aws-cdk/aws-autoscaling-common/package.json @@ -34,7 +34,8 @@ "test": "cdk-test", "integ": "cdk-integ", "pkglint": "pkglint -f", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "keywords": [ "aws", diff --git a/packages/@aws-cdk/aws-autoscaling/package.json b/packages/@aws-cdk/aws-autoscaling/package.json index 034dd28932e7a..15d817bf5d798 100644 --- a/packages/@aws-cdk/aws-autoscaling/package.json +++ b/packages/@aws-cdk/aws-autoscaling/package.json @@ -34,7 +34,8 @@ "test": "cdk-test", "integ": "cdk-integ", "pkglint": "pkglint -f", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "cdk-build": { "cloudformation": "AWS::AutoScaling" @@ -82,5 +83,12 @@ }, "engines": { "node": ">= 8.10.0" + }, + "awslint": { + "exclude": [ + "import-props-interface:@aws-cdk/aws-autoscaling.AutoScalingGroupImportProps", + "resource-interface-extends-construct:@aws-cdk/aws-autoscaling.IAutoScalingGroup", + "export:@aws-cdk/aws-autoscaling.IAutoScalingGroup" + ] } } diff --git a/packages/@aws-cdk/aws-autoscalingplans/package.json b/packages/@aws-cdk/aws-autoscalingplans/package.json index bcf0ba922cb55..ee7cc4713ee65 100644 --- a/packages/@aws-cdk/aws-autoscalingplans/package.json +++ b/packages/@aws-cdk/aws-autoscalingplans/package.json @@ -34,7 +34,8 @@ "test": "cdk-test", "integ": "cdk-integ", "pkglint": "pkglint -f", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "cdk-build": { "cloudformation": "AWS::AutoScalingPlans" diff --git a/packages/@aws-cdk/aws-batch/package.json b/packages/@aws-cdk/aws-batch/package.json index da8f672f87d27..fce8f30ba8c47 100644 --- a/packages/@aws-cdk/aws-batch/package.json +++ b/packages/@aws-cdk/aws-batch/package.json @@ -34,7 +34,8 @@ "test": "cdk-test", "integ": "cdk-integ", "pkglint": "pkglint -f", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "cdk-build": { "cloudformation": "AWS::Batch" diff --git a/packages/@aws-cdk/aws-budgets/package.json b/packages/@aws-cdk/aws-budgets/package.json index b3e89ea140568..5d959d0188452 100644 --- a/packages/@aws-cdk/aws-budgets/package.json +++ b/packages/@aws-cdk/aws-budgets/package.json @@ -34,7 +34,8 @@ "test": "cdk-test", "integ": "cdk-integ", "pkglint": "pkglint -f", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "cdk-build": { "cloudformation": "AWS::Budgets" diff --git a/packages/@aws-cdk/aws-certificatemanager/package.json b/packages/@aws-cdk/aws-certificatemanager/package.json index 35b69cb670f22..6b1e6d57d051e 100644 --- a/packages/@aws-cdk/aws-certificatemanager/package.json +++ b/packages/@aws-cdk/aws-certificatemanager/package.json @@ -34,7 +34,8 @@ "test": "cdk-test", "integ": "cdk-integ", "pkglint": "pkglint -f", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "cdk-build": { "cloudformation": "AWS::CertificateManager" diff --git a/packages/@aws-cdk/aws-cloud9/package.json b/packages/@aws-cdk/aws-cloud9/package.json index f3e42aaf7e5ee..d995030c736b0 100644 --- a/packages/@aws-cdk/aws-cloud9/package.json +++ b/packages/@aws-cdk/aws-cloud9/package.json @@ -34,7 +34,8 @@ "test": "cdk-test", "integ": "cdk-integ", "pkglint": "pkglint -f", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "cdk-build": { "cloudformation": "AWS::Cloud9" diff --git a/packages/@aws-cdk/aws-cloudformation/package.json b/packages/@aws-cdk/aws-cloudformation/package.json index 233eafdb68d0c..0c584f7137903 100644 --- a/packages/@aws-cdk/aws-cloudformation/package.json +++ b/packages/@aws-cdk/aws-cloudformation/package.json @@ -34,7 +34,8 @@ "test": "cdk-test", "integ": "cdk-integ", "pkglint": "pkglint -f", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "cdk-build": { "cloudformation": "AWS::CloudFormation" @@ -82,5 +83,11 @@ }, "engines": { "node": ">= 8.10.0" + }, + "awslint": { + "exclude": [ + "construct-ctor:@aws-cdk/aws-cloudformation.PipelineCloudFormationAction.", + "construct-ctor:@aws-cdk/aws-cloudformation.PipelineCloudFormationDeployAction." + ] } } diff --git a/packages/@aws-cdk/aws-cloudfront/package.json b/packages/@aws-cdk/aws-cloudfront/package.json index 7210ef4328290..26c0fdc8fcf48 100644 --- a/packages/@aws-cdk/aws-cloudfront/package.json +++ b/packages/@aws-cdk/aws-cloudfront/package.json @@ -34,7 +34,8 @@ "test": "cdk-test", "integ": "cdk-integ", "pkglint": "pkglint -f", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "cdk-build": { "cloudformation": "AWS::CloudFront" diff --git a/packages/@aws-cdk/aws-cloudtrail/package.json b/packages/@aws-cdk/aws-cloudtrail/package.json index defaae3b1d9c2..b8e075bbab1cb 100644 --- a/packages/@aws-cdk/aws-cloudtrail/package.json +++ b/packages/@aws-cdk/aws-cloudtrail/package.json @@ -33,7 +33,8 @@ "lint": "cdk-lint", "test": "cdk-test", "pkglint": "pkglint -f", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "cdk-build": { "cloudformation": "AWS::CloudTrail" diff --git a/packages/@aws-cdk/aws-cloudwatch/package.json b/packages/@aws-cdk/aws-cloudwatch/package.json index a2f192265fdd3..c4ef501d64a47 100644 --- a/packages/@aws-cdk/aws-cloudwatch/package.json +++ b/packages/@aws-cdk/aws-cloudwatch/package.json @@ -34,7 +34,8 @@ "test": "cdk-test", "integ": "cdk-integ", "pkglint": "pkglint -f", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "cdk-build": { "cloudformation": "AWS::CloudWatch" diff --git a/packages/@aws-cdk/aws-codebuild/package.json b/packages/@aws-cdk/aws-codebuild/package.json index 91daf4fffa3d9..356862d08abea 100644 --- a/packages/@aws-cdk/aws-codebuild/package.json +++ b/packages/@aws-cdk/aws-codebuild/package.json @@ -34,7 +34,8 @@ "test": "cdk-test", "integ": "cdk-integ", "pkglint": "pkglint -f", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "cdk-build": { "cloudformation": "AWS::CodeBuild" diff --git a/packages/@aws-cdk/aws-codecommit/package.json b/packages/@aws-cdk/aws-codecommit/package.json index e011c48396b7d..98d1de35f1f78 100644 --- a/packages/@aws-cdk/aws-codecommit/package.json +++ b/packages/@aws-cdk/aws-codecommit/package.json @@ -34,7 +34,8 @@ "test": "cdk-test", "integ": "cdk-integ", "pkglint": "pkglint -f", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "cdk-build": { "cloudformation": "AWS::CodeCommit" diff --git a/packages/@aws-cdk/aws-codedeploy-api/package.json b/packages/@aws-cdk/aws-codedeploy-api/package.json index 76e148ef40aeb..e02b8c3dced0d 100644 --- a/packages/@aws-cdk/aws-codedeploy-api/package.json +++ b/packages/@aws-cdk/aws-codedeploy-api/package.json @@ -34,7 +34,8 @@ "test": "cdk-test", "integ": "cdk-integ", "pkglint": "pkglint -f", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "keywords": [ "aws", diff --git a/packages/@aws-cdk/aws-codedeploy/package.json b/packages/@aws-cdk/aws-codedeploy/package.json index 95218c6cd0a27..f28057db5731b 100644 --- a/packages/@aws-cdk/aws-codedeploy/package.json +++ b/packages/@aws-cdk/aws-codedeploy/package.json @@ -34,7 +34,8 @@ "test": "cdk-test", "integ": "cdk-integ", "pkglint": "pkglint -f", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "cdk-build": { "cloudformation": "AWS::CodeDeploy" @@ -81,5 +82,10 @@ }, "engines": { "node": ">= 8.10.0" + }, + "awslint": { + "exclude": [ + "construct-ctor:@aws-cdk/aws-codedeploy.ServerDeploymentGroupBase..params[2]" + ] } } diff --git a/packages/@aws-cdk/aws-codepipeline-api/package.json b/packages/@aws-cdk/aws-codepipeline-api/package.json index 8285616d847e3..3998d2b5fd1b4 100644 --- a/packages/@aws-cdk/aws-codepipeline-api/package.json +++ b/packages/@aws-cdk/aws-codepipeline-api/package.json @@ -34,7 +34,8 @@ "test": "cdk-test", "integ": "cdk-integ", "pkglint": "pkglint -f", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "keywords": [ "aws", @@ -70,5 +71,11 @@ }, "engines": { "node": ">= 8.10.0" + }, + "awslint": { + "exclude": [ + "construct-ctor:@aws-cdk/aws-codepipeline-api.Artifact..params[0]", + "construct-ctor:@aws-cdk/aws-codepipeline-api.Artifact..params[1]" + ] } } diff --git a/packages/@aws-cdk/aws-codepipeline/package.json b/packages/@aws-cdk/aws-codepipeline/package.json index 549c99ce07e87..f0249b590a9a0 100644 --- a/packages/@aws-cdk/aws-codepipeline/package.json +++ b/packages/@aws-cdk/aws-codepipeline/package.json @@ -34,7 +34,8 @@ "test": "cdk-test", "integ": "cdk-integ", "pkglint": "pkglint -f", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "cdk-build": { "cloudformation": "AWS::CodePipeline" @@ -89,5 +90,11 @@ }, "engines": { "node": ">= 8.10.0" + }, + "awslint": { + "exclude": [ + "construct-ctor:@aws-cdk/aws-codepipeline.CrossRegionScaffoldStack..params[0]", + "construct-ctor:@aws-cdk/aws-codepipeline.CrossRegionScaffoldStack..params[1]" + ] } } diff --git a/packages/@aws-cdk/aws-cognito/package.json b/packages/@aws-cdk/aws-cognito/package.json index f52fabbbc854e..e285692352c86 100644 --- a/packages/@aws-cdk/aws-cognito/package.json +++ b/packages/@aws-cdk/aws-cognito/package.json @@ -34,7 +34,8 @@ "test": "cdk-test", "integ": "cdk-integ", "pkglint": "pkglint -f", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "cdk-build": { "cloudformation": "AWS::Cognito" diff --git a/packages/@aws-cdk/aws-config/package.json b/packages/@aws-cdk/aws-config/package.json index 3b36df88af87b..69fee10b973ab 100644 --- a/packages/@aws-cdk/aws-config/package.json +++ b/packages/@aws-cdk/aws-config/package.json @@ -34,7 +34,8 @@ "test": "cdk-test", "integ": "cdk-integ", "pkglint": "pkglint -f", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "cdk-build": { "cloudformation": "AWS::Config" diff --git a/packages/@aws-cdk/aws-datapipeline/package.json b/packages/@aws-cdk/aws-datapipeline/package.json index c7ae0bb216f0a..90f0516eb8394 100644 --- a/packages/@aws-cdk/aws-datapipeline/package.json +++ b/packages/@aws-cdk/aws-datapipeline/package.json @@ -34,7 +34,8 @@ "test": "cdk-test", "integ": "cdk-integ", "pkglint": "pkglint -f", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "cdk-build": { "cloudformation": "AWS::DataPipeline" diff --git a/packages/@aws-cdk/aws-dax/package.json b/packages/@aws-cdk/aws-dax/package.json index 1609995e27c84..f01c994a94040 100644 --- a/packages/@aws-cdk/aws-dax/package.json +++ b/packages/@aws-cdk/aws-dax/package.json @@ -34,7 +34,8 @@ "test": "cdk-test", "integ": "cdk-integ", "pkglint": "pkglint -f", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "cdk-build": { "cloudformation": "AWS::DAX" diff --git a/packages/@aws-cdk/aws-directoryservice/package.json b/packages/@aws-cdk/aws-directoryservice/package.json index 3334bca86a8fe..6937576ef8aba 100644 --- a/packages/@aws-cdk/aws-directoryservice/package.json +++ b/packages/@aws-cdk/aws-directoryservice/package.json @@ -34,7 +34,8 @@ "test": "cdk-test", "integ": "cdk-integ", "pkglint": "pkglint -f", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "cdk-build": { "cloudformation": "AWS::DirectoryService" diff --git a/packages/@aws-cdk/aws-dlm/package.json b/packages/@aws-cdk/aws-dlm/package.json index 364ee94a35ef5..d453dc09e29d9 100644 --- a/packages/@aws-cdk/aws-dlm/package.json +++ b/packages/@aws-cdk/aws-dlm/package.json @@ -35,7 +35,8 @@ "package": "cdk-package", "pkglint": "pkglint -f", "test": "cdk-test", - "watch": "cdk-watch" + "watch": "cdk-watch", + "awslint": "cdk-awslint" }, "cdk-build": { "cloudformation": "AWS::DLM" diff --git a/packages/@aws-cdk/aws-dms/package.json b/packages/@aws-cdk/aws-dms/package.json index bff06730e9783..cb87037905226 100644 --- a/packages/@aws-cdk/aws-dms/package.json +++ b/packages/@aws-cdk/aws-dms/package.json @@ -34,7 +34,8 @@ "test": "cdk-test", "integ": "cdk-integ", "pkglint": "pkglint -f", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "cdk-build": { "cloudformation": "AWS::DMS" diff --git a/packages/@aws-cdk/aws-dynamodb/package.json b/packages/@aws-cdk/aws-dynamodb/package.json index 8cdc1c563bb0a..1398aeabfb9b1 100644 --- a/packages/@aws-cdk/aws-dynamodb/package.json +++ b/packages/@aws-cdk/aws-dynamodb/package.json @@ -34,7 +34,8 @@ "test": "cdk-test", "integ": "cdk-integ", "pkglint": "pkglint -f", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "cdk-build": { "cloudformation": "AWS::DynamoDB" diff --git a/packages/@aws-cdk/aws-ec2/package.json b/packages/@aws-cdk/aws-ec2/package.json index 690e56f765541..ab5f3163e6a30 100644 --- a/packages/@aws-cdk/aws-ec2/package.json +++ b/packages/@aws-cdk/aws-ec2/package.json @@ -34,7 +34,8 @@ "test": "cdk-test", "integ": "cdk-integ", "pkglint": "pkglint -f", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "cdk-build": { "cloudformation": "AWS::EC2" @@ -70,5 +71,10 @@ }, "engines": { "node": ">= 8.10.0" + }, + "awslint": { + "exclude": [ + "resource-attribute:@aws-cdk/aws-ec2.ISecurityGroup.securityGroupVpcId" + ] } } diff --git a/packages/@aws-cdk/aws-ecr/package.json b/packages/@aws-cdk/aws-ecr/package.json index 2e1187294817d..66ecea755a6e6 100644 --- a/packages/@aws-cdk/aws-ecr/package.json +++ b/packages/@aws-cdk/aws-ecr/package.json @@ -34,7 +34,8 @@ "test": "cdk-test", "integ": "cdk-integ", "pkglint": "pkglint -f", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "cdk-build": { "cloudformation": "AWS::ECR" @@ -73,5 +74,10 @@ }, "engines": { "node": ">= 8.10.0" + }, + "awslint": { + "exclude": [ + "import:@aws-cdk/aws-ecr.Repository" + ] } } diff --git a/packages/@aws-cdk/aws-ecs/package.json b/packages/@aws-cdk/aws-ecs/package.json index ae06ccb157d46..cb5e391e19407 100644 --- a/packages/@aws-cdk/aws-ecs/package.json +++ b/packages/@aws-cdk/aws-ecs/package.json @@ -34,7 +34,8 @@ "test": "cdk-test", "integ": "cdk-integ", "pkglint": "pkglint -f", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "cdk-build": { "cloudformation": "AWS::ECS" @@ -97,5 +98,13 @@ }, "engines": { "node": ">= 8.10.0" + }, + "awslint": { + "exclude": [ + "resource-attribute:@aws-cdk/aws-ecs.ICluster.clusterArn", + "construct-ctor:@aws-cdk/aws-ecs.BaseService.", + "construct-ctor:@aws-cdk/aws-ecs.ContainerDefinition.", + "construct-ctor:@aws-cdk/aws-ecs.LoadBalancedFargateServiceApplet..params[0]" + ] } } diff --git a/packages/@aws-cdk/aws-efs/package.json b/packages/@aws-cdk/aws-efs/package.json index d389108bf2e9a..0e1e0503cfbc1 100644 --- a/packages/@aws-cdk/aws-efs/package.json +++ b/packages/@aws-cdk/aws-efs/package.json @@ -34,7 +34,8 @@ "test": "cdk-test", "integ": "cdk-integ", "pkglint": "pkglint -f", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "cdk-build": { "cloudformation": "AWS::EFS" diff --git a/packages/@aws-cdk/aws-eks/package.json b/packages/@aws-cdk/aws-eks/package.json index 3c22bed75c055..60b99e7ba34a5 100644 --- a/packages/@aws-cdk/aws-eks/package.json +++ b/packages/@aws-cdk/aws-eks/package.json @@ -34,7 +34,8 @@ "test": "cdk-test", "integ": "cdk-integ", "pkglint": "pkglint -f", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "cdk-build": { "cloudformation": "AWS::EKS" diff --git a/packages/@aws-cdk/aws-elasticache/package.json b/packages/@aws-cdk/aws-elasticache/package.json index 84ce2d02d4370..db71b6ceea00e 100644 --- a/packages/@aws-cdk/aws-elasticache/package.json +++ b/packages/@aws-cdk/aws-elasticache/package.json @@ -34,7 +34,8 @@ "test": "cdk-test", "integ": "cdk-integ", "pkglint": "pkglint -f", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "cdk-build": { "cloudformation": "AWS::ElastiCache" diff --git a/packages/@aws-cdk/aws-elasticbeanstalk/package.json b/packages/@aws-cdk/aws-elasticbeanstalk/package.json index 96e0cfb9aef33..8485af3458425 100644 --- a/packages/@aws-cdk/aws-elasticbeanstalk/package.json +++ b/packages/@aws-cdk/aws-elasticbeanstalk/package.json @@ -34,7 +34,8 @@ "test": "cdk-test", "integ": "cdk-integ", "pkglint": "pkglint -f", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "cdk-build": { "cloudformation": "AWS::ElasticBeanstalk" diff --git a/packages/@aws-cdk/aws-elasticloadbalancing/package.json b/packages/@aws-cdk/aws-elasticloadbalancing/package.json index 358d15077b8c4..3db81523cd10c 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancing/package.json +++ b/packages/@aws-cdk/aws-elasticloadbalancing/package.json @@ -34,7 +34,8 @@ "test": "cdk-test", "integ": "cdk-integ", "pkglint": "pkglint -f", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "cdk-build": { "cloudformation": "AWS::ElasticLoadBalancing" diff --git a/packages/@aws-cdk/aws-elasticloadbalancingv2/package.json b/packages/@aws-cdk/aws-elasticloadbalancingv2/package.json index 0d3912a156f62..f0c32b4097d58 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2/package.json +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2/package.json @@ -34,7 +34,8 @@ "test": "cdk-test", "integ": "cdk-integ", "pkglint": "pkglint -f", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "cdk-build": { "cloudformation": "AWS::ElasticLoadBalancingV2" @@ -78,5 +79,12 @@ }, "engines": { "node": ">= 8.10.0" + }, + "awslint": { + "exclude": [ + "construct-ctor:@aws-cdk/aws-elasticloadbalancingv2.BaseListener..params[2]", + "construct-ctor:@aws-cdk/aws-elasticloadbalancingv2.BaseLoadBalancer.", + "construct-ctor:@aws-cdk/aws-elasticloadbalancingv2.TargetGroupBase." + ] } } diff --git a/packages/@aws-cdk/aws-elasticsearch/package.json b/packages/@aws-cdk/aws-elasticsearch/package.json index cdbda0225cd1e..17362517d46b4 100644 --- a/packages/@aws-cdk/aws-elasticsearch/package.json +++ b/packages/@aws-cdk/aws-elasticsearch/package.json @@ -34,7 +34,8 @@ "test": "cdk-test", "integ": "cdk-integ", "pkglint": "pkglint -f", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "cdk-build": { "cloudformation": "AWS::Elasticsearch" diff --git a/packages/@aws-cdk/aws-emr/package.json b/packages/@aws-cdk/aws-emr/package.json index 6160a1aaecac8..26d0f0d760ddc 100644 --- a/packages/@aws-cdk/aws-emr/package.json +++ b/packages/@aws-cdk/aws-emr/package.json @@ -34,7 +34,8 @@ "test": "cdk-test", "integ": "cdk-integ", "pkglint": "pkglint -f", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "cdk-build": { "cloudformation": "AWS::EMR" diff --git a/packages/@aws-cdk/aws-events/package.json b/packages/@aws-cdk/aws-events/package.json index fc76b39cc1b32..c090901545031 100644 --- a/packages/@aws-cdk/aws-events/package.json +++ b/packages/@aws-cdk/aws-events/package.json @@ -34,7 +34,8 @@ "test": "cdk-test", "integ": "cdk-integ", "pkglint": "pkglint -f", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "cdk-build": { "cloudformation": "AWS::Events" diff --git a/packages/@aws-cdk/aws-gamelift/package.json b/packages/@aws-cdk/aws-gamelift/package.json index 487ac5a15504b..63c5e341eb8f8 100644 --- a/packages/@aws-cdk/aws-gamelift/package.json +++ b/packages/@aws-cdk/aws-gamelift/package.json @@ -34,7 +34,8 @@ "test": "cdk-test", "integ": "cdk-integ", "pkglint": "pkglint -f", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "cdk-build": { "cloudformation": "AWS::GameLift" diff --git a/packages/@aws-cdk/aws-glue/package.json b/packages/@aws-cdk/aws-glue/package.json index 932ddbe62c020..a7aab0cc26e8a 100644 --- a/packages/@aws-cdk/aws-glue/package.json +++ b/packages/@aws-cdk/aws-glue/package.json @@ -34,7 +34,8 @@ "test": "cdk-test", "integ": "cdk-integ", "pkglint": "pkglint -f", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "cdk-build": { "cloudformation": "AWS::Glue" diff --git a/packages/@aws-cdk/aws-guardduty/package.json b/packages/@aws-cdk/aws-guardduty/package.json index a9242e8601f89..86dc0a10afadc 100644 --- a/packages/@aws-cdk/aws-guardduty/package.json +++ b/packages/@aws-cdk/aws-guardduty/package.json @@ -34,7 +34,8 @@ "test": "cdk-test", "integ": "cdk-integ", "pkglint": "pkglint -f", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "cdk-build": { "cloudformation": "AWS::GuardDuty" diff --git a/packages/@aws-cdk/aws-iam/package.json b/packages/@aws-cdk/aws-iam/package.json index f081646de9106..57e6a9f2d37c1 100644 --- a/packages/@aws-cdk/aws-iam/package.json +++ b/packages/@aws-cdk/aws-iam/package.json @@ -34,7 +34,8 @@ "test": "cdk-test", "pkglint": "pkglint -f", "integ": "cdk-integ", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "cdk-build": { "cloudformation": "AWS::IAM" diff --git a/packages/@aws-cdk/aws-inspector/package.json b/packages/@aws-cdk/aws-inspector/package.json index 6b6a4724b1df0..503088c8c3234 100644 --- a/packages/@aws-cdk/aws-inspector/package.json +++ b/packages/@aws-cdk/aws-inspector/package.json @@ -34,7 +34,8 @@ "test": "cdk-test", "integ": "cdk-integ", "pkglint": "pkglint -f", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "cdk-build": { "cloudformation": "AWS::Inspector" diff --git a/packages/@aws-cdk/aws-iot/package.json b/packages/@aws-cdk/aws-iot/package.json index ef4dc66f49cd3..991d3b4ba04c6 100644 --- a/packages/@aws-cdk/aws-iot/package.json +++ b/packages/@aws-cdk/aws-iot/package.json @@ -34,7 +34,8 @@ "test": "cdk-test", "integ": "cdk-integ", "pkglint": "pkglint -f", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "cdk-build": { "cloudformation": "AWS::IoT" diff --git a/packages/@aws-cdk/aws-iot1click/package.json b/packages/@aws-cdk/aws-iot1click/package.json index 9c56eb0d12c8d..6a15d49470199 100644 --- a/packages/@aws-cdk/aws-iot1click/package.json +++ b/packages/@aws-cdk/aws-iot1click/package.json @@ -35,7 +35,8 @@ "package": "cdk-package", "pkglint": "pkglint -f", "test": "cdk-test", - "watch": "cdk-watch" + "watch": "cdk-watch", + "awslint": "cdk-awslint" }, "cdk-build": { "cloudformation": "AWS::IoT1Click" diff --git a/packages/@aws-cdk/aws-iotanalytics/package.json b/packages/@aws-cdk/aws-iotanalytics/package.json index 5a6cf4b3bfc99..1386cdd8c906d 100644 --- a/packages/@aws-cdk/aws-iotanalytics/package.json +++ b/packages/@aws-cdk/aws-iotanalytics/package.json @@ -35,7 +35,8 @@ "package": "cdk-package", "pkglint": "pkglint -f", "test": "cdk-test", - "watch": "cdk-watch" + "watch": "cdk-watch", + "awslint": "cdk-awslint" }, "cdk-build": { "cloudformation": "AWS::IoTAnalytics" diff --git a/packages/@aws-cdk/aws-kinesis/package.json b/packages/@aws-cdk/aws-kinesis/package.json index 73af4770b3b8e..1737d2f2a329e 100644 --- a/packages/@aws-cdk/aws-kinesis/package.json +++ b/packages/@aws-cdk/aws-kinesis/package.json @@ -33,7 +33,8 @@ "lint": "cdk-lint", "test": "cdk-test", "pkglint": "pkglint -f", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "cdk-build": { "cloudformation": "AWS::Kinesis" diff --git a/packages/@aws-cdk/aws-kinesisanalytics/package.json b/packages/@aws-cdk/aws-kinesisanalytics/package.json index 54ae8dde60727..b4b6596a43fc7 100644 --- a/packages/@aws-cdk/aws-kinesisanalytics/package.json +++ b/packages/@aws-cdk/aws-kinesisanalytics/package.json @@ -34,7 +34,8 @@ "test": "cdk-test", "integ": "cdk-integ", "pkglint": "pkglint -f", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "cdk-build": { "cloudformation": "AWS::KinesisAnalytics" diff --git a/packages/@aws-cdk/aws-kinesisfirehose/package.json b/packages/@aws-cdk/aws-kinesisfirehose/package.json index 8b55ca992d718..42f9780408cf9 100644 --- a/packages/@aws-cdk/aws-kinesisfirehose/package.json +++ b/packages/@aws-cdk/aws-kinesisfirehose/package.json @@ -34,7 +34,8 @@ "test": "cdk-test", "integ": "cdk-integ", "pkglint": "pkglint -f", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "cdk-build": { "cloudformation": "AWS::KinesisFirehose" diff --git a/packages/@aws-cdk/aws-kms/package.json b/packages/@aws-cdk/aws-kms/package.json index 468bca559404b..8acfbfc402353 100644 --- a/packages/@aws-cdk/aws-kms/package.json +++ b/packages/@aws-cdk/aws-kms/package.json @@ -34,7 +34,8 @@ "test": "cdk-test", "pkglint": "pkglint -f", "integ": "cdk-integ", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "cdk-build": { "cloudformation": "AWS::KMS" diff --git a/packages/@aws-cdk/aws-lambda-event-sources/package.json b/packages/@aws-cdk/aws-lambda-event-sources/package.json index 2e6a0b293bcd2..ad9603dd81275 100644 --- a/packages/@aws-cdk/aws-lambda-event-sources/package.json +++ b/packages/@aws-cdk/aws-lambda-event-sources/package.json @@ -34,7 +34,8 @@ "test": "cdk-test", "integ": "cdk-integ", "pkglint": "pkglint -f", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "keywords": [ "aws", diff --git a/packages/@aws-cdk/aws-lambda/package.json b/packages/@aws-cdk/aws-lambda/package.json index 980f3833cca9c..74b6542f1bd15 100644 --- a/packages/@aws-cdk/aws-lambda/package.json +++ b/packages/@aws-cdk/aws-lambda/package.json @@ -34,7 +34,8 @@ "test": "cdk-test", "integ": "cdk-integ", "pkglint": "pkglint -f", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "cdk-build": { "cloudformation": "AWS::Lambda" diff --git a/packages/@aws-cdk/aws-logs/package.json b/packages/@aws-cdk/aws-logs/package.json index 79f31516bf47a..12527c729505d 100644 --- a/packages/@aws-cdk/aws-logs/package.json +++ b/packages/@aws-cdk/aws-logs/package.json @@ -34,7 +34,8 @@ "test": "cdk-test", "integ": "cdk-integ", "pkglint": "pkglint -f", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "cdk-build": { "cloudformation": "AWS::Logs" diff --git a/packages/@aws-cdk/aws-neptune/package.json b/packages/@aws-cdk/aws-neptune/package.json index b5052f7b7a197..e1e13650e9dd6 100644 --- a/packages/@aws-cdk/aws-neptune/package.json +++ b/packages/@aws-cdk/aws-neptune/package.json @@ -35,7 +35,8 @@ "package": "cdk-package", "pkglint": "pkglint -f", "test": "cdk-test", - "watch": "cdk-watch" + "watch": "cdk-watch", + "awslint": "cdk-awslint" }, "cdk-build": { "cloudformation": "AWS::Neptune" diff --git a/packages/@aws-cdk/aws-opsworks/package.json b/packages/@aws-cdk/aws-opsworks/package.json index 4ea78d2eb07dd..5d363912958ab 100644 --- a/packages/@aws-cdk/aws-opsworks/package.json +++ b/packages/@aws-cdk/aws-opsworks/package.json @@ -34,7 +34,8 @@ "test": "cdk-test", "integ": "cdk-integ", "pkglint": "pkglint -f", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "cdk-build": { "cloudformation": "AWS::OpsWorks" diff --git a/packages/@aws-cdk/aws-quickstarts/package.json b/packages/@aws-cdk/aws-quickstarts/package.json index 0fee9a436b033..3f6a55a076514 100644 --- a/packages/@aws-cdk/aws-quickstarts/package.json +++ b/packages/@aws-cdk/aws-quickstarts/package.json @@ -33,7 +33,8 @@ "lint": "cdk-lint", "pkglint": "pkglint -f", "test": "cdk-test", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "keywords": [ "aws", diff --git a/packages/@aws-cdk/aws-rds/package.json b/packages/@aws-cdk/aws-rds/package.json index 99114093abbd6..049f3c1953472 100644 --- a/packages/@aws-cdk/aws-rds/package.json +++ b/packages/@aws-cdk/aws-rds/package.json @@ -34,7 +34,8 @@ "test": "cdk-test", "integ": "cdk-integ", "pkglint": "pkglint -f", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "cdk-build": { "cloudformation": "AWS::RDS" diff --git a/packages/@aws-cdk/aws-redshift/package.json b/packages/@aws-cdk/aws-redshift/package.json index d6154e75e6855..6920fbd937764 100644 --- a/packages/@aws-cdk/aws-redshift/package.json +++ b/packages/@aws-cdk/aws-redshift/package.json @@ -34,7 +34,8 @@ "test": "cdk-test", "integ": "cdk-integ", "pkglint": "pkglint -f", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "cdk-build": { "cloudformation": "AWS::Redshift" diff --git a/packages/@aws-cdk/aws-route53/package.json b/packages/@aws-cdk/aws-route53/package.json index 09fece2d30e19..34482a57ed11a 100644 --- a/packages/@aws-cdk/aws-route53/package.json +++ b/packages/@aws-cdk/aws-route53/package.json @@ -34,7 +34,8 @@ "test": "cdk-test", "integ": "cdk-integ", "pkglint": "pkglint -f", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "cdk-build": { "cloudformation": "AWS::Route53" @@ -72,5 +73,11 @@ }, "engines": { "node": ">= 8.10.0" + }, + "awslint": { + "exclude": [ + "resource-attribute:@aws-cdk/aws-route53.IHostedZone.hostedZoneNameServers", + "resource-props:@aws-cdk/aws-route53.HostedZoneProps" + ] } } diff --git a/packages/@aws-cdk/aws-route53resolver/package.json b/packages/@aws-cdk/aws-route53resolver/package.json index 8de064d928dbd..5875e15290d93 100644 --- a/packages/@aws-cdk/aws-route53resolver/package.json +++ b/packages/@aws-cdk/aws-route53resolver/package.json @@ -35,7 +35,8 @@ "package": "cdk-package", "pkglint": "pkglint -f", "test": "cdk-test", - "watch": "cdk-watch" + "watch": "cdk-watch", + "awslint": "cdk-awslint" }, "cdk-build": { "cloudformation": "AWS::Route53Resolver" diff --git a/packages/@aws-cdk/aws-s3-deployment/package.json b/packages/@aws-cdk/aws-s3-deployment/package.json index de74af896b839..522737d367c28 100644 --- a/packages/@aws-cdk/aws-s3-deployment/package.json +++ b/packages/@aws-cdk/aws-s3-deployment/package.json @@ -34,7 +34,8 @@ "test": "cdk-test", "integ": "cdk-integ", "pkglint": "pkglint -f", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "cdk-build": { "pre": [ diff --git a/packages/@aws-cdk/aws-s3-notifications/package.json b/packages/@aws-cdk/aws-s3-notifications/package.json index 403bd2d2d6295..694c48a1824c8 100644 --- a/packages/@aws-cdk/aws-s3-notifications/package.json +++ b/packages/@aws-cdk/aws-s3-notifications/package.json @@ -34,7 +34,8 @@ "test": "cdk-test", "integ": "cdk-integ", "pkglint": "pkglint -f", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "keywords": [ "aws", diff --git a/packages/@aws-cdk/aws-s3/package.json b/packages/@aws-cdk/aws-s3/package.json index 83012ea8d9314..523508b3e7009 100644 --- a/packages/@aws-cdk/aws-s3/package.json +++ b/packages/@aws-cdk/aws-s3/package.json @@ -34,7 +34,8 @@ "test": "cdk-test", "integ": "cdk-integ", "pkglint": "pkglint -f", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "cdk-build": { "cloudformation": "AWS::S3" @@ -75,5 +76,14 @@ }, "engines": { "node": ">= 8.10.0" + }, + "awslint": { + "exclude": [ + "resource-attribute:@aws-cdk/aws-s3.IBucket.bucketDomainName", + "resource-attribute:@aws-cdk/aws-s3.IBucket.bucketDualStackDomainName", + "resource-attribute:@aws-cdk/aws-s3.IBucket.bucketRegionalDomainName", + "resource-attribute:@aws-cdk/aws-s3.IBucket.bucketWebsiteUrl", + "resource-interface:@aws-cdk/aws-s3.IBucketPolicy" + ] } } diff --git a/packages/@aws-cdk/aws-sagemaker/package.json b/packages/@aws-cdk/aws-sagemaker/package.json index b7b3294b6d003..32e6ad4bb3fbf 100644 --- a/packages/@aws-cdk/aws-sagemaker/package.json +++ b/packages/@aws-cdk/aws-sagemaker/package.json @@ -35,7 +35,8 @@ "package": "cdk-package", "pkglint": "pkglint -f", "test": "cdk-test", - "watch": "cdk-watch" + "watch": "cdk-watch", + "awslint": "cdk-awslint" }, "cdk-build": { "cloudformation": "AWS::SageMaker" diff --git a/packages/@aws-cdk/aws-sdb/package.json b/packages/@aws-cdk/aws-sdb/package.json index f13f99bc89401..a7d5f9e1567a9 100644 --- a/packages/@aws-cdk/aws-sdb/package.json +++ b/packages/@aws-cdk/aws-sdb/package.json @@ -34,7 +34,8 @@ "test": "cdk-test", "integ": "cdk-integ", "pkglint": "pkglint -f", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "cdk-build": { "cloudformation": "AWS::SDB" diff --git a/packages/@aws-cdk/aws-secretsmanager/package.json b/packages/@aws-cdk/aws-secretsmanager/package.json index de44e132ac10c..f682086ddbe90 100644 --- a/packages/@aws-cdk/aws-secretsmanager/package.json +++ b/packages/@aws-cdk/aws-secretsmanager/package.json @@ -35,7 +35,8 @@ "package": "cdk-package", "pkglint": "pkglint -f", "test": "cdk-test", - "watch": "cdk-watch" + "watch": "cdk-watch", + "awslint": "cdk-awslint" }, "cdk-build": { "cloudformation": "AWS::SecretsManager" diff --git a/packages/@aws-cdk/aws-serverless/package.json b/packages/@aws-cdk/aws-serverless/package.json index b629d0a01321d..018cb4139c9da 100644 --- a/packages/@aws-cdk/aws-serverless/package.json +++ b/packages/@aws-cdk/aws-serverless/package.json @@ -35,7 +35,8 @@ "package": "cdk-package", "pkglint": "pkglint -f", "test": "cdk-test", - "watch": "cdk-watch" + "watch": "cdk-watch", + "awslint": "cdk-awslint" }, "cdk-build": { "cloudformation": "AWS::Serverless" diff --git a/packages/@aws-cdk/aws-servicecatalog/package.json b/packages/@aws-cdk/aws-servicecatalog/package.json index 3196efdc750b2..f86e47e1d438f 100644 --- a/packages/@aws-cdk/aws-servicecatalog/package.json +++ b/packages/@aws-cdk/aws-servicecatalog/package.json @@ -34,7 +34,8 @@ "test": "cdk-test", "integ": "cdk-integ", "pkglint": "pkglint -f", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "cdk-build": { "cloudformation": "AWS::ServiceCatalog" diff --git a/packages/@aws-cdk/aws-servicediscovery/package.json b/packages/@aws-cdk/aws-servicediscovery/package.json index a79a7acd326c9..cadbbf6f2d99e 100644 --- a/packages/@aws-cdk/aws-servicediscovery/package.json +++ b/packages/@aws-cdk/aws-servicediscovery/package.json @@ -34,7 +34,8 @@ "test": "cdk-test", "integ": "cdk-integ", "pkglint": "pkglint -f", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "cdk-build": { "cloudformation": "AWS::ServiceDiscovery" diff --git a/packages/@aws-cdk/aws-ses/package.json b/packages/@aws-cdk/aws-ses/package.json index 22f85cedc0b29..d01b5b192087a 100644 --- a/packages/@aws-cdk/aws-ses/package.json +++ b/packages/@aws-cdk/aws-ses/package.json @@ -34,7 +34,8 @@ "test": "cdk-test", "integ": "cdk-integ", "pkglint": "pkglint -f", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "cdk-build": { "cloudformation": "AWS::SES" diff --git a/packages/@aws-cdk/aws-sns/package.json b/packages/@aws-cdk/aws-sns/package.json index 4bc59e9531d0d..d557def1ace01 100644 --- a/packages/@aws-cdk/aws-sns/package.json +++ b/packages/@aws-cdk/aws-sns/package.json @@ -35,7 +35,8 @@ "test": "cdk-test", "integ": "cdk-integ", "pkglint": "pkglint -f", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "cdk-build": { "cloudformation": "AWS::SNS" diff --git a/packages/@aws-cdk/aws-sqs/package.json b/packages/@aws-cdk/aws-sqs/package.json index de0a3286571fa..4cd5ed45af113 100644 --- a/packages/@aws-cdk/aws-sqs/package.json +++ b/packages/@aws-cdk/aws-sqs/package.json @@ -34,7 +34,8 @@ "test": "cdk-test", "integ": "cdk-integ", "pkglint": "pkglint -f", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "cdk-build": { "cloudformation": "AWS::SQS" @@ -77,5 +78,10 @@ }, "engines": { "node": ">= 8.10.0" + }, + "awslint": { + "exclude": [ + "resource-attribute:@aws-cdk/aws-sqs.IQueue.queueName" + ] } } diff --git a/packages/@aws-cdk/aws-ssm/package.json b/packages/@aws-cdk/aws-ssm/package.json index 4bf110a86a34b..bba7ae006a162 100644 --- a/packages/@aws-cdk/aws-ssm/package.json +++ b/packages/@aws-cdk/aws-ssm/package.json @@ -34,7 +34,8 @@ "test": "cdk-test", "integ": "cdk-integ", "pkglint": "pkglint -f", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "cdk-build": { "cloudformation": "AWS::SSM" diff --git a/packages/@aws-cdk/aws-stepfunctions/package.json b/packages/@aws-cdk/aws-stepfunctions/package.json index 0a860b544586d..1c15ec1089cdc 100644 --- a/packages/@aws-cdk/aws-stepfunctions/package.json +++ b/packages/@aws-cdk/aws-stepfunctions/package.json @@ -34,7 +34,8 @@ "test": "cdk-test", "integ": "cdk-integ", "pkglint": "pkglint -f", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "cdk-build": { "cloudformation": "AWS::StepFunctions" @@ -73,5 +74,10 @@ }, "engines": { "node": ">= 8.10.0" + }, + "awslint": { + "exclude": [ + "resource-attribute:@aws-cdk/aws-stepfunctions.IStateMachine.stateMachineName" + ] } } diff --git a/packages/@aws-cdk/aws-waf/package.json b/packages/@aws-cdk/aws-waf/package.json index 616c06ab7816b..40efdd860d07e 100644 --- a/packages/@aws-cdk/aws-waf/package.json +++ b/packages/@aws-cdk/aws-waf/package.json @@ -34,7 +34,8 @@ "test": "cdk-test", "integ": "cdk-integ", "pkglint": "pkglint -f", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "cdk-build": { "cloudformation": "AWS::WAF" diff --git a/packages/@aws-cdk/aws-wafregional/package.json b/packages/@aws-cdk/aws-wafregional/package.json index 2e2e95962ed5c..7b0e85664a697 100644 --- a/packages/@aws-cdk/aws-wafregional/package.json +++ b/packages/@aws-cdk/aws-wafregional/package.json @@ -34,7 +34,8 @@ "test": "cdk-test", "integ": "cdk-integ", "pkglint": "pkglint -f", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "cdk-build": { "cloudformation": "AWS::WAFRegional" diff --git a/packages/@aws-cdk/aws-workspaces/package.json b/packages/@aws-cdk/aws-workspaces/package.json index 7fa19bbb59350..a0c9b3abbe82a 100644 --- a/packages/@aws-cdk/aws-workspaces/package.json +++ b/packages/@aws-cdk/aws-workspaces/package.json @@ -34,7 +34,8 @@ "test": "cdk-test", "integ": "cdk-integ", "pkglint": "pkglint -f", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "cdk-build": { "cloudformation": "AWS::WorkSpaces" diff --git a/packages/@aws-cdk/cdk/package.json b/packages/@aws-cdk/cdk/package.json index c026bb60d2c2e..1461494506fc5 100644 --- a/packages/@aws-cdk/cdk/package.json +++ b/packages/@aws-cdk/cdk/package.json @@ -27,13 +27,21 @@ "type": "git", "url": "https://github.com/awslabs/aws-cdk.git" }, + "awslint": { + "exclude": [ + "construct-ctor:@aws-cdk/cdk.App.", + "construct-ctor:@aws-cdk/cdk.Root.", + "construct-ctor:@aws-cdk/cdk.Stack..params*" + ] + }, "scripts": { "build": "cdk-build", "watch": "cdk-watch", "lint": "cdk-lint", "test": "cdk-test", "pkglint": "pkglint -f", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "nyc": { "lines": 55, diff --git a/packages/@aws-cdk/cx-api/package.json b/packages/@aws-cdk/cx-api/package.json index ba2705505ed12..1f29869c88486 100644 --- a/packages/@aws-cdk/cx-api/package.json +++ b/packages/@aws-cdk/cx-api/package.json @@ -29,7 +29,8 @@ "lint": "cdk-lint", "test": "cdk-test", "pkglint": "pkglint -f", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "author": { "name": "Amazon Web Services", diff --git a/packages/@aws-cdk/runtime-values/package.json b/packages/@aws-cdk/runtime-values/package.json index 89e66158af901..69c4cc442c7f7 100644 --- a/packages/@aws-cdk/runtime-values/package.json +++ b/packages/@aws-cdk/runtime-values/package.json @@ -34,7 +34,8 @@ "test": "cdk-test", "pkglint": "pkglint -f", "integ": "cdk-integ", - "package": "cdk-package" + "package": "cdk-package", + "awslint": "cdk-awslint" }, "keywords": [ "aws", diff --git a/tools/awslint/.gitignore b/tools/awslint/.gitignore new file mode 100644 index 0000000000000..a2e520b801389 --- /dev/null +++ b/tools/awslint/.gitignore @@ -0,0 +1,95 @@ +*.js +*.d.ts +jsii + +# Created by https://www.gitignore.io/api/node +# Edit at https://www.gitignore.io/?templates=node + +### Node ### +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# TypeScript v1 declaration files +typings/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env + +# parcel-bundler cache (https://parceljs.org/) +.cache + +# next.js build output +.next + +# nuxt.js build output +.nuxt + +# vuepress build output +.vuepress/dist + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +#DynamoDB Local files +.dynamodb/ + +# End of https://www.gitignore.io/api/node + +dist +.LAST_PACKAGE +.LAST_BUILD +*.snk +.nycrc \ No newline at end of file diff --git a/tools/awslint/.npmignore b/tools/awslint/.npmignore new file mode 100644 index 0000000000000..bc9fd0e49f9a1 --- /dev/null +++ b/tools/awslint/.npmignore @@ -0,0 +1,5 @@ + +dist +.LAST_PACKAGE +.LAST_BUILD +*.snk \ No newline at end of file diff --git a/tools/awslint/LICENSE b/tools/awslint/LICENSE new file mode 100644 index 0000000000000..46c185646b439 --- /dev/null +++ b/tools/awslint/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2018-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/tools/awslint/NOTICE b/tools/awslint/NOTICE new file mode 100644 index 0000000000000..8585168af8b7d --- /dev/null +++ b/tools/awslint/NOTICE @@ -0,0 +1,2 @@ +AWS Cloud Development Kit (AWS CDK) +Copyright 2018-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. diff --git a/tools/awslint/README.md b/tools/awslint/README.md new file mode 100644 index 0000000000000..fc5511dbe593c --- /dev/null +++ b/tools/awslint/README.md @@ -0,0 +1,130 @@ +# awslint + +A linter for the AWS Construct Library's API. It reflects a construct library's +module via it's `.jsii` manifest and checks that the module adheres to the [AWS +Resource Construct Design Guidelines](../../design/aws-guidelines.md). + +## Usage + +First, the module must be built through `jsii` (the linter reads the `.jsii` +manifest). + +Now, if you simply run `awslint` from a module directory, you will +see a list of diagnostic messages. + +For example: + +```console +$ cd @aws-cdk/aws-sns +$ awslint +@aws-cdk/aws-sns.ISubscription -- warning: every resource must have a resource interface [resource-interface] +@aws-cdk/aws-sns.ITopicPolicy -- warning: every resource must have a resource interface [resource-interface] +``` + +Each diagnostics includes the following elements: + +``` +@aws-cdk/aws-sns.ISubscription -- warning: every resource must have a resource interface [resource-interface] +[----------------------------] [------] [-------------------------------------------] [------------------] + ^ ^ ^ ^ + | | | | + scope level message rule +``` + +## Options and Configuration + +`awslint` accepts options either through the command-line (i.e. `--debug`) or +via an `awslint` key in the module's `package.json`. + +```json +{ + "awslint": { + "debug": true + } +} +``` + +## Include/Exclude + +The `--include` and `--exclude` options can be used to specify which rules will +be evaluated. + +For example: + +```console +# evaluate only the "resource-props" and "import" rules in all scopes +$ awslint -i resource-props -i import + +# evaluate only the "import" rule in all scopes besides ones that begin with "@aws-cdk/aws-s3" +$ awslint -i import -x "*:@aws-cdk/aws-s3*" +``` + + +Filters are specified using the following pattern: + + rule[*][:scope[*]] + +If a `"*"` suffix is provided, the component is treated as a prefix. Otherwise, +it is evaluated as a full match. + +If scope is not specified, all scopes are implied (`*`). + +Examples: + +* `*:*` - matches all rules in all scopes +* `*:@aws-cdk/aws-apigateway.IRestApi*` - matches all rules in scopes that begin with `@aws-cdk/aws-apigateway.IRestApi` +* `resource-props` - matches the `resource-props` rule in all scopes +* `resource-*` - matches all rules that begin with `resource-`. +* `resource-*:@aws-cdk/aws-sns*` - matches all `resource-` rules with scope that + begins with `@aws-cdk/aws-sns`. + +When a rule is excluded, it will be displayed as `skipped:` in the output and +will always considered to pass. + +## Saving State + +The `--save` option can be used to capture all failed linting rules and save them as `exclude`s +in the module's `package.json` file. This is useful to bootstrap the linting process and \ +progressively fix errors. + +```console +$ awslint --save +[shows errors] + +$ cat package.json +{ + ... + "awslint": { + "exclude": [ + "...", // added by awslint --save + "...", // added by awslint --save + ] + } +} + +$ awslint +[no errors] +``` + +If `--save` is specified, `awslint` will always exit with status code 0. + +## Output Options + +* Use `--verbose` to print all passing linter rules (disabled by default). +* Use `--quiet` to hide all warnings and skips (just prints errors) + +## Listing Rules + +To list all linter rules: + +```console +$ awslint list +module-name: module name must be @aws-cdk/aws- +construct-ctor: signature of all construct constructors should be "scope, id, props" +resource-class: every resource must have a resource class (L2) +... +``` + +The [AWS Resource Construct Design Guidelines](../../design/aws-guidelines.md) document +includes references for all rules. For example, see [#resource-class](../../design/aws-guidelines.md#resource-class) +for a discussion about the "resource-class" rule. diff --git a/tools/awslint/bin/awslint b/tools/awslint/bin/awslint new file mode 100755 index 0000000000000..6aa8e684ff117 --- /dev/null +++ b/tools/awslint/bin/awslint @@ -0,0 +1,2 @@ +#!/usr/bin/env node +require('./awslint.js'); \ No newline at end of file diff --git a/tools/awslint/bin/awslint.ts b/tools/awslint/bin/awslint.ts new file mode 100644 index 0000000000000..1fbb08f94df2f --- /dev/null +++ b/tools/awslint/bin/awslint.ts @@ -0,0 +1,254 @@ +// tslint:disable:max-line-length +import child_process = require('child_process'); +import colors = require('colors'); +import fs = require('fs-extra'); +import reflect = require('jsii-reflect'); +import path = require('path'); +import yargs = require('yargs'); +import { constructLinter, DiagnosticLevel, moduleLinter, resourceLinter } from '../lib'; + +const LINTERS = [ moduleLinter, constructLinter, resourceLinter ]; + +async function main() { + const argv = yargs + .usage('awslint [options] [command]') + .showHelpOnFail(true) + .command('', 'lint the current module (default)') + .command('list', 'list all available rules') + .option('include', { alias: 'i', type: 'array', desc: 'evaluate only this rule(s)', default: [ '*' ] }) + .option('exclude', { alias: 'x', type: 'array', desc: 'do not evaludate these rules (takes priority over --include)', default: [ ] }) + .option('save', { type: 'boolean', desc: 'updates package.json with "exclude" statements for all failing rules', default: false }) + .option('verbose', { alias: 'v', type: 'boolean', desc: 'verbose output (prints all assertions)', default: false }) + .option('quiet', { alias: 'q', type: 'boolean', desc: 'quiet mode - shows only errors', default: false }) + .option('force', { type: 'boolean', desc: 'succeed silently if this is not a jsii module', default: true }) + .option('config', { type: 'boolean', desc: 'reads options from the "awslint" section in package.json', default: true }) + .option('debug', { type: 'boolean', desc: 'debug output', default: false }) + .option('compile', { alias: 'c', type: 'boolean', desc: 'always run the jsii compiler (use "--no-compile" to never run the compiler, even if .jsii doesn\'t exist)' }) + .group('include', 'Filtering') + .group('exclude', 'Filtering') + .group('config', 'Configuration') + .group('save', 'Configuration') + .group('verbose', 'Output') + .group('quiet', 'Output') + .group('debug', 'Output') + .group('compile', 'Build') + .example('awslint', 'lints the current module against all rules') + .example('awslint -v -i "resource*" -i "import*"', 'lints against all rules that start with "resource" or "import" and print successes') + .example('awslint -x "*:@aws-cdk/aws-s3*"', 'evaluate all rules in all scopes besides ones that begin with "@aws-cdk/aws-s3"') + .example('awslint --save', 'updated "package.json" with "exclude"s for all failing rules'); + + if (!process.stdout.isTTY) { + colors.disable(); + } + + const args = argv.argv; + + if (args._.length > 1) { + argv.showHelp(); + process.exit(1); + } + + const command = args._[0] || 'lint'; + const workdir = process.cwd(); + + const config = path.join(workdir, 'package.json'); + + if (command === 'list') { + for (const linter of LINTERS) { + for (const rule of linter.rules) { + console.info(`${colors.cyan(rule.code)}: ${rule.message}`); + } + } + return; + } + + // if no package.json and force is true (default), just don't do anything + if (!await fs.pathExists(config) && args.force) { + return; + } + + const pkg = await fs.readJSON(config); + + // if this is not a jsii module we have nothing to look for + if (!pkg.jsii) { + if (args.force) { + return; // just silently succeed + } + + throw new Error(`Module in ${workdir} is not a jsii module (no "jsii" section in package.json)`); + } + + // if package.json contains a `jsii` section but there is no .jsii file + // it means we haven't compiled the module. + if (await shouldCompile()) { + await shell('jsii'); + } + + // read "awslint" from package.json + if (args.config) { + mergeOptions(args, pkg.awslint); + } + + if (args.debug) { + console.error('command: ' + command); + console.error('options: ' + JSON.stringify(args, undefined, 2)); + } + + if (command === 'lint') { + const assembly = await loadModule(workdir); + if (!assembly) { + return; + } + + const excludesToSave = new Array(); + let errors = 0; + + for (const linter of LINTERS) { + const results = linter.eval(assembly, { + include: args.include, + exclude: args.exclude, + }); + + // process results + + for (const diag of results) { + let color; + switch (diag.level) { + case DiagnosticLevel.Success: + if (args.verbose) { + color = colors.gray; + } + break; + case DiagnosticLevel.Error: + errors++; + color = colors.red; + if (args.save) { + excludesToSave.push(`${diag.rule.code}:${diag.scope}`); + } + break; + case DiagnosticLevel.Warning: + if (!args.quiet) { + color = colors.yellow; + } + break; + case DiagnosticLevel.Skipped: + if (!args.quiet) { + color = colors.blue; + } + break; + } + + if (color) { + console.error(color(`${colors.bold(diag.scope)} -- ${DiagnosticLevel[diag.level].toLowerCase()}: ${diag.message} [${diag.rule.code}]`)); + } + } + } + + if (excludesToSave.length > 0) { + if (!pkg.awslint) { + pkg.awslint = { }; + } + + if (!pkg.awslint.exclude) { + pkg.awslint.exclude = []; + } + + const excludes: string[] = pkg.awslint.exclude; + + for (const exclude of excludesToSave) { + if (excludes.indexOf(exclude) === -1) { + excludes.push(exclude); + } + } + + if (excludes.length > 0) { + await fs.writeJSON(config, pkg, { spaces: 2 }); + } + } + + if (errors && !args.save) { + process.exit(1); + } + + return; + } + + argv.showHelp(); + throw new Error(`Invalid command: ${command}`); + + async function shouldCompile() { + + // if --compile is explicitly enabled then just compile always + if (args.compile === true) { + return true; + } + + // if we have a .jsii file and we are not forced to compile, then don't compile + if (await fs.pathExists(path.join(workdir, '.jsii'))) { + return false; + } + + // we don't have a .jsii file, and --no-compile is explicily set, then it's an error + if (args.compile === false) { + throw new Error(`No .jsii file and --no-compile is set`); + } + + // compile! + return true; + } + +} + +main().catch(e => { + console.error(colors.red(e.message)); + process.exit(1); +}); + +async function loadModule(dir: string) { + const ts = new reflect.TypeSystem(); + await ts.load(dir); + if (ts.roots.length !== 1) { + throw new Error(`Expecting only a single root assembly`); + } + + return ts.roots[0]; +} + +function mergeOptions(dest: any, pkg?: any) { + if (!pkg) { return; } // no options in package.json + + for (const [key, value] of Object.entries(pkg)) { + + // if this is an array option, then add values to destination + if (Array.isArray(value)) { + const arr = dest[key] || [ ]; + arr.push(...value); + dest[key] = arr; + continue; + } + + // objects are not allowed + if (typeof value === 'object') { + throw new Error(`Invalid option "${key}" in package.json: ${JSON.stringify(value)}`); + } + + // primitives simply override + dest[key] = value; + } + + return dest; +} + +async function shell(command: string) { + const child = child_process.spawn(command, { stdio: [ 'inherit', 'inherit', 'inherit' ]}); + return new Promise((ok, ko) => { + child.once('exit', status => { + if (status === 0) { + return ok(); + } else { + return ko(new Error(`${command} exited with status ${status}`)); + } + }); + child.once('error', ko); + }); +} \ No newline at end of file diff --git a/tools/awslint/lib/cfn-resources.ts b/tools/awslint/lib/cfn-resources.ts new file mode 100644 index 0000000000000..838a56978b9d5 --- /dev/null +++ b/tools/awslint/lib/cfn-resources.ts @@ -0,0 +1,83 @@ +import reflect = require('jsii-reflect'); +import { isConstruct } from './util'; + +export interface CfnResourceSpec { + /** + * The full AWS CloudFormation name of the resource (i.e. `AWS::S3::Bucket`) + */ + fullname: string; + + /** + * The AWS CloudFormation namespace of the resource (i.e. `AWS::S3`) + */ + namespace: string; + + /** + * The base name of the resource (i.e. `Bucket`) + */ + basename: string; + + /** + * The documentation link + */ + doc: string; + + /** + * The set of CloudFormation attributes this resource has (i.e. `bucketArn`, `queueUrl`) + */ + attributes: string[]; +} + +/** + * Given a jsii assembly, extracts all CloudFormation resources from the module. + * @param a + */ +export function findCfnResources(assembly: reflect.Assembly): CfnResourceSpec[] { + + return assembly.classes.filter(c => isCfnResource(c)).map(layer1 => { + const basename = layer1.name.substr('Cfn'.length); + const doc = layer1.docs.docs.link || ''; + + // HACK: extract full CFN name from initializer docs + const initializerDoc = (layer1.initializer && layer1.initializer.docs.docs.comment) || ''; + const out = /Creates a new ``([^`]+)``/.exec(initializerDoc); + const fullname = out && out[1]; + if (!fullname) { + throw new Error('Unable to extract CloudFormation resource name from initializer documentation'); + } + + const namespace = fullname.split('::').slice(0, 2).join('::'); + + const resource: CfnResourceSpec = { + namespace, + fullname, + doc, + basename, + attributes: parseResourceAttributes(layer1) + }; + + return resource; + }); + + function parseResourceAttributes(cfnResourceClass: reflect.ClassType) { + return cfnResourceClass.properties.filter(p => p.docs.docs.cloudformation_attribute).map(p => p.name); + } + + function isCfnResource(c: reflect.ClassType) { + const resourceBaseClass = c.system.findFqn('@aws-cdk/cdk.Resource'); + + if (!isConstruct(c)) { + return false; + } + + if (c.base !== resourceBaseClass) { + return false; + } + + if (!c.name.startsWith('Cfn')) { + return false; + } + + return true; + } +} diff --git a/tools/awslint/lib/index.ts b/tools/awslint/lib/index.ts new file mode 100644 index 0000000000000..6c12fbf0e3c6e --- /dev/null +++ b/tools/awslint/lib/index.ts @@ -0,0 +1,2 @@ +export * from './linter'; +export * from './rules'; diff --git a/tools/awslint/lib/linter.ts b/tools/awslint/lib/linter.ts new file mode 100644 index 0000000000000..76fff07bd1d6d --- /dev/null +++ b/tools/awslint/lib/linter.ts @@ -0,0 +1,234 @@ +import reflect = require('jsii-reflect'); + +export interface LinterOptions { + /** + * List of rules to include. + * @default all rules + */ + include?: string[]; + + /** + * List of rules to exclude (takes precedence on "include") + * @default none + */ + exclude?: string[]; +} + +/** + * Evaluates a bunch of rules against some context. + */ +export class Linter { + private readonly _rules: { [name: string]: Rule } = { }; + + constructor(private readonly init: (assembly: reflect.Assembly) => T | T[] | undefined) { + return; + } + + public get rules() { + return Object.values(this._rules); + } + + /** + * Install another rule. + */ + public add(rule: Rule) { + if (rule.code in this._rules) { + throw new Error(`rule "${rule.code}" already exists`); + } + + this._rules[rule.code] = rule; + } + + /** + * Evaluate all rules against the context. + */ + public eval(assembly: reflect.Assembly, options: LinterOptions | undefined): Array> { + options = options || { }; + + let ctxs = this.init(assembly); + if (!ctxs) { + return []; // skip + } + + if (!Array.isArray(ctxs)) { + ctxs = [ ctxs ]; + } + + const results = new Array>(); + + for (const ctx of ctxs) { + for (const rule of Object.values(this._rules)) { + const evaluation = new Evaluation(ctx, rule, options); + rule.eval(evaluation); + results.push(...evaluation.diagnostics); + } + } + + return results; + } +} + +/** + * Passed in to each rule during evaluation. + */ +export class Evaluation { + public readonly ctx: T; + public readonly options: LinterOptions; + public diagnostics = new Array>(); + private readonly curr: Rule; + + constructor(ctx: T, rule: Rule, options: LinterOptions) { + this.ctx = ctx; + this.options = options; + this.curr = rule; + } + + public assert(condition: any, scope: string, extra?: string): condition is true { + const include = this.shouldEvaluate(this.curr.code, scope); + const message = this.curr.message + (extra || ''); + + let level: DiagnosticLevel; + if (!include) { + level = DiagnosticLevel.Skipped; + } else if (condition) { + level = DiagnosticLevel.Success; + } else if (this.curr.warning) { + level = DiagnosticLevel.Warning; + } else { + level = DiagnosticLevel.Error; + } + + const diag: Diagnostic = { + level, + ctx: this.ctx, + rule: this.curr, + scope, + message, + }; + + this.diagnostics.push(diag); + + return condition; + } + + public assertEquals(actual: any, expected: any, scope: string) { + return this.assert(actual === expected, scope, ` (expected="${expected}",actual="${actual}")`); + } + + public assertSignature(method: reflect.Method, expectations: MethodSignatureExpectations) { + const scope = method.parentType.fqn + '.' + method.name; + if (expectations.returns) { + this.assertEquals(expectations.returns.toString(), expectations.returns, scope); + } + + if (expectations.parameters) { + const expectedCount = expectations.parameters.length; + const actualCount = method.parameters.length; + if (this.assertEquals(actualCount, expectedCount, scope)) { + for (let i = 0; i < expectations.parameters.length; ++i) { + const expect = expectations.parameters[i]; + const actual = method.parameters[i]; + const pscope = scope + `.params[${i}]`; + if (expect.name) { + const expectedName = expect.name; + const actualName = actual.name; + this.assertEquals(actualName, expectedName, pscope); + } + if (expect.type) { + const expectedType = expect.type; + const actualType = (() => { + if (actual.type.fqn) { + return actual.type.fqn.fqn; + } + if (actual.type.primitive) { + return actual.type.primitive; + } + return actual.type.toString(); + })(); + + this.assertEquals(actualType, expectedType, pscope); + } + } + } + } + } + + /** + * Evaluates whether the rule should be evaluated based on the filters applied. + */ + private shouldEvaluate(code: string, scope: string) { + if (!this.options.include || this.options.include.length === 0) { + return true; + } + + for (const include of this.options.include) { + // match include + if (matchRule(include)) { + for (const exclude of this.options.exclude || []) { + // match exclude + if (matchRule(exclude)) { + return false; + } + } + return true; + } + } + + return false; + + function matchRule(filter: string) { + if (filter.indexOf(':') === -1) { + filter += ':*'; // add "*" scope filter if there isn't one + } + + // filter format is "code:scope" and both support "*" suffix to indicate startsWith + const [ codeFilter, scopeFilter ] = filter.split(':'); + return matchPattern(code, codeFilter) && matchPattern(scope, scopeFilter); + } + + function matchPattern(s: string, pattern: string) { + if (pattern.endsWith('*')) { + const prefix = pattern.substr(0, pattern.length - 1); + return s.startsWith(prefix); + } else { + return s === pattern; + } + } + } + +} + +export interface Rule { + code: string, + message: string; + warning?: boolean; + eval(linter: Evaluation): void; +} + +export interface MethodSignatureParameterExpectation { + name?: string; + type?: string; + + /** should this param be optional? */ + optional?: boolean; +} + +export interface MethodSignatureExpectations { + parameters?: MethodSignatureParameterExpectation[]; + returns?: string; +} + +export enum DiagnosticLevel { + Skipped, + Success, + Error, + Warning, +} + +export interface Diagnostic { + ctx: T; + level: DiagnosticLevel; + rule: Rule + scope: string; + message: string; +} diff --git a/tools/awslint/lib/rules/construct.ts b/tools/awslint/lib/rules/construct.ts new file mode 100644 index 0000000000000..91824605bcd0b --- /dev/null +++ b/tools/awslint/lib/rules/construct.ts @@ -0,0 +1,51 @@ +import reflect = require('jsii-reflect'); +import { Linter, MethodSignatureParameterExpectation } from '../linter'; +import { CONSTRUCT_FQN, isConstruct } from '../util'; + +interface ConstructLinterContext { + readonly construct: reflect.ClassType; +} + +export const constructLinter = new Linter(assembly => assembly.classes + .filter(t => isConstruct(t)) + .map(construct => ({ construct }))); + +constructLinter.add({ + code: 'construct-ctor', + message: 'signature of all construct constructors should be "scope, id, props"', + eval: e => { + // only applies to non abstract classes + if (e.ctx.construct.abstract) { + return; + } + + const initializer = e.ctx.construct.initializer; + if (!e.assert(initializer, e.ctx.construct.fqn)) { + return; + } + + const expectedParams = new Array(); + + expectedParams.push({ + name: 'scope', + type: CONSTRUCT_FQN + }); + + expectedParams.push({ + name: 'id', + type: 'string' + }); + + // it's okay for a construct not to have a "props" argument so we only + // assert the "props" argument if there are more than two parameters + if (initializer.parameters.length > 2) { + expectedParams.push({ + name: 'props', + }); + } + + e.assertSignature(initializer, { + parameters: expectedParams + }); + } +}); diff --git a/tools/awslint/lib/rules/index.ts b/tools/awslint/lib/rules/index.ts new file mode 100644 index 0000000000000..065133c0eb94c --- /dev/null +++ b/tools/awslint/lib/rules/index.ts @@ -0,0 +1,3 @@ +export * from './module'; +export * from './construct'; +export * from './resource'; diff --git a/tools/awslint/lib/rules/module.ts b/tools/awslint/lib/rules/module.ts new file mode 100644 index 0000000000000..ae4ac8db9d315 --- /dev/null +++ b/tools/awslint/lib/rules/module.ts @@ -0,0 +1,31 @@ +import reflect = require('jsii-reflect'); +import { findCfnResources } from '../cfn-resources'; +import { Linter } from '../linter'; + +interface ModuleLinterContext { + readonly assembly: reflect.Assembly; + readonly namespace: string; +} + +export const moduleLinter = new Linter(assembly => { + const cfnResources = findCfnResources(assembly); + if (cfnResources.length === 0) { + return undefined; // no resources + } + + return [ { + assembly, + namespace: cfnResources[0].namespace + } ]; +}); + +moduleLinter.add( { + code: 'module-name', + message: 'module name must be @aws-cdk/aws-', + eval: e => { + if (!e.ctx.namespace) { return; } + if (!e.ctx.assembly) { return; } + const namespace = e.ctx.namespace.toLocaleLowerCase().replace('::', '-'); + e.assertEquals(e.ctx.assembly.name, `@aws-cdk/${namespace}`, e.ctx.assembly.name); + } +}); diff --git a/tools/awslint/lib/rules/resource.ts b/tools/awslint/lib/rules/resource.ts new file mode 100644 index 0000000000000..8c1f391fd2ffb --- /dev/null +++ b/tools/awslint/lib/rules/resource.ts @@ -0,0 +1,180 @@ +import reflect = require('jsii-reflect'); +import { CfnResourceSpec, findCfnResources } from '../cfn-resources'; +import { Linter } from '../linter'; +import { CONSTRUCT_FQN, CONSTRUCT_INTERFACE_FQN, isConstruct } from '../util'; + +export const resourceLinter = new Linter(assembly => { + return findCfnResources(assembly).map(cfn => ({ + assembly, + resource: cfn, + ts: assembly.system, + })); +}); + +interface ResourceLinterContext { + readonly ts: reflect.TypeSystem; + readonly resource: CfnResourceSpec; + readonly assembly: reflect.Assembly; + resourceClass?: reflect.ClassType; + resourcePropsInterface?: reflect.InterfaceType; + resourceInterface?: reflect.InterfaceType; + importPropsInterface?: reflect.InterfaceType; + resourceAttributes?: reflect.Property[]; +} + +resourceLinter.add({ + code: 'resource-class', + message: 'every resource must have a resource class (L2)', + warning: true, + eval: e => { + const resourceFqn = `${e.ctx.assembly.name}.${e.ctx.resource.basename}`; + const resourceClass = e.ctx.ts.classes.find(c => c.fqn === resourceFqn); + if (!e.assert(resourceClass, resourceFqn)) { + return; + } + + e.ctx.resourceClass = resourceClass; + } +}); + +resourceLinter.add({ + code: 'resource-class-is-construct', + message: `resource classes must extend "cdk.Construct" directly or indirectly`, + eval: e => { + if (!e.ctx.resourceClass) { return; } + e.assert(isConstruct(e.ctx.resourceClass), e.ctx.resourceClass.fqn); + } +}); + +resourceLinter.add({ + code: 'resource-props', + message: 'an interface for resource props must be defined', + eval: e => { + if (!e.ctx.resourceClass) { return; } + const fqn = `${e.ctx.assembly.name}.${e.ctx.resource.basename}Props`; + const resourcePropsInterface = e.ctx.ts.interfaces.find(i => i.fqn === fqn); + if (!e.assert(resourcePropsInterface, fqn)) { + return; + } + + e.ctx.resourcePropsInterface = resourcePropsInterface; + } +}); + +resourceLinter.add({ + code: 'resource-interface', + message: 'every resource must have a resource interface', + warning: true, + eval: e => { + if (!e.ctx.resourceClass) { return; } + + // first, let's look up the IFoo interface + const interfaceFqn = `${e.ctx.assembly.name}.I${e.ctx.resource.basename}`; + const resourceInterface = e.ctx.ts.interfaces.find(i => i.fqn === interfaceFqn); + if (!e.assert(resourceInterface, interfaceFqn)) { + return; + } + + e.ctx.resourceInterface = resourceInterface; + } +}); + +resourceLinter.add({ + code: 'import-props-interface', + message: 'every resource must have an "FooImportProps" interface', + eval: e => { + if (!e.ctx.resourceInterface) { + return; + } + + const attrsFqn = `${e.ctx.assembly.name}.${e.ctx.resource.basename}ImportProps`; + const importPropsInterface = e.ctx.ts.interfaces.find(c => c.fqn === attrsFqn); + if (e.assert(importPropsInterface, attrsFqn)) { + e.ctx.importPropsInterface = importPropsInterface; + } + } +}); + +resourceLinter.add({ + code: 'resource-interface-extends-construct', + message: 'resource interface must extends cdk.IConstruct', + eval: e => { + if (!e.ctx.resourceInterface) { return; } + e.assert(e.ctx.resourceInterface.interfaces.some(i => i.fqn === CONSTRUCT_INTERFACE_FQN), e.ctx.resourceInterface.fqn); + } +}); + +resourceLinter.add({ + code: 'resource-attribute', + message: 'resources must represent all attributes as properties', + eval: e => { + if (!e.ctx.resourceInterface) { return; } + + // verify that the interface has all attributes as readonly properties + const resourceAttributes = new Array(); + for (const attr of e.ctx.resource.attributes) { + const attribute: reflect.Property | undefined = e.ctx.resourceInterface.properties.find(p => p.name === attr); + const scope: string = e.ctx.resourceInterface.fqn + '.' + attr; + if (e.assert(attribute, scope)) { + resourceAttributes.push(attribute); + } + } + + e.ctx.resourceAttributes = resourceAttributes; + } +}); + +resourceLinter.add({ + code: 'resource-attribute-immutable', + message: 'resource attributes must be immutable (readonly)', + eval: e => { + if (!e.ctx.resourceAttributes) { return; } + for (const att of e.ctx.resourceAttributes) { + e.assert(att.immutable, att.parentType.fqn + '.' + att.name); + } + } +}); + +resourceLinter.add({ + code: 'import', + message: 'resource class must have a static "import" method', + eval: e => { + if (!e.ctx.resourceClass) { return; } + if (!e.ctx.resourceInterface) { return; } + if (!e.ctx.importPropsInterface) { return; } + + const importMethod = e.ctx.resourceClass.methods.find(m => m.static && m.name === 'import'); + if (!e.assert(importMethod, e.ctx.resourceClass.fqn)) { + return; + } + + e.assertSignature(importMethod, { + returns: e.ctx.resourceInterface.fqn, + parameters: [ + { name: 'scope', type: CONSTRUCT_FQN }, + { name: 'id', type: 'string' }, + { name: 'props', type: e.ctx.importPropsInterface.fqn } + ], + }); + } +}); + +resourceLinter.add({ + code: 'export', + message: 'resource interface must have an "export" method', + eval: e => { + if (!e.ctx.resourceInterface) { return; } + + const exportMethod = e.ctx.resourceInterface.methods.find(m => m.name === 'export'); + if (!e.assert(exportMethod, e.ctx.resourceInterface.fqn)) { + return; + } + + if (!e.ctx.importPropsInterface) { return; } + + e.assertSignature(exportMethod, { + returns: e.ctx.importPropsInterface.fqn, + parameters: [] + }); + } +}); diff --git a/tools/awslint/lib/util.ts b/tools/awslint/lib/util.ts new file mode 100644 index 0000000000000..59b13085074ae --- /dev/null +++ b/tools/awslint/lib/util.ts @@ -0,0 +1,11 @@ +import reflect = require('jsii-reflect'); + +export const CONSTRUCT_FQN = '@aws-cdk/cdk.Construct'; +export const CONSTRUCT_INTERFACE_FQN = '@aws-cdk/cdk.IConstruct'; + +export function isConstruct(c: reflect.ClassType) { + const constructClass = c.system.findFqn(CONSTRUCT_FQN); + const bases = c.getAncestors(); + const root = bases[bases.length - 1]; + return root === constructClass; +} diff --git a/tools/awslint/load.sh b/tools/awslint/load.sh new file mode 100755 index 0000000000000..35a3769d88027 --- /dev/null +++ b/tools/awslint/load.sh @@ -0,0 +1,32 @@ +#!/bin/bash +set -euo pipefail + +# given an aws-cdk bundle archive (the one published to github releases), extract +# all .jsii manifests and places them under "jsii/*.jsii" +# now they can be used with jsii-reflect + +zip=${1:-} +if [ -z "${zip}" ]; then + echo "Usage: $(basename $0) " + exit 1 +fi + +outdir=$PWD/jsii +rm -fr ${outdir} +mkdir -p ${outdir} + +workdir=$(mktemp -d) +(cd ${workdir} && unzip -q ${zip} js/*) + +for tarball in $(find ${workdir} -name *.jsii.tgz); do + basename=$(basename ${tarball} .jsii.tgz) + ( + staging=$(mktemp -d) + cd ${staging} + if tar -xzv --strip-components=1 -f ${tarball} package/.jsii 2> /dev/null; then + echo ${basename} + mv .jsii ${outdir}/${basename}.jsii + fi + rm -fr ${staging} + ) +done diff --git a/tools/awslint/package-lock.json b/tools/awslint/package-lock.json new file mode 100644 index 0000000000000..c9681041c5302 --- /dev/null +++ b/tools/awslint/package-lock.json @@ -0,0 +1,796 @@ +{ + "name": "awslint", + "version": "0.21.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@types/colors": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/colors/-/colors-1.2.1.tgz", + "integrity": "sha512-7jNkpfN2lVO07nJ1RWzyMnNhH/I5N9iWuMPx9pedptxJ4MODf8rRV0lbJi6RakQ4sKQk231Fw4e2W9n3D7gZ3w==", + "dev": true, + "requires": { + "colors": "*" + } + }, + "@types/fs-extra": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-5.0.4.tgz", + "integrity": "sha512-DsknoBvD8s+RFfSGjmERJ7ZOP1HI0UZRA3FSI+Zakhrc/Gy26YQsLI+m5V5DHxroHRJqCDLKJp7Hixn8zyaF7g==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/jest": { + "version": "23.3.10", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-23.3.10.tgz", + "integrity": "sha512-DC8xTuW/6TYgvEg3HEXS7cu9OijFqprVDXXiOcdOKZCU/5PJNLZU37VVvmZHdtMiGOa8wAA/We+JzbdxFzQTRQ==", + "dev": true + }, + "@types/node": { + "version": "10.12.18", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.18.tgz", + "integrity": "sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ==", + "dev": true + }, + "@types/yargs": { + "version": "12.0.4", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-12.0.4.tgz", + "integrity": "sha512-hBYcOaYPnlAPj2OZbivFFzsW61mN0L3N0Aq4KwRt/ZXlw1c25Srj8413MBu5IhFGOyOdjLi2/qOqtStDTXGs9g==", + "dev": true + }, + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "babel-code-frame": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", + "dev": true, + "requires": { + "chalk": "^1.1.3", + "esutils": "^2.0.2", + "js-tokens": "^3.0.2" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + } + } + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "builtin-modules": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", + "dev": true + }, + "camelcase": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.0.0.tgz", + "integrity": "sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==" + }, + "chalk": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", + "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "cliui": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", + "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", + "requires": { + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0", + "wrap-ansi": "^2.0.0" + } + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "colors": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.3.3.tgz", + "integrity": "sha512-mmGt/1pZqYRjMxB1axhTo16/snVZ5krrKkcmMeVKxzECMMXoCgnvTPp10QgHfcbQZw8Dq2jMNG6je4JlWU0gWg==" + }, + "commander": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz", + "integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" + }, + "diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "esutils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", + "dev": true + }, + "execa": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.10.0.tgz", + "integrity": "sha512-7XOMnz8Ynx1gGo/3hyV9loYNPWM94jG3+3T3Y8tsfSstFmETmENCMU/A/zj8Lyaj1lkgEepKepvd6240tBRvlw==", + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "requires": { + "locate-path": "^3.0.0" + } + }, + "fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==" + }, + "get-stream": { + "version": "3.0.0", + "resolved": "http://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" + }, + "glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "graceful-fs": { + "version": "4.1.15", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", + "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==" + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + } + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "invert-kv": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", + "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==" + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + }, + "js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", + "dev": true + }, + "js-yaml": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz", + "integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "jsii-reflect": { + "version": "0.7.12", + "resolved": "https://registry.npmjs.org/jsii-reflect/-/jsii-reflect-0.7.12.tgz", + "integrity": "sha512-FZJWt2yBW6K3y5pkyU7/FxEqA6/gpk5wme4YrC9ngzuVwraK+zcxF+b48sMeUP1tcsgEqElcWV+b9JTKyKSHlA==", + "requires": { + "colors": "^1.3.2", + "fs-extra": "^7.0.1", + "jsii-spec": "^0.7.12", + "oo-ascii-tree": "^0.7.12", + "yargs": "^12.0.5" + } + }, + "jsii-spec": { + "version": "0.7.12", + "resolved": "https://registry.npmjs.org/jsii-spec/-/jsii-spec-0.7.12.tgz", + "integrity": "sha512-nfTNRkBBx5+VVfWAoik01FipkkbEiLtMVbD3bFGHrVmjbwKicOkIeo4+E/2Cls7ETD/MAdO6d/XkQCOAh/lh3g==", + "requires": { + "jsonschema": "^1.2.4" + } + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "jsonschema": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/jsonschema/-/jsonschema-1.2.4.tgz", + "integrity": "sha512-lz1nOH69GbsVHeVgEdvyavc/33oymY1AZwtePMiMj4HZPMbP5OIKK3zT9INMWjwua/V4Z4yq7wSlBbSG+g4AEw==" + }, + "lcid": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", + "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", + "requires": { + "invert-kv": "^2.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "map-age-cleaner": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", + "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", + "requires": { + "p-defer": "^1.0.0" + } + }, + "mem": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-4.0.0.tgz", + "integrity": "sha512-WQxG/5xYc3tMbYLXoXPm81ET2WDULiU5FxbuIoNbJqLOOI8zehXFdZuiUEgfdrU2mVB1pxBZUGlYORSrpuJreA==", + "requires": { + "map-age-cleaner": "^0.1.1", + "mimic-fn": "^1.0.0", + "p-is-promise": "^1.1.0" + } + }, + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==" + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "requires": { + "path-key": "^2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "oo-ascii-tree": { + "version": "0.7.12", + "resolved": "https://registry.npmjs.org/oo-ascii-tree/-/oo-ascii-tree-0.7.12.tgz", + "integrity": "sha512-WM7Jb9OA7lnucLvgou9F0yq3uIXwl3ilx4N1EqgFh/FnjQfI0YL4I8ksGEvGbOXQHwYSobe0SoVlobdKl2PmHA==" + }, + "os-locale": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.0.1.tgz", + "integrity": "sha512-7g5e7dmXPtzcP4bgsZ8ixDVqA7oWYuEz4lOSujeWyliPai4gfVDiFIcwBg3aGCPnmSGfzOKTK3ccPn0CKv3DBw==", + "requires": { + "execa": "^0.10.0", + "lcid": "^2.0.0", + "mem": "^4.0.0" + } + }, + "p-defer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", + "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=" + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" + }, + "p-is-promise": { + "version": "1.1.0", + "resolved": "http://registry.npmjs.org/p-is-promise/-/p-is-promise-1.1.0.tgz", + "integrity": "sha1-nJRWmJ6fZYgBewQ01WCXZ1w9oF4=" + }, + "p-limit": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.1.0.tgz", + "integrity": "sha512-NhURkNcrVB+8hNfLuysU8enY5xn2KXphsHBaC2YmRNTZRc7RWusw6apSpdEj3jo4CMb6W9nrF6tTnsJsJeyu6g==", + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.0.0.tgz", + "integrity": "sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==" + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "http://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=" + }, + "resolve": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.9.0.tgz", + "integrity": "sha512-TZNye00tI67lwYvzxCxHGjwTNlUV70io54/Ed4j6PscB8xVfuBJpRenI/o6dVk0cY0PYTY27AgCoGGxRnYuItQ==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + }, + "semver": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", + "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==" + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "http://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "requires": { + "ansi-regex": "^3.0.0" + } + }, + "strip-eof": { + "version": "1.0.0", + "resolved": "http://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + }, + "tslib": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", + "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==", + "dev": true + }, + "tslint": { + "version": "5.12.0", + "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.12.0.tgz", + "integrity": "sha512-CKEcH1MHUBhoV43SA/Jmy1l24HJJgI0eyLbBNSRyFlsQvb9v6Zdq+Nz2vEOH00nC5SUx4SneJ59PZUS/ARcokQ==", + "dev": true, + "requires": { + "babel-code-frame": "^6.22.0", + "builtin-modules": "^1.1.1", + "chalk": "^2.3.0", + "commander": "^2.12.1", + "diff": "^3.2.0", + "glob": "^7.1.1", + "js-yaml": "^3.7.0", + "minimatch": "^3.0.4", + "resolve": "^1.3.2", + "semver": "^5.3.0", + "tslib": "^1.8.0", + "tsutils": "^2.27.2" + } + }, + "tsutils": { + "version": "2.29.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", + "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + }, + "typescript": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.2.2.tgz", + "integrity": "sha512-VCj5UiSyHBjwfYacmDuc/NOk4QQixbE+Wn7MFJuS0nRuPQbof132Pw4u53dm264O8LPc2MVsc7RJNml5szurkg==", + "dev": true + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "http://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "http://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==" + }, + "yargs": { + "version": "12.0.5", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", + "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", + "requires": { + "cliui": "^4.0.0", + "decamelize": "^1.2.0", + "find-up": "^3.0.0", + "get-caller-file": "^1.0.1", + "os-locale": "^3.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1 || ^4.0.0", + "yargs-parser": "^11.1.1" + } + }, + "yargs-parser": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", + "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } +} diff --git a/tools/awslint/package.json b/tools/awslint/package.json new file mode 100644 index 0000000000000..b86084317530f --- /dev/null +++ b/tools/awslint/package.json @@ -0,0 +1,48 @@ +{ + "name": "awslint", + "private": true, + "version": "0.21.0", + "description": "Enforces the AWS Construct Library guidelines", + "main": "index.js", + "scripts": { + "build": "tsc && chmod +x bin/awslint", + "lint": "tslint && pkglint", + "test": "echo ok", + "watch": "tsc -w" + }, + "bin": { + "awslint": "bin/awslint" + }, + "dependencies": { + "colors": "^1.3.3", + "fs-extra": "^7.0.1", + "jsii-reflect": "^0.7.12", + "yargs": "^12.0.5" + }, + "devDependencies": { + "tslint": "^5.12.0", + "typescript": "^3.2.2", + "@types/fs-extra": "^5.0.4", + "@types/jest": "^23.3.10", + "@types/colors": "^1.2.1", + "@types/yargs": "^12.0.4" + }, + "repository": { + "type": "git", + "url": "https://github.com/awslabs/aws-cdk.git" + }, + "homepage": "https://github.com/awslabs/aws-cdk", + "license": "Apache-2.0", + "author": { + "name": "Amazon Web Services", + "url": "https://aws.amazon.com", + "organization": true + }, + "keywords": [ + "aws", + "cdk" + ], + "engines": { + "node": ">= 8.10.0" + } +} diff --git a/tools/awslint/tsconfig.json b/tools/awslint/tsconfig.json new file mode 100644 index 0000000000000..2455ee3509efe --- /dev/null +++ b/tools/awslint/tsconfig.json @@ -0,0 +1,28 @@ +{ + "compilerOptions": { + "alwaysStrict": true, + "charset": "utf8", + "declaration": true, + "experimentalDecorators": true, + "inlineSourceMap": true, + "inlineSources": true, + "lib": [ + "es2016", + "es2017.object", + "es2017.string" + ], + "module": "CommonJS", + "noEmitOnError": false, + "noFallthroughCasesInSwitch": true, + "noImplicitAny": true, + "noImplicitReturns": true, + "noImplicitThis": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "resolveJsonModule": true, + "strict": true, + "strictNullChecks": true, + "target": "ES2018" + }, + "_generated_by_jsii_": "Generated by jsii - safe to delete, and ideally should be in .gitignore" +} diff --git a/tools/awslint/tslint.yaml b/tools/awslint/tslint.yaml new file mode 100644 index 0000000000000..5982c63ec6b49 --- /dev/null +++ b/tools/awslint/tslint.yaml @@ -0,0 +1,53 @@ +extends: "tslint:recommended" +rules: + semicolon: [true, "always", "ignore-interfaces"] + + # Due to VSCode syntax highlighting we're unlikely to do this wrong, and it gets annoying + # when trying to construct literal Fn::Sub() arguments. + no-invalid-template-strings: false + + # No preference for quotes (?) + quotemark: false + + # Our props struct currently don't start with "I" + interface-name: false + + # We're not Java + max-classes-per-file: false + + # We have this wrong on all classes, keep it a warning for now + member-access: + severity: warning + + # Rule is dumb, complains about aliases for interface definitions + interface-over-type-literal: false + + # File should end with a newline. Why? + eofline: false + + # Way more readable without + arrow-parens: false + + # We're using namespaces. + no-namespace: false + + # The lines with CloudFormation doc links are quite long + max-line-length: [true, 150] + + # Super annoying + object-literal-sort-keys: false + + # Trailing comma gets into a fight with itself when splitting lists over multiple lines + trailing-comma: false + + # We create Constructs for their side effect all the time + no-unused-expression: [true, "allow-new"] + + # Without this rule, _blabla would be disallowed, which is necessary to silence unused variable errors. + # Allow Pascal Case for static variables + variable-name: [true, "ban-keywords", "check-format", "allow-leading-underscore", "allow-pascal-case"] + + # Unhandled promises are the source of all kinds of bugs and race conditions... + no-floating-promises: true + + no-console: false \ No newline at end of file diff --git a/tools/cdk-build-tools/bin/cdk-awslint b/tools/cdk-build-tools/bin/cdk-awslint new file mode 100755 index 0000000000000..ea9cac3495e6b --- /dev/null +++ b/tools/cdk-build-tools/bin/cdk-awslint @@ -0,0 +1,2 @@ +#!/usr/bin/env node +require('awslint/bin/awslint.js'); \ No newline at end of file diff --git a/tools/cdk-build-tools/lib/compile.ts b/tools/cdk-build-tools/lib/compile.ts index bcbb50a295be4..7785aa6920f4f 100644 --- a/tools/cdk-build-tools/lib/compile.ts +++ b/tools/cdk-build-tools/lib/compile.ts @@ -1,3 +1,4 @@ +import path = require('path'); import { makeExecutable, shell } from "./os"; import { CompilerOverrides, currentPackageJson, packageCompiler } from "./package-info"; import { Timers } from "./timer"; @@ -17,4 +18,5 @@ export async function compileCurrentPackage(timers: Timers, compilers: CompilerO // Always call linters await shell(['tslint', '--project', '.'], timers); await shell(['pkglint'], timers); + await shell([ path.join(__dirname, '..', 'bin', 'cdk-awslint') ], timers); } diff --git a/tools/cdk-build-tools/package.json b/tools/cdk-build-tools/package.json index 3e52cb8ee5569..83231fd466935 100644 --- a/tools/cdk-build-tools/package.json +++ b/tools/cdk-build-tools/package.json @@ -12,10 +12,11 @@ "cdk-build": "bin/cdk-build", "cdk-watch": "bin/cdk-watch", "cdk-test": "bin/cdk-test", - "cdk-package": "bin/cdk-package" + "cdk-package": "bin/cdk-package", + "cdk-awslint": "bin/cdk-awslint" }, "scripts": { - "build": "tsc && tslint -p . && chmod +x bin/cdk-build && chmod +x bin/cdk-test && chmod +x bin/cdk-watch && pkglint", + "build": "tsc && tslint -p . && chmod +x bin/cdk-build && chmod +x bin/cdk-test && chmod +x bin/cdk-watch && chmod +x bin/cdk-awslint && pkglint", "watch": "tsc -w", "pkglint": "pkglint -f" }, @@ -37,6 +38,7 @@ "nodeunit": "^0.11.3", "nyc": "^13.0.1", "typescript": "^3.1.2", + "awslint": "^0.21.0", "yargs": "^9.0.1" }, "keywords": [ diff --git a/tools/pkglint/lib/rules.ts b/tools/pkglint/lib/rules.ts index df951f5651185..4e6a6649fe5de 100644 --- a/tools/pkglint/lib/rules.ts +++ b/tools/pkglint/lib/rules.ts @@ -611,10 +611,6 @@ export class AllVersionsTheSame extends ValidationRule { private readonly ourPackages: {[pkg: string]: string} = {}; private readonly usedDeps: {[pkg: string]: VersionCount[]} = {}; - constructor() { - super(); - } - public prepare(pkg: PackageJson): void { this.ourPackages[pkg.json.name] = "^" + pkg.json.version; this.recordDeps(pkg.json.dependencies); @@ -676,6 +672,18 @@ export class AllVersionsTheSame extends ValidationRule { } } +export class AwsLint extends ValidationRule { + public name = 'awslint'; + + public validate(pkg: PackageJson) { + if (!isJSII(pkg)) { + return; + } + + expectJSON(this.name, pkg, 'scripts.awslint', 'cdk-awslint'); + } +} + /** * Determine whether this is a JSII package *