@cdktn/provider-generator 0.24.0-pre.9 → 0.24.0-pre.91

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.
Files changed (124) hide show
  1. package/.spec.swcrc +22 -0
  2. package/LICENSE +355 -0
  3. package/README.md +1 -1
  4. package/build/__tests__/edge-provider-schema/builder.d.ts +9 -3
  5. package/build/__tests__/edge-provider-schema/builder.js +6 -3
  6. package/build/__tests__/edge-provider-schema/cli.js +8 -3
  7. package/build/__tests__/edge-provider-schema/index.js +119 -1
  8. package/build/__tests__/provider.test.js +4 -1
  9. package/build/get/__tests__/generator/ephemeral-resources.test.d.ts +2 -0
  10. package/build/get/__tests__/generator/ephemeral-resources.test.js +49 -0
  11. package/build/get/__tests__/generator/module-generator.test.js +12 -12
  12. package/build/get/__tests__/generator/provider-functions.test.d.ts +2 -0
  13. package/build/get/__tests__/generator/provider-functions.test.js +176 -0
  14. package/build/get/__tests__/generator/provider-source-matching.test.d.ts +2 -0
  15. package/build/get/__tests__/generator/provider-source-matching.test.js +45 -0
  16. package/build/get/__tests__/generator/provider.test.js +9 -1
  17. package/build/get/__tests__/generator/supported-stored-classes.test.d.ts +2 -0
  18. package/build/get/__tests__/generator/supported-stored-classes.test.js +137 -0
  19. package/build/get/__tests__/generator/types.test.js +44 -1
  20. package/build/get/__tests__/generator/write-only.test.d.ts +2 -0
  21. package/build/get/__tests__/generator/write-only.test.js +65 -0
  22. package/build/get/__tests__/target-versions.test.d.ts +2 -0
  23. package/build/get/__tests__/target-versions.test.js +171 -0
  24. package/build/get/constructs-maker.d.ts +8 -1
  25. package/build/get/constructs-maker.js +46 -10
  26. package/build/get/generator/emitter/attributes-emitter.d.ts +13 -2
  27. package/build/get/generator/emitter/attributes-emitter.js +71 -5
  28. package/build/get/generator/emitter/index.d.ts +1 -0
  29. package/build/get/generator/emitter/index.js +2 -1
  30. package/build/get/generator/emitter/provider-functions-emitter.d.ts +11 -0
  31. package/build/get/generator/emitter/provider-functions-emitter.js +94 -0
  32. package/build/get/generator/emitter/resource-emitter.d.ts +5 -1
  33. package/build/get/generator/emitter/resource-emitter.js +83 -7
  34. package/build/get/generator/emitter/struct-emitter.js +10 -10
  35. package/build/get/generator/models/attribute-model.d.ts +4 -0
  36. package/build/get/generator/models/attribute-model.js +50 -4
  37. package/build/get/generator/models/attribute-type-model.d.ts +24 -0
  38. package/build/get/generator/models/attribute-type-model.js +91 -5
  39. package/build/get/generator/models/index.d.ts +1 -0
  40. package/build/get/generator/models/index.js +2 -1
  41. package/build/get/generator/models/provider-function-model.d.ts +102 -0
  42. package/build/get/generator/models/provider-function-model.js +667 -0
  43. package/build/get/generator/models/resource-model.d.ts +3 -0
  44. package/build/get/generator/models/resource-model.js +13 -4
  45. package/build/get/generator/models/struct.d.ts +2 -0
  46. package/build/get/generator/models/struct.js +6 -2
  47. package/build/get/generator/models/supported-stored-classes.d.ts +5 -0
  48. package/build/get/generator/models/supported-stored-classes.js +86 -0
  49. package/build/get/generator/module-generator.js +3 -3
  50. package/build/get/generator/provider-generator.d.ts +2 -0
  51. package/build/get/generator/provider-generator.js +41 -6
  52. package/build/get/generator/resource-parser.js +3 -1
  53. package/jest.config.js +16 -9
  54. package/package.json +28 -27
  55. package/package.sh +1 -1
  56. package/src/__tests__/__snapshots__/edge-provider-schema.test.ts.snap +4 -0
  57. package/src/__tests__/__snapshots__/provider.test.ts.snap +2828 -2828
  58. package/src/__tests__/edge-provider-schema/builder.ts +201 -0
  59. package/src/__tests__/edge-provider-schema/cli.ts +51 -0
  60. package/src/__tests__/edge-provider-schema/index.ts +284 -0
  61. package/src/__tests__/edge-provider-schema.test.ts +24 -0
  62. package/src/__tests__/provider.test.ts +183 -0
  63. package/src/get/__tests__/constructs-maker.test.ts +118 -0
  64. package/src/get/__tests__/generator/__snapshots__/complex-computed-types.test.ts.snap +5 -5
  65. package/src/get/__tests__/generator/__snapshots__/ephemeral-resources.test.ts.snap +524 -0
  66. package/src/get/__tests__/generator/__snapshots__/export-sharding.test.ts.snap +3310 -3310
  67. package/src/get/__tests__/generator/__snapshots__/module-generator.test.ts.snap +355 -355
  68. package/src/get/__tests__/generator/__snapshots__/nested-types.test.ts.snap +8 -8
  69. package/src/get/__tests__/generator/__snapshots__/provider-functions.test.ts.snap +444 -0
  70. package/src/get/__tests__/generator/__snapshots__/provider.test.ts.snap +8 -8
  71. package/src/get/__tests__/generator/__snapshots__/resource-types.test.ts.snap +126 -126
  72. package/src/get/__tests__/generator/__snapshots__/skipped-attributes.test.ts.snap +17 -17
  73. package/src/get/__tests__/generator/__snapshots__/types.test.ts.snap +201 -52
  74. package/src/get/__tests__/generator/__snapshots__/write-only.test.ts.snap +262 -0
  75. package/src/get/__tests__/generator/complex-computed-types.test.ts +28 -0
  76. package/src/get/__tests__/generator/deep-nested-attributes.test.ts +56 -0
  77. package/src/get/__tests__/generator/description-escaping.test.ts +84 -0
  78. package/src/get/__tests__/generator/empty-provider-resources.test.ts +26 -0
  79. package/src/get/__tests__/generator/ephemeral-resources.test.ts +46 -0
  80. package/src/get/__tests__/generator/export-sharding.test.ts +169 -0
  81. package/src/get/__tests__/generator/fixtures/deeply-nested-collections.test.fixture.json +129 -0
  82. package/src/get/__tests__/generator/fixtures/ephemeral-resources.test.fixture.json +160 -0
  83. package/src/get/__tests__/generator/fixtures/provider-functions-collision.test.fixture.json +31 -0
  84. package/src/get/__tests__/generator/fixtures/provider-functions-synthetic.test.fixture.json +192 -0
  85. package/src/get/__tests__/generator/fixtures/provider-functions.test.fixture.json +170 -0
  86. package/src/get/__tests__/generator/fixtures/write-only.test.fixture.json +58 -0
  87. package/src/get/__tests__/generator/import-style.test.ts +129 -0
  88. package/src/get/__tests__/generator/module-generator.test.ts +528 -0
  89. package/src/get/__tests__/generator/nested-types.test.ts +54 -0
  90. package/src/get/__tests__/generator/provider-functions.test.ts +262 -0
  91. package/src/get/__tests__/generator/provider-source-matching.test.ts +84 -0
  92. package/src/get/__tests__/generator/provider.test.ts +59 -0
  93. package/src/get/__tests__/generator/resource-types.test.ts +118 -0
  94. package/src/get/__tests__/generator/skipped-attributes.test.ts +72 -0
  95. package/src/get/__tests__/generator/supported-stored-classes.test.ts +147 -0
  96. package/src/get/__tests__/generator/types.test.ts +686 -0
  97. package/src/get/__tests__/generator/versions-file.test.ts +72 -0
  98. package/src/get/__tests__/generator/write-only.test.ts +71 -0
  99. package/src/get/__tests__/target-versions.test.ts +206 -0
  100. package/src/get/__tests__/util.ts +75 -0
  101. package/src/get/constructs-maker.ts +885 -0
  102. package/src/get/generator/custom-defaults.ts +493 -0
  103. package/src/get/generator/emitter/attributes-emitter.ts +316 -0
  104. package/src/get/generator/emitter/index.ts +6 -0
  105. package/src/get/generator/emitter/provider-functions-emitter.ts +117 -0
  106. package/src/get/generator/emitter/resource-emitter.ts +330 -0
  107. package/src/get/generator/emitter/struct-emitter.ts +683 -0
  108. package/src/get/generator/loop-detection.ts +81 -0
  109. package/src/get/generator/models/attribute-model.ts +273 -0
  110. package/src/get/generator/models/attribute-type-model.ts +560 -0
  111. package/src/get/generator/models/index.ts +8 -0
  112. package/src/get/generator/models/provider-function-model.ts +872 -0
  113. package/src/get/generator/models/resource-model.ts +183 -0
  114. package/src/get/generator/models/scope.ts +54 -0
  115. package/src/get/generator/models/struct.ts +124 -0
  116. package/src/get/generator/models/supported-stored-classes.ts +88 -0
  117. package/src/get/generator/module-generator.ts +234 -0
  118. package/src/get/generator/provider-generator.ts +428 -0
  119. package/src/get/generator/resource-parser.ts +764 -0
  120. package/src/get/generator/sanitized-comments.ts +49 -0
  121. package/src/get/generator/skipped-attributes.ts +27 -0
  122. package/src/index.ts +40 -0
  123. package/src/util.ts +26 -0
  124. package/tsconfig.json +1 -2
