forked from aws/aws-cdk
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathast.ts
More file actions
150 lines (130 loc) · 5.18 KB
/
ast.ts
File metadata and controls
150 lines (130 loc) · 5.18 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
import { SpecDatabase, Resource, Service } from '@aws-cdk/service-spec-types';
import { Module } from '@cdklabs/typewriter';
import { AugmentationsModule } from './augmentation-generator';
import { CannedMetricsModule } from './canned-metrics';
import { CDK_CORE, CONSTRUCTS, ModuleImportLocations } from './cdk';
import { SelectiveImport } from './relationship-decider';
import { ResourceClass } from './resource-class';
/**
* A module containing a single resource
*/
export class ResourceModule extends Module {
public constructor(public readonly service: string, public readonly resource: string) {
super(`@aws-cdk/${service}/${resource}-l1`);
}
}
/**
* A module containing a service
*/
export class ServiceModule extends Module {
public constructor(public readonly service: string, public readonly shortName: string) {
super(`@aws-cdk/${service}`);
}
}
export interface AstBuilderProps {
readonly db: SpecDatabase;
/**
* Override the locations modules are imported from
*/
readonly importLocations?: ModuleImportLocations;
/**
* Append a suffix at the end of generated names.
*/
readonly nameSuffix?: string;
/**
* Mark everything in the service as deprecated using the provided deprecation message.
*
* @default - not deprecated
*/
readonly deprecated?: string;
}
export class AstBuilder<T extends Module> {
/**
* Build a module for all resources in a service
*/
public static forService(service: Service, props: AstBuilderProps): AstBuilder<ServiceModule> {
const scope = new ServiceModule(service.name, service.shortName);
const aug = new AugmentationsModule(props.db, service.name, props.importLocations?.cloudwatch);
const metrics = CannedMetricsModule.forService(props.db, service);
const ast = new AstBuilder(scope, props, aug, metrics);
const resources = props.db.follow('hasResource', service);
for (const link of resources) {
ast.addResource(link.entity);
}
ast.renderImports();
return ast;
}
/**
* Build an module for a single resource
*/
public static forResource(resource: Resource, props: AstBuilderProps): AstBuilder<ResourceModule> {
const parts = resource.cloudFormationType.toLowerCase().split('::');
const scope = new ResourceModule(parts[1], parts[2]);
const aug = new AugmentationsModule(props.db, parts[1], props.importLocations?.cloudwatch);
const metrics = CannedMetricsModule.forResource(props.db, resource);
const ast = new AstBuilder(scope, props, aug, metrics);
ast.addResource(resource);
ast.renderImports();
return ast;
}
public readonly db: SpecDatabase;
/**
* Map of CloudFormation resource name to generated class name
*/
public readonly resources: Record<string, string> = {};
private nameSuffix?: string;
private deprecated?: string;
public readonly selectiveImports = new Array<SelectiveImport>();
private readonly modulesRootLocation: string;
protected constructor(
public readonly module: T,
props: AstBuilderProps,
public readonly augmentations?: AugmentationsModule,
public readonly cannedMetrics?: CannedMetricsModule,
) {
this.db = props.db;
this.nameSuffix = props.nameSuffix;
this.deprecated = props.deprecated;
this.modulesRootLocation = props.importLocations?.modulesRoot ?? '../..';
CDK_CORE.import(this.module, 'cdk', { fromLocation: props.importLocations?.core });
CONSTRUCTS.import(this.module, 'constructs');
CDK_CORE.helpers.import(this.module, 'cfn_parse', { fromLocation: props.importLocations?.coreHelpers });
CDK_CORE.errors.import(this.module, 'cdk_errors', { fromLocation: props.importLocations?.coreErrors });
}
public addResource(resource: Resource) {
const resourceClass = new ResourceClass(this.module, this.db, resource, {
suffix: this.nameSuffix,
deprecated: this.deprecated,
});
this.resources[resource.cloudFormationType] = resourceClass.spec.name;
resourceClass.build();
this.addImports(resourceClass);
this.augmentations?.augmentResource(resource, resourceClass);
}
private addImports(resourceClass: ResourceClass) {
for (const selectiveImport of resourceClass.imports) {
const existingModuleImport = this.selectiveImports.find(
(imp) => imp.moduleName === selectiveImport.moduleName,
);
if (!existingModuleImport) {
this.selectiveImports.push(selectiveImport);
} else {
// We need to avoid importing the same reference multiple times
for (const type of selectiveImport.types) {
if (!existingModuleImport.types.find((t) =>
t.originalType === type.originalType && t.aliasedType === type.aliasedType,
)) {
existingModuleImport.types.push(type);
}
}
}
}
}
public renderImports() {
const sortedImports = this.selectiveImports.sort((a, b) => a.moduleName.localeCompare(b.moduleName));
for (const selectiveImport of sortedImports) {
const sourceModule = new Module(selectiveImport.moduleName);
sourceModule.importSelective(this.module, selectiveImport.types.map((t) => `${t.originalType} as ${t.aliasedType}`), { fromLocation: `${this.modulesRootLocation}/${sourceModule.name}` });
}
}
}