@cdktn/provider-generator 0.25.0-pre.9 → 0.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -0
- package/build/__tests__/edge-provider-schema/builder.js +3 -3
- package/build/__tests__/edge-provider-schema/cli.js +18 -8
- package/build/__tests__/edge-provider-schema.test.js +18 -8
- package/build/__tests__/provider.test.js +44 -16
- package/build/get/__tests__/constructs-maker.test.js +91 -8
- package/build/get/__tests__/generator/complex-computed-types.test.js +18 -8
- package/build/get/__tests__/generator/deep-nested-attributes.test.js +18 -8
- package/build/get/__tests__/generator/description-escaping.test.js +18 -8
- package/build/get/__tests__/generator/empty-provider-resources.test.js +18 -8
- package/build/get/__tests__/generator/ephemeral-resources.test.js +18 -8
- package/build/get/__tests__/generator/export-sharding.test.js +18 -8
- package/build/get/__tests__/generator/import-style.test.js +18 -8
- package/build/get/__tests__/generator/module-docs-registry.test.d.ts +2 -0
- package/build/get/__tests__/generator/module-docs-registry.test.js +91 -0
- package/build/get/__tests__/generator/module-generator.test.js +18 -8
- package/build/get/__tests__/generator/nested-types.test.js +18 -8
- package/build/get/__tests__/generator/provider-functions-name-collision.test.d.ts +2 -0
- package/build/get/__tests__/generator/provider-functions-name-collision.test.js +89 -0
- package/build/get/__tests__/generator/provider-functions.test.js +18 -8
- package/build/get/__tests__/generator/provider.test.js +18 -8
- package/build/get/__tests__/generator/resource-types.test.js +18 -8
- package/build/get/__tests__/generator/skipped-attributes.test.js +18 -8
- package/build/get/__tests__/generator/types.test.js +18 -8
- package/build/get/__tests__/generator/versions-file.test.js +25 -10
- package/build/get/__tests__/generator/write-only.test.js +18 -8
- package/build/get/__tests__/registry-class-invalidation.test.d.ts +2 -0
- package/build/get/__tests__/registry-class-invalidation.test.js +120 -0
- package/build/get/__tests__/target-versions.test.js +46 -8
- package/build/get/__tests__/util.js +20 -11
- package/build/get/constructs-maker.d.ts +17 -0
- package/build/get/constructs-maker.js +86 -11
- package/build/get/generator/emitter/struct-emitter.js +20 -10
- package/build/get/generator/loop-detection.js +2 -3
- package/build/get/generator/models/attribute-model.js +3 -3
- package/build/get/generator/models/attribute-type-model.js +3 -3
- package/build/get/generator/models/provider-function-model.js +3 -4
- package/build/get/generator/models/resource-model.d.ts +5 -1
- package/build/get/generator/models/resource-model.js +20 -10
- package/build/get/generator/models/supported-stored-classes.js +2 -3
- package/build/get/generator/module-generator.d.ts +3 -2
- package/build/get/generator/module-generator.js +15 -17
- package/build/get/generator/provider-generator.d.ts +15 -1
- package/build/get/generator/provider-generator.js +20 -5
- package/build/get/generator/resource-parser.d.ts +3 -1
- package/build/get/generator/resource-parser.js +16 -7
- package/build/get/generator/sanitized-comments.js +2 -3
- package/build/get/generator/skipped-attributes.js +2 -3
- package/build/index.js +3 -3
- package/build/util.js +3 -4
- package/package.json +32 -9
- package/src/__tests__/__snapshots__/provider.test.ts.snap +1309 -3206
- package/src/__tests__/provider.test.ts +31 -8
- package/src/get/__tests__/constructs-maker.test.ts +125 -1
- package/src/get/__tests__/generator/fixtures/provider-functions-name-collision.test.fixture.json +42 -0
- package/src/get/__tests__/generator/module-docs-registry.test.ts +89 -0
- package/src/get/__tests__/generator/provider-functions-name-collision.test.ts +84 -0
- package/src/get/__tests__/generator/versions-file.test.ts +10 -5
- package/src/get/__tests__/registry-class-invalidation.test.ts +123 -0
- package/src/get/__tests__/target-versions.test.ts +43 -0
- package/src/get/constructs-maker.ts +108 -7
- package/src/get/generator/emitter/struct-emitter.ts +2 -2
- package/src/get/generator/models/attribute-type-model.ts +1 -2
- package/src/get/generator/models/resource-model.ts +32 -9
- package/src/get/generator/module-generator.ts +20 -18
- package/src/get/generator/provider-generator.ts +27 -4
- package/src/get/generator/resource-parser.ts +21 -3
- package/tsconfig.json +4 -0
|
@@ -17,7 +17,11 @@ import {
|
|
|
17
17
|
type LanguageOptions,
|
|
18
18
|
type TerraformTargetVersions,
|
|
19
19
|
} from "@cdktn/commons";
|
|
20
|
-
import {
|
|
20
|
+
import {
|
|
21
|
+
DISPLAY_VERSION,
|
|
22
|
+
Language,
|
|
23
|
+
registryForTargetVersions,
|
|
24
|
+
} from "@cdktn/commons";
|
|
21
25
|
import { TerraformProviderGenerator } from "./generator/provider-generator";
|
|
22
26
|
import { ModuleGenerator } from "./generator/module-generator";
|
|
23
27
|
import { glob } from "glob";
|
|
@@ -275,11 +279,13 @@ type ConstraintFile = {
|
|
|
275
279
|
providers: Record<string, string>;
|
|
276
280
|
cdktf: string;
|
|
277
281
|
/**
|
|
278
|
-
* The project's declared targetVersions (cdktf.json)
|
|
279
|
-
*
|
|
280
|
-
*
|
|
281
|
-
*
|
|
282
|
-
*
|
|
282
|
+
* The project's declared targetVersions (cdktf.json). Read back by
|
|
283
|
+
* `filterAlreadyGenerated` to compare registry *classes*: which registry the
|
|
284
|
+
* generated docs links point at follows from this, so output produced for the
|
|
285
|
+
* other class is stale. The version ranges themselves do not affect the
|
|
286
|
+
* generated surface - the full surface is always generated and narrowing
|
|
287
|
+
* happens at synth - so a range moving within a class is not a reason to
|
|
288
|
+
* regenerate.
|
|
283
289
|
*/
|
|
284
290
|
targetVersions?: TerraformTargetVersions;
|
|
285
291
|
/**
|
|
@@ -315,6 +321,8 @@ export class ConstructsMaker {
|
|
|
315
321
|
private readonly codeMakerOutdir: string;
|
|
316
322
|
private readonly code: CodeMaker;
|
|
317
323
|
private versions: { [providerName: string]: string | undefined };
|
|
324
|
+
/** Resource directory names emitted per provider folder during this run */
|
|
325
|
+
public emittedResourceFolders: { [providerName: string]: string[] };
|
|
318
326
|
|
|
319
327
|
constructor(
|
|
320
328
|
private readonly options: GetOptions,
|
|
@@ -328,6 +336,7 @@ export class ConstructsMaker {
|
|
|
328
336
|
fs.mkdirpSync(this.codeMakerOutdir);
|
|
329
337
|
this.code = new CodeMaker();
|
|
330
338
|
this.versions = {};
|
|
339
|
+
this.emittedResourceFolders = {};
|
|
331
340
|
}
|
|
332
341
|
private async generateTypescriptProvider(
|
|
333
342
|
target: ConstructsMakerProviderTarget,
|
|
@@ -336,10 +345,15 @@ export class ConstructsMaker {
|
|
|
336
345
|
const endTSTimer = logTimespan(`Generate Typescript for ${target.name}`);
|
|
337
346
|
const generator = new TerraformProviderGenerator(this.code, schema, {
|
|
338
347
|
importExtension: this.options.languageOptions?.importExtension,
|
|
348
|
+
targetVersions: this.options.targetVersions,
|
|
339
349
|
});
|
|
340
350
|
generator.generate(target);
|
|
341
351
|
|
|
342
352
|
this.versions = { ...this.versions, ...generator.versions };
|
|
353
|
+
this.emittedResourceFolders = {
|
|
354
|
+
...this.emittedResourceFolders,
|
|
355
|
+
...generator.emittedResourceFolders,
|
|
356
|
+
};
|
|
343
357
|
endTSTimer();
|
|
344
358
|
}
|
|
345
359
|
|
|
@@ -399,6 +413,22 @@ export class ConstructsMaker {
|
|
|
399
413
|
return constraints;
|
|
400
414
|
}
|
|
401
415
|
|
|
416
|
+
// Generated docs links, and the prose naming the registry around them, follow
|
|
417
|
+
// the project's declared targets, so output from the other registry class is
|
|
418
|
+
// stale even when every provider version still matches.
|
|
419
|
+
const previousRegistry = registryForTargetVersions(
|
|
420
|
+
previousConstraints.targetVersions,
|
|
421
|
+
);
|
|
422
|
+
const currentRegistry = registryForTargetVersions(
|
|
423
|
+
this.options.targetVersions,
|
|
424
|
+
);
|
|
425
|
+
if (previousRegistry.hostname !== currentRegistry.hostname) {
|
|
426
|
+
logger.info(
|
|
427
|
+
`The target registry has changed from ${previousRegistry.displayName} to ${currentRegistry.displayName}, generating all constraints.`,
|
|
428
|
+
);
|
|
429
|
+
return constraints;
|
|
430
|
+
}
|
|
431
|
+
|
|
402
432
|
const constraintsToGenerate = constraints.filter((constraint) => {
|
|
403
433
|
const constraintMatches =
|
|
404
434
|
previousConstraints.providers![constraint.fqn] === constraint.version;
|
|
@@ -449,7 +479,7 @@ export class ConstructsMaker {
|
|
|
449
479
|
) {
|
|
450
480
|
const endTSTimer = logTimespan(`Generate Typescript for ${target.name}`);
|
|
451
481
|
target.spec = schema;
|
|
452
|
-
new ModuleGenerator(this.code, [target]);
|
|
482
|
+
new ModuleGenerator(this.code, [target], this.options.targetVersions);
|
|
453
483
|
endTSTimer();
|
|
454
484
|
}
|
|
455
485
|
|
|
@@ -521,7 +551,21 @@ export class ConstructsMaker {
|
|
|
521
551
|
);
|
|
522
552
|
}
|
|
523
553
|
|
|
554
|
+
// Keys are fully qualified and the host is whichever registry the fetching CLI
|
|
555
|
+
// resolved from, so a provider regenerated this run can arrive under a different
|
|
556
|
+
// host than the file already holds. Carrying the old key forward as well would
|
|
557
|
+
// leave two entries for one provider, and the stale one sorts first.
|
|
558
|
+
const regenerated = new Set(
|
|
559
|
+
Object.keys(this.versions).map((fqpn) =>
|
|
560
|
+
fqpn.split("/").slice(1).join("/"),
|
|
561
|
+
),
|
|
562
|
+
);
|
|
563
|
+
|
|
524
564
|
const versions = allowedConstraints.reduce((acc, constraint) => {
|
|
565
|
+
if (regenerated.has(constraint.fqn)) {
|
|
566
|
+
return acc;
|
|
567
|
+
}
|
|
568
|
+
|
|
525
569
|
const provider = Object.entries(previousVersions).find(([name]) =>
|
|
526
570
|
// This could be more refined, but it's good enough for now
|
|
527
571
|
name.endsWith(constraint.fqn),
|
|
@@ -595,6 +639,62 @@ export class ConstructsMaker {
|
|
|
595
639
|
});
|
|
596
640
|
}
|
|
597
641
|
|
|
642
|
+
/**
|
|
643
|
+
* Delete resource directories under `providers/` that this run did not emit, so each provider folder holds
|
|
644
|
+
* exactly the resources in the version it was generated against.
|
|
645
|
+
*
|
|
646
|
+
* Walks only providers present in `emittedResourceFolders`, keeping any provider this run skipped. Within those,
|
|
647
|
+
* removes every subdirectory not emitted and leaves provider-level files (`index.ts`, `lazy-index.ts`) in place.
|
|
648
|
+
* Deletes files, so it must run after `save()` has written the current run's output.
|
|
649
|
+
*
|
|
650
|
+
* TypeScript only: `providers/<name>/<resource>/` maps one-to-one onto the emitted index exports. Other languages
|
|
651
|
+
* co-locate providers and modules in the same folders, which makes a dropped resource indistinguishable from a
|
|
652
|
+
* hand-written construct.
|
|
653
|
+
*/
|
|
654
|
+
public async removeOrphanedResourceFolders() {
|
|
655
|
+
if (!this.isJavascriptTarget) {
|
|
656
|
+
return;
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
for (const [provider, emittedFolders] of Object.entries(
|
|
660
|
+
this.emittedResourceFolders,
|
|
661
|
+
)) {
|
|
662
|
+
const providerFolder = path.resolve(
|
|
663
|
+
this.codeMakerOutdir,
|
|
664
|
+
"providers",
|
|
665
|
+
provider,
|
|
666
|
+
);
|
|
667
|
+
|
|
668
|
+
let entries: string[] = [];
|
|
669
|
+
try {
|
|
670
|
+
entries = await fs.readdir(providerFolder);
|
|
671
|
+
} catch (e) {
|
|
672
|
+
logger.debug(
|
|
673
|
+
`Could not list resource folders in '${providerFolder}': ${e}`,
|
|
674
|
+
);
|
|
675
|
+
continue;
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
const expected = new Set(emittedFolders);
|
|
679
|
+
for (const entry of entries) {
|
|
680
|
+
if (expected.has(entry)) {
|
|
681
|
+
continue;
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
const entryPath = path.resolve(providerFolder, entry);
|
|
685
|
+
if (!fs.statSync(entryPath).isDirectory()) {
|
|
686
|
+
// Leave index.ts / lazy-index.ts and any other provider-level files alone.
|
|
687
|
+
continue;
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
logger.debug(
|
|
691
|
+
`Removing orphaned resource folder '${entry}' from provider '${provider}': it was not emitted for the current provider version`,
|
|
692
|
+
);
|
|
693
|
+
await fs.remove(entryPath);
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
|
|
598
698
|
// emits a constraints.json file with a map of the used provider fqpns and version constraints
|
|
599
699
|
// this is used for caching purposes
|
|
600
700
|
private emitConstraintsFile(
|
|
@@ -757,6 +857,7 @@ a NODE_OPTIONS variable, we won't override it. Hence, the provider generation mi
|
|
|
757
857
|
|
|
758
858
|
if (this.isJavascriptTarget) {
|
|
759
859
|
await this.save();
|
|
860
|
+
await this.removeOrphanedResourceFolders();
|
|
760
861
|
}
|
|
761
862
|
|
|
762
863
|
if (!this.isJavascriptTarget || this.options.outputJsii) {
|
|
@@ -61,13 +61,13 @@ export class StructEmitter {
|
|
|
61
61
|
comment.line(att.description);
|
|
62
62
|
comment.line(``);
|
|
63
63
|
comment.line(
|
|
64
|
-
`Docs at
|
|
64
|
+
`Docs at ${resource.docsRegistryName}: {@link ${resource.linkToDocs}#${att.terraformName} ${resource.className}#${att.terraformName}}`,
|
|
65
65
|
);
|
|
66
66
|
this.warnAboutIdField(att);
|
|
67
67
|
comment.end();
|
|
68
68
|
} else {
|
|
69
69
|
comment.line(
|
|
70
|
-
`Docs at
|
|
70
|
+
`Docs at ${resource.docsRegistryName}: {@link ${resource.linkToDocs}#${att.terraformName} ${resource.className}#${att.terraformName}}`,
|
|
71
71
|
);
|
|
72
72
|
this.warnAboutIdField(att);
|
|
73
73
|
comment.end();
|
|
@@ -215,8 +215,7 @@ export interface CollectionAttributeTypeModel extends AttributeTypeModel {
|
|
|
215
215
|
// into the nearest typed "Any"-wrapper. undefined only defensively - see
|
|
216
216
|
// resolveStoredClassName in supported-stored-classes.ts.
|
|
217
217
|
readonly resolvedStoredClass:
|
|
218
|
-
|
|
219
|
-
| undefined;
|
|
218
|
+
{ name: string; collapsed: boolean } | undefined;
|
|
220
219
|
}
|
|
221
220
|
|
|
222
221
|
export class ListAttributeTypeModel implements CollectionAttributeTypeModel {
|
|
@@ -5,7 +5,11 @@ import path from "path";
|
|
|
5
5
|
import { FQPN, parseFQPN, ProviderName } from "@cdktn/provider-schema";
|
|
6
6
|
import { AttributeModel } from "./attribute-model";
|
|
7
7
|
import { Struct, ConfigStruct } from "./struct";
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
Schema,
|
|
10
|
+
TerraformTargetVersions,
|
|
11
|
+
registryForTargetVersions,
|
|
12
|
+
} from "@cdktn/commons";
|
|
9
13
|
import { ProviderFunctionsModel } from "./provider-function-model";
|
|
10
14
|
|
|
11
15
|
// Limit is 1200 to prevent stack size error.
|
|
@@ -25,6 +29,7 @@ interface ResourceModelOptions {
|
|
|
25
29
|
schema: Schema;
|
|
26
30
|
terraformSchemaType: string;
|
|
27
31
|
configStructName: string;
|
|
32
|
+
targetVersions?: TerraformTargetVersions;
|
|
28
33
|
}
|
|
29
34
|
|
|
30
35
|
type DefinitionPath = string;
|
|
@@ -35,6 +40,7 @@ export class ResourceModel {
|
|
|
35
40
|
public baseName: string;
|
|
36
41
|
public provider: ProviderName;
|
|
37
42
|
public fqpn: FQPN;
|
|
43
|
+
private readonly targetVersions?: TerraformTargetVersions;
|
|
38
44
|
public providerVersionConstraint?: string;
|
|
39
45
|
public providerVersion?: string;
|
|
40
46
|
public terraformProviderSource?: string;
|
|
@@ -68,6 +74,7 @@ export class ResourceModel {
|
|
|
68
74
|
this.attributes = options.attributes;
|
|
69
75
|
this.schema = options.schema;
|
|
70
76
|
this.fqpn = options.fqpn;
|
|
77
|
+
this.targetVersions = options.targetVersions;
|
|
71
78
|
this.provider = parseFQPN(options.fqpn).name;
|
|
72
79
|
this.terraformProviderName = this.provider;
|
|
73
80
|
this.fileName = options.fileName;
|
|
@@ -108,16 +115,32 @@ export class ResourceModel {
|
|
|
108
115
|
return this.escapeSchema(JSON.stringify(this.schema, undefined, 2));
|
|
109
116
|
}
|
|
110
117
|
|
|
118
|
+
/** Registry that linkToDocs points at, for use in the surrounding prose. */
|
|
119
|
+
public get docsRegistryName(): string {
|
|
120
|
+
return registryForTargetVersions(this.targetVersions).displayName;
|
|
121
|
+
}
|
|
122
|
+
|
|
111
123
|
public get linkToDocs(): string {
|
|
112
|
-
|
|
124
|
+
// Keyed off the project's declared targets, not the FQPN hostname: that
|
|
125
|
+
// hostname reflects whichever CLI fetched the schema, which would make the
|
|
126
|
+
// generated link differ between a terraform and a tofu `cdktn get`.
|
|
127
|
+
const registry = registryForTargetVersions(this.targetVersions);
|
|
128
|
+
const { namespace, name } = parseFQPN(this.fqpn);
|
|
113
129
|
const version = this.providerVersion || "latest";
|
|
114
|
-
const
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
130
|
+
const kind = this.isProvider
|
|
131
|
+
? "provider"
|
|
132
|
+
: this.isDataSource
|
|
133
|
+
? "data-source"
|
|
134
|
+
: this.isEphemeralResource
|
|
135
|
+
? "ephemeral-resource"
|
|
136
|
+
: "resource";
|
|
137
|
+
return registry.docsUrl(
|
|
138
|
+
namespace,
|
|
139
|
+
name,
|
|
140
|
+
version,
|
|
141
|
+
kind,
|
|
142
|
+
this.terraformDocName,
|
|
143
|
+
);
|
|
121
144
|
}
|
|
122
145
|
|
|
123
146
|
public get isProvider(): boolean {
|
|
@@ -3,12 +3,17 @@
|
|
|
3
3
|
import { CodeMaker, toCamelCase } from "codemaker";
|
|
4
4
|
import { AttributeModel } from "./models";
|
|
5
5
|
import { sanitizedComment } from "./sanitized-comments";
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
ConstructsMakerModuleTarget,
|
|
8
|
+
TerraformTargetVersions,
|
|
9
|
+
registryForTargetVersions,
|
|
10
|
+
} from "@cdktn/commons";
|
|
7
11
|
|
|
8
12
|
export class ModuleGenerator {
|
|
9
13
|
constructor(
|
|
10
14
|
private readonly code: CodeMaker,
|
|
11
15
|
private readonly targets: ConstructsMakerModuleTarget[],
|
|
16
|
+
private readonly targetVersions?: TerraformTargetVersions,
|
|
12
17
|
) {
|
|
13
18
|
this.code.indentation = 2;
|
|
14
19
|
|
|
@@ -76,22 +81,19 @@ export class ModuleGenerator {
|
|
|
76
81
|
// ... and more
|
|
77
82
|
// ../module and ./module (local paths)
|
|
78
83
|
const isNonRegistryModule = target.source.includes(".");
|
|
79
|
-
|
|
80
|
-
//
|
|
81
|
-
|
|
82
|
-
//
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
// not submodule specified, just append the version
|
|
93
|
-
registryPath = `${target.source}/${target.version || "latest"}`;
|
|
94
|
-
}
|
|
84
|
+
// Keyed off the project's declared targets rather than a fixed registry, so
|
|
85
|
+
// the link matches the product the project builds for.
|
|
86
|
+
const registry = registryForTargetVersions(this.targetVersions);
|
|
87
|
+
// Registries only have pages for a direct child of `modules/`, e.g.
|
|
88
|
+
// terraform-aws-modules/vpc/aws//modules/vpc-endpoints; any other `//` path
|
|
89
|
+
// links to the module itself.
|
|
90
|
+
const [moduleSource, subPath] = target.source.split("//", 2);
|
|
91
|
+
const submodule = subPath?.match(/^modules\/([^/]+)$/)?.[1];
|
|
92
|
+
const docsUrl = registry.moduleDocsUrl(
|
|
93
|
+
moduleSource,
|
|
94
|
+
target.version,
|
|
95
|
+
submodule,
|
|
96
|
+
);
|
|
95
97
|
|
|
96
98
|
const comment = sanitizedComment(this.code);
|
|
97
99
|
comment.line(`Defines an ${baseName} based on a Terraform module`);
|
|
@@ -99,7 +101,7 @@ export class ModuleGenerator {
|
|
|
99
101
|
comment.line(
|
|
100
102
|
isNonRegistryModule
|
|
101
103
|
? `Source at ${target.source}`
|
|
102
|
-
: `Docs at
|
|
104
|
+
: `Docs at ${registry.displayName}: {@link ${docsUrl} ${target.source}}`,
|
|
103
105
|
);
|
|
104
106
|
comment.end();
|
|
105
107
|
this.code.openBlock(`export class ${baseName} extends TerraformModule`);
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
logger,
|
|
9
9
|
ProviderSchema,
|
|
10
10
|
TerraformProviderConstraint,
|
|
11
|
+
TerraformTargetVersions,
|
|
11
12
|
} from "@cdktn/commons";
|
|
12
13
|
import { FQPN, parseFQPN, ProviderName } from "@cdktn/provider-schema";
|
|
13
14
|
import {
|
|
@@ -29,6 +30,8 @@ export interface TerraformProviderGeneratorOptions {
|
|
|
29
30
|
* Defaults to "" (CommonJS).
|
|
30
31
|
*/
|
|
31
32
|
readonly importExtension?: string;
|
|
33
|
+
/** Project's declared targetVersions; selects the docs registry. */
|
|
34
|
+
readonly targetVersions?: TerraformTargetVersions;
|
|
32
35
|
}
|
|
33
36
|
|
|
34
37
|
interface ProviderData {
|
|
@@ -107,12 +110,17 @@ export interface ProviderConstraints {
|
|
|
107
110
|
}
|
|
108
111
|
|
|
109
112
|
export class TerraformProviderGenerator {
|
|
110
|
-
private resourceParser
|
|
113
|
+
private resourceParser: ResourceParser;
|
|
111
114
|
private resourceEmitter: ResourceEmitter;
|
|
112
115
|
private structEmitter: StructEmitter;
|
|
113
116
|
private providerFunctionsEmitter: ProviderFunctionsEmitter;
|
|
114
117
|
private readonly importExtension: string;
|
|
115
118
|
public versions: { [fqpn: string]: string | undefined } = {};
|
|
119
|
+
/**
|
|
120
|
+
* Resource directory names emitted per provider folder Used to prune directories
|
|
121
|
+
* left over from a previous provider version.
|
|
122
|
+
*/
|
|
123
|
+
public emittedResourceFolders: { [provider: string]: string[] } = {};
|
|
116
124
|
|
|
117
125
|
constructor(
|
|
118
126
|
private readonly code: CodeMaker,
|
|
@@ -121,6 +129,7 @@ export class TerraformProviderGenerator {
|
|
|
121
129
|
) {
|
|
122
130
|
this.code.indentation = 2;
|
|
123
131
|
this.importExtension = options.importExtension ?? "";
|
|
132
|
+
this.resourceParser = new ResourceParser(options.targetVersions);
|
|
124
133
|
this.resourceEmitter = new ResourceEmitter(this.code, this.importExtension);
|
|
125
134
|
this.structEmitter = new StructEmitter(this.code, this.importExtension);
|
|
126
135
|
this.providerFunctionsEmitter = new ProviderFunctionsEmitter(this.code);
|
|
@@ -298,6 +307,20 @@ export class TerraformProviderGenerator {
|
|
|
298
307
|
|
|
299
308
|
this.emitIndexFile(name, files);
|
|
300
309
|
this.emitLazyIndexFile(name, files);
|
|
310
|
+
|
|
311
|
+
// Record exactly what this run emitted, derived the same way the index
|
|
312
|
+
// files derive their exports, so pruning can never disagree with them.
|
|
313
|
+
this.emittedResourceFolders[name] = files.map((file) =>
|
|
314
|
+
this.resourceFolderName(name, file),
|
|
315
|
+
);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Map an emitted file path to the resource directory name it lives in, e.g.
|
|
320
|
+
* `providers/mongodbatlas/teams/index.ts` -> `teams`.
|
|
321
|
+
*/
|
|
322
|
+
private resourceFolderName(provider: ProviderName, file: string): string {
|
|
323
|
+
return file.replace(`providers/${provider}/`, "").replace("/index.ts", "");
|
|
301
324
|
}
|
|
302
325
|
|
|
303
326
|
private emitProviderFunctions(
|
|
@@ -322,7 +345,7 @@ export class TerraformProviderGenerator {
|
|
|
322
345
|
? resource.provider
|
|
323
346
|
: resource.terraformType;
|
|
324
347
|
this.code.line(
|
|
325
|
-
`Refer to the
|
|
348
|
+
`Refer to the ${resource.docsRegistryName} for docs: [\`${type}\`](${resource.linkToDocs}).`,
|
|
326
349
|
);
|
|
327
350
|
this.code.closeFile(filePath);
|
|
328
351
|
}
|
|
@@ -333,7 +356,7 @@ export class TerraformProviderGenerator {
|
|
|
333
356
|
this.code.openFile(filePath);
|
|
334
357
|
this.code.line("// generated by cdktn get");
|
|
335
358
|
for (const file of files) {
|
|
336
|
-
const dirName =
|
|
359
|
+
const dirName = this.resourceFolderName(provider, file);
|
|
337
360
|
this.code.line(
|
|
338
361
|
`export * as ${toCamelCase(dirName)} from './${dirName}/index${this.importExtension}';`,
|
|
339
362
|
);
|
|
@@ -348,7 +371,7 @@ export class TerraformProviderGenerator {
|
|
|
348
371
|
this.code.openFile(filePath);
|
|
349
372
|
this.code.line("// generated by cdktn get");
|
|
350
373
|
for (const file of files) {
|
|
351
|
-
const dirName =
|
|
374
|
+
const dirName = this.resourceFolderName(provider, file);
|
|
352
375
|
this.code.line(
|
|
353
376
|
`Object.defineProperty(exports, '${toCamelCase(
|
|
354
377
|
dirName,
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
isAttributeNestedType,
|
|
12
12
|
isNestedTypeAttribute,
|
|
13
13
|
Schema,
|
|
14
|
+
TerraformTargetVersions,
|
|
14
15
|
} from "@cdktn/commons";
|
|
15
16
|
import { ProviderName, FQPN, parseFQPN } from "@cdktn/provider-schema";
|
|
16
17
|
import {
|
|
@@ -82,8 +83,17 @@ export function sanitizeClassOrNamespaceName(
|
|
|
82
83
|
isProvider = false,
|
|
83
84
|
) {
|
|
84
85
|
const resourceIsNamedProvider = !isProvider && baseName === "provider";
|
|
85
|
-
|
|
86
|
-
|
|
86
|
+
// A resource named `<provider>_provider_functions` maps to the same
|
|
87
|
+
// providers/<provider>/provider-functions/ directory that the provider's
|
|
88
|
+
// functions submodule is emitted to, and would silently overwrite it.
|
|
89
|
+
const resourceIsNamedProviderFunctions =
|
|
90
|
+
!isProvider && baseName === "provider_functions";
|
|
91
|
+
|
|
92
|
+
if (
|
|
93
|
+
isReservedClassOrNamespaceName(baseName) ||
|
|
94
|
+
resourceIsNamedProvider ||
|
|
95
|
+
resourceIsNamedProviderFunctions
|
|
96
|
+
) {
|
|
87
97
|
return `${baseName}_resource`;
|
|
88
98
|
} else {
|
|
89
99
|
return baseName;
|
|
@@ -114,6 +124,7 @@ class Parser {
|
|
|
114
124
|
constructor(
|
|
115
125
|
private classNames: string[],
|
|
116
126
|
private usedBaseNames: Map<string, string>,
|
|
127
|
+
private targetVersions?: TerraformTargetVersions,
|
|
117
128
|
) {}
|
|
118
129
|
|
|
119
130
|
private uniqueClassName(className: string): string {
|
|
@@ -285,6 +296,7 @@ class Parser {
|
|
|
285
296
|
terraformSchemaType,
|
|
286
297
|
structs: this.structs,
|
|
287
298
|
configStructName,
|
|
299
|
+
targetVersions: this.targetVersions,
|
|
288
300
|
});
|
|
289
301
|
|
|
290
302
|
return resourceModel;
|
|
@@ -718,6 +730,8 @@ export class ResourceParser {
|
|
|
718
730
|
private uniqueBaseNames: Map<string, string> = new Map();
|
|
719
731
|
private resources: Record<string, ResourceModel> = {};
|
|
720
732
|
|
|
733
|
+
constructor(private readonly targetVersions?: TerraformTargetVersions) {}
|
|
734
|
+
|
|
721
735
|
public parse(
|
|
722
736
|
fqpn: FQPN,
|
|
723
737
|
type: string,
|
|
@@ -729,7 +743,11 @@ export class ResourceParser {
|
|
|
729
743
|
return this.resources[type];
|
|
730
744
|
}
|
|
731
745
|
|
|
732
|
-
const parser = new Parser(
|
|
746
|
+
const parser = new Parser(
|
|
747
|
+
this.uniqueClassnames,
|
|
748
|
+
this.uniqueBaseNames,
|
|
749
|
+
this.targetVersions,
|
|
750
|
+
);
|
|
733
751
|
const resource = parser.resourceFrom(
|
|
734
752
|
fqpn,
|
|
735
753
|
type,
|