@@ -0,0 +1,428 @@
1
+ // Copyright (c) HashiCorp, Inc
2
+ // SPDX-License-Identifier: MPL-2.0
3
+ import { CodeMaker, toCamelCase } from "codemaker";
4
+ import {
5
+ ConstructsMakerProviderTarget,
6
+ ConstructsMakerTarget,
7
+ LANGUAGES,
8
+ logger,
9
+ ProviderSchema,
10
+ TerraformProviderConstraint,
11
+ } from "@cdktn/commons";
12
+ import { FQPN, parseFQPN, ProviderName } from "@cdktn/provider-schema";
13
+ import {
14
+ ProviderFunctionsModel,
15
+ ResourceModel,
16
+ assertNoFunctionsGetterCollision,
17
+ buildProviderFunctionsModel,
18
+ } from "./models";
19
+ import { ResourceParser } from "./resource-parser";
20
+ import {
21
+ ProviderFunctionsEmitter,
22
+ ResourceEmitter,
23
+ StructEmitter,
24
+ } from "./emitter";
25
+
26
+ export interface TerraformProviderGeneratorOptions {
27
+ /**
28
+ * File extension to write on emitted relative imports.
29
+ * Defaults to "" (CommonJS).
30
+ */
31
+ readonly importExtension?: string;
32
+ }
33
+
34
+ interface ProviderData {
35
+ name: string;
36
+ source: string;
37
+ version: string;
38
+ }
39
+
40
+ const isMatching = (
41
+ target: ConstructsMakerTarget,
42
+ terraformSchemaName: string,
43
+ ): boolean => {
44
+ if (target.isModule) return false;
45
+
46
+ const elements = terraformSchemaName.split("/");
47
+
48
+ if (elements.length === 1) {
49
+ return target.source === terraformSchemaName;
50
+ } else {
51
+ const [hostname, namespace, provider] = elements;
52
+
53
+ if (!hostname || !namespace || !provider) {
54
+ throw new Error(`can't handle ${terraformSchemaName}`);
55
+ }
56
+
57
+ // If target.source is set, try to match it
58
+ if (target.source) {
59
+ const targetSource = {
60
+ // defaults
61
+ hostname: "registry.terraform.io",
62
+ namespace: "hashicorp",
63
+ name: "",
64
+ };
65
+ const targetElements = target.source.split("/");
66
+ // Only a 3-part source explicitly pins a registry hostname. Shorthand
67
+ // sources (e.g. "hashicorp/awscc") omit it, so we must not assume a
68
+ // default: Terraform resolves them under registry.terraform.io while
69
+ // OpenTofu resolves them under registry.opentofu.org. Comparing against a
70
+ // hard-coded default would make schema lookup fail under OpenTofu even
71
+ // though the provider was installed correctly.
72
+ const explicitHostname = targetElements.length === 3;
73
+ switch (targetElements.length) {
74
+ case 1: // only name set
75
+ targetSource.name = targetElements[0].toLowerCase();
76
+ break;
77
+ case 2: // namespace and name set
78
+ targetSource.namespace = targetElements[0].toLowerCase();
79
+ targetSource.name = targetElements[1].toLowerCase();
80
+ break;
81
+ case 3: // hostname, namespace and name set
82
+ targetSource.hostname = targetElements[0].toLowerCase();
83
+ targetSource.namespace = targetElements[1].toLowerCase();
84
+ targetSource.name = targetElements[2].toLowerCase();
85
+ break;
86
+ default:
87
+ throw new Error(
88
+ `can't handle ${target.source}. Expected string with 1, 2 or 3 elements separated by '/'`,
89
+ );
90
+ }
91
+
92
+ return (
93
+ (!explicitHostname || targetSource.hostname === hostname) &&
94
+ targetSource.namespace === namespace &&
95
+ targetSource.name === provider
96
+ );
97
+ }
98
+
99
+ // Else, try to match target.name to the provider name
100
+
101
+ return target.name === provider;
102
+ }
103
+ };
104
+
105
+ export interface ProviderConstraints {
106
+ [fqn: string]: ProviderData;
107
+ }
108
+
109
+ export class TerraformProviderGenerator {
110
+ private resourceParser = new ResourceParser();
111
+ private resourceEmitter: ResourceEmitter;
112
+ private structEmitter: StructEmitter;
113
+ private providerFunctionsEmitter: ProviderFunctionsEmitter;
114
+ private readonly importExtension: string;
115
+ public versions: { [fqpn: string]: string | undefined } = {};
116
+
117
+ constructor(
118
+ private readonly code: CodeMaker,
119
+ private readonly schema: ProviderSchema,
120
+ options: TerraformProviderGeneratorOptions = {},
121
+ ) {
122
+ this.code.indentation = 2;
123
+ this.importExtension = options.importExtension ?? "";
124
+ this.resourceEmitter = new ResourceEmitter(this.code, this.importExtension);
125
+ this.structEmitter = new StructEmitter(this.code, this.importExtension);
126
+ this.providerFunctionsEmitter = new ProviderFunctionsEmitter(this.code);
127
+ }
128
+
129
+ private getProviderByConstraint(
130
+ providerConstraint: ConstructsMakerTarget,
131
+ ): FQPN | undefined {
132
+ return Object.keys(this.schema.provider_schemas || {}).find((fqpn) =>
133
+ isMatching(providerConstraint, fqpn),
134
+ ) as FQPN | undefined;
135
+ }
136
+
137
+ public generate(providerConstraint: ConstructsMakerTarget) {
138
+ const fqpn = this.getProviderByConstraint(providerConstraint);
139
+ if (!fqpn) {
140
+ logger.debug(
141
+ `Could not find provider constraint for ${providerConstraint} in schema: ${JSON.stringify(
142
+ this.schema,
143
+ null,
144
+ 2,
145
+ )}`,
146
+ );
147
+ throw new Error(
148
+ `Could not find provider with constraint ${JSON.stringify(
149
+ providerConstraint,
150
+ )}`,
151
+ );
152
+ }
153
+
154
+ const providerVersion = this.schema.provider_versions?.[fqpn];
155
+ this.emitProvider(fqpn, providerVersion, providerConstraint);
156
+ this.versions[fqpn] = providerVersion;
157
+ }
158
+
159
+ public generateAll() {
160
+ for (const fqpn of Object.keys(this.schema.provider_schemas || {})) {
161
+ this.generate(
162
+ new ConstructsMakerProviderTarget(
163
+ new TerraformProviderConstraint(fqpn),
164
+ LANGUAGES[0],
165
+ ),
166
+ );
167
+ }
168
+ }
169
+
170
+ public async save(outdir: string) {
171
+ await this.code.save(outdir);
172
+ }
173
+
174
+ public buildResourceModels(
175
+ fqpn: FQPN,
176
+ constraint?: ConstructsMakerTarget,
177
+ ): ResourceModel[] {
178
+ const provider = this.schema.provider_schemas?.[fqpn];
179
+ if (!provider) {
180
+ throw new Error(`Can not find provider '${fqpn}' in schema`);
181
+ }
182
+
183
+ const resources = Object.entries(provider.resource_schemas || {}).map(
184
+ ([type, resource]) =>
185
+ this.resourceParser.parse(fqpn, type, resource, "resource", constraint),
186
+ );
187
+
188
+ const dataSources = Object.entries(provider.data_source_schemas || {}).map(
189
+ ([type, resource]) =>
190
+ this.resourceParser.parse(
191
+ fqpn,
192
+ `data_${type}`,
193
+ resource,
194
+ "data_source",
195
+ constraint,
196
+ ),
197
+ );
198
+
199
+ const ephemeralResources = Object.entries(
200
+ provider.ephemeral_resource_schemas || {},
201
+ ).map(([type, resource]) =>
202
+ this.resourceParser.parse(
203
+ fqpn,
204
+ `ephemeral_${type}`,
205
+ resource,
206
+ "ephemeral_resource",
207
+ constraint,
208
+ ),
209
+ );
210
+
211
+ // CRITICAL: ephemeral resources must be appended AFTER resources and data
212
+ // sources - uniqueClassName dedup is order-dependent and existing
213
+ // prebuilt snapshots must not churn.
214
+ return ([] as ResourceModel[]).concat(
215
+ ...resources,
216
+ ...dataSources,
217
+ ...ephemeralResources,
218
+ );
219
+ }
220
+
221
+ public getClassNameForResource(terraformType: string) {
222
+ return this.resourceParser.getClassNameForResource(terraformType);
223
+ }
224
+
225
+ public getNamespaceNameForResource(terraformType: string) {
226
+ return this.resourceParser.getNamespaceNameForResource(terraformType);
227
+ }
228
+
229
+ private emitProvider(
230
+ fqpn: FQPN,
231
+ providerVersion?: string,
232
+ constraint?: ConstructsMakerTarget,
233
+ ) {
234
+ const name = constraint?.name
235
+ ? (constraint.name as ProviderName)
236
+ : parseFQPN(fqpn).name;
237
+ const provider = this.schema.provider_schemas?.[fqpn];
238
+ if (!provider) {
239
+ throw new Error(`Can not find provider '${fqpn}' in schema`);
240
+ }
241
+
242
+ const files: string[] = [];
243
+ this.buildResourceModels(fqpn, constraint).forEach((resourceModel) => {
244
+ if (constraint) {
245
+ resourceModel.providerVersionConstraint = constraint.version;
246
+ resourceModel.terraformProviderSource = constraint.source;
247
+ }
248
+ resourceModel.providerVersion = providerVersion;
249
+
250
+ if (resourceModel.structsRequireSharding) {
251
+ files.push(this.emitResourceWithComplexStruct(resourceModel));
252
+ } else {
253
+ files.push(this.emitResource(resourceModel));
254
+ }
255
+ this.emitResourceReadme(resourceModel);
256
+ });
257
+
258
+ // Built before the provider class is emitted (rather than after, as
259
+ // resources/data sources/ephemeral resources above already are) so that
260
+ // the model can be attached to providerResource and picked up by
261
+ // ResourceEmitter while it still emits the provider class file below -
262
+ // see the engineering brief for why this ordering matters.
263
+ const providerFunctionsModel = buildProviderFunctionsModel(
264
+ name,
265
+ provider.functions,
266
+ );
267
+
268
+ if (provider.provider) {
269
+ const providerResource = this.resourceParser.parse(
270
+ fqpn,
271
+ `provider`,
272
+ provider.provider,
273
+ "provider",
274
+ constraint,
275
+ );
276
+ if (constraint) {
277
+ providerResource.providerVersionConstraint = constraint.version;
278
+ providerResource.terraformProviderSource = constraint.source;
279
+ providerResource.terraformProviderName = constraint.name;
280
+ }
281
+ providerResource.providerVersion = providerVersion;
282
+
283
+ if (providerFunctionsModel) {
284
+ assertNoFunctionsGetterCollision(
285
+ name,
286
+ providerResource.attributes.map((att) => att.name),
287
+ );
288
+ providerResource.providerFunctionsModel = providerFunctionsModel;
289
+ }
290
+
291
+ files.push(this.emitResource(providerResource));
292
+ this.emitResourceReadme(providerResource);
293
+ }
294
+
295
+ if (providerFunctionsModel) {
296
+ files.push(this.emitProviderFunctions(name, providerFunctionsModel));
297
+ }
298
+
299
+ this.emitIndexFile(name, files);
300
+ this.emitLazyIndexFile(name, files);
301
+ }
302
+
303
+ private emitProviderFunctions(
304
+ provider: ProviderName,
305
+ model: ProviderFunctionsModel,
306
+ ): string {
307
+ const filePath = `providers/${provider}/provider-functions/index.ts`;
308
+ this.code.openFile(filePath);
309
+ this.code.line(`// generated from provider function schema`);
310
+ this.code.line();
311
+ this.providerFunctionsEmitter.emit(model);
312
+ this.code.closeFile(filePath);
313
+ return filePath;
314
+ }
315
+
316
+ private emitResourceReadme(resource: ResourceModel): void {
317
+ const filePath = `${resource.namespaceFolderPath}/README.md`;
318
+ this.code.openFile(filePath);
319
+ this.code.line(`# \`${resource.terraformType}\``);
320
+ this.code.line();
321
+ const type = resource.isProvider
322
+ ? resource.provider
323
+ : resource.terraformType;
324
+ this.code.line(
325
+ `Refer to the Terraform Registry for docs: [\`${type}\`](${resource.linkToDocs}).`,
326
+ );
327
+ this.code.closeFile(filePath);
328
+ }
329
+
330
+ private emitIndexFile(provider: ProviderName, files: string[]): void {
331
+ const folder = `providers/${provider}`;
332
+ const filePath = `${folder}/index.ts`;
333
+ this.code.openFile(filePath);
334
+ this.code.line("// generated by cdktn get");
335
+ for (const file of files) {
336
+ const dirName = file.replace(`${folder}/`, "").replace("/index.ts", "");
337
+ this.code.line(
338
+ `export * as ${toCamelCase(dirName)} from './${dirName}/index${this.importExtension}';`,
339
+ );
340
+ }
341
+ this.code.line();
342
+ this.code.closeFile(filePath);
343
+ }
344
+
345
+ private emitLazyIndexFile(provider: ProviderName, files: string[]): void {
346
+ const folder = `providers/${provider}`;
347
+ const filePath = `${folder}/lazy-index.ts`;
348
+ this.code.openFile(filePath);
349
+ this.code.line("// generated by cdktn get");
350
+ for (const file of files) {
351
+ const dirName = file.replace(`${folder}/`, "").replace("/index.ts", "");
352
+ this.code.line(
353
+ `Object.defineProperty(exports, '${toCamelCase(
354
+ dirName,
355
+ )}', { get: function () { return require('./${dirName}'); } });`,
356
+ );
357
+ }
358
+ this.code.line();
359
+ this.code.closeFile(filePath);
360
+ }
361
+
362
+ private emitResource(resource: ResourceModel): string {
363
+ this.code.openFile(resource.filePath);
364
+ this.emitFileHeader(resource);
365
+ this.structEmitter.emit(resource);
366
+ this.resourceEmitter.emit(resource);
367
+ this.code.closeFile(resource.filePath);
368
+
369
+ return resource.filePath;
370
+ }
371
+
372
+ private emitResourceWithComplexStruct(resource: ResourceModel) {
373
+ const generatedFiles = [];
374
+
375
+ // drop the last segment of the filepath
376
+ const filePath = resource.filePath;
377
+ this.code.openFile(filePath);
378
+ this.code.line(`// generated from terraform resource schema`);
379
+ this.code.line();
380
+
381
+ if (resource.structsRequireSharding) {
382
+ const structsSpecifier = `./${resource.structsFolderName}/index${this.importExtension}`;
383
+ if (resource.referencedTypes.length > 0) {
384
+ this.code.line(
385
+ `import { ${resource.referencedTypes.join(
386
+ ", \n",
387
+ )}} from '${structsSpecifier}';`,
388
+ );
389
+ }
390
+
391
+ this.code.line(`export * from '${structsSpecifier}';`);
392
+
393
+ resource.importStatements.forEach((statement) =>
394
+ this.code.line(statement),
395
+ );
396
+
397
+ this.structEmitter.emitInterface(resource, resource.configStruct);
398
+ this.resourceEmitter.emit(resource);
399
+
400
+ this.code.closeFile(filePath);
401
+
402
+ this.structEmitter.emit(resource);
403
+ generatedFiles.push(resource.fileName);
404
+ generatedFiles.push(resource.structsFolderName);
405
+ } else {
406
+ resource.importStatements.forEach((statement) =>
407
+ this.code.line(statement),
408
+ );
409
+
410
+ this.structEmitter.emit(resource);
411
+ this.resourceEmitter.emit(resource);
412
+ this.code.closeFile(filePath);
413
+ generatedFiles.push(resource.fileName);
414
+ }
415
+
416
+ return filePath;
417
+ }
418
+
419
+ private emitFileHeader(resource: ResourceModel) {
420
+ this.code.line(`// ${resource.linkToDocs}`);
421
+ this.code.line(`// generated from terraform resource schema`);
422
+ this.code.line();
423
+ resource.importStatements.forEach((statement) => this.code.line(statement));
424
+ this.code.line();
425
+ this.code.line("// Configuration");
426
+ this.code.line();
427
+ }
428
+ }