@cdktn/hcl2cdk 0.24.0-pre.43 → 0.24.0-pre.47
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/LICENSE +355 -0
- package/README.md +1 -1
- package/build/__tests__/expressions.test.js +10 -19
- package/build/__tests__/functions.test.js +8 -18
- package/build/__tests__/testHelpers.js +3 -2
- package/build/coerceType.js +11 -21
- package/build/dynamic-blocks.js +3 -3
- package/build/expressions.js +13 -22
- package/build/function-bindings/functions.generated.js +2 -2
- package/build/generation.js +24 -34
- package/build/index.js +15 -25
- package/build/iteration.js +7 -6
- package/build/jsii-rosetta-workarounds.js +6 -5
- package/build/partialCode.js +11 -20
- package/build/provider.js +4 -3
- package/build/references.js +6 -5
- package/build/schema.js +8 -18
- package/build/terraformSchema.js +4 -4
- package/build/utils.js +3 -3
- package/build/variables.js +12 -21
- package/package.json +20 -17
- package/package.sh +1 -1
- package/src/__tests__/coerceType.test.ts +207 -0
- package/src/__tests__/expressionToTs.test.ts +1167 -0
- package/src/__tests__/expressions.test.ts +541 -0
- package/src/__tests__/findExpressionType.test.ts +112 -0
- package/src/__tests__/functions.test.ts +768 -0
- package/src/__tests__/generation.test.ts +72 -0
- package/src/__tests__/jsii-rosetta-workarounds.test.ts +145 -0
- package/src/__tests__/partialCode.test.ts +432 -0
- package/src/__tests__/terraformSchema.test.ts +107 -0
- package/src/__tests__/testHelpers.ts +11 -0
- package/src/coerceType.ts +261 -0
- package/src/dynamic-blocks.ts +61 -0
- package/src/expressions.ts +968 -0
- package/src/function-bindings/functions.generated.ts +1139 -0
- package/src/function-bindings/functions.ts +104 -0
- package/src/generation.ts +1189 -0
- package/src/index.ts +584 -0
- package/src/iteration.ts +156 -0
- package/src/jsii-rosetta-workarounds.ts +145 -0
- package/src/partialCode.ts +132 -0
- package/src/provider.ts +60 -0
- package/src/references.ts +193 -0
- package/src/schema.ts +74 -0
- package/src/terraformSchema.ts +182 -0
- package/src/types.ts +58 -0
- package/src/utils.ts +19 -0
- package/src/variables.ts +214 -0
- package/test/__snapshots__/backends.test.ts.snap +70 -0
- package/test/__snapshots__/externals.test.ts.snap +37 -0
- package/test/__snapshots__/granular-imports.test.ts.snap +180 -0
- package/test/__snapshots__/imports.test.ts.snap +159 -0
- package/test/__snapshots__/iteration.test.ts.snap +532 -0
- package/test/__snapshots__/jsiiLanguage.test.ts.snap +347 -0
- package/test/__snapshots__/locals.test.ts.snap +55 -0
- package/test/__snapshots__/modules.test.ts.snap +127 -0
- package/test/__snapshots__/outputs.test.ts.snap +77 -0
- package/test/__snapshots__/partialCode.test.ts.snap +120 -0
- package/test/__snapshots__/provider.test.ts.snap +128 -0
- package/test/__snapshots__/references.test.ts.snap +376 -0
- package/test/__snapshots__/resource-meta-properties.test.ts.snap +342 -0
- package/test/__snapshots__/resources.test.ts.snap +613 -0
- package/test/__snapshots__/tfExpressions.test.ts.snap +537 -0
- package/test/__snapshots__/typeCoercion.test.ts.snap +253 -0
- package/test/__snapshots__/variables.test.ts.snap +150 -0
- package/test/backends.test.ts +75 -0
- package/test/convertProject.test.ts +257 -0
- package/test/externals.test.ts +35 -0
- package/test/globalSetup.ts +224 -0
- package/test/globalTeardown.ts +11 -0
- package/test/granular-imports.test.ts +161 -0
- package/test/hcl2cdk.test.ts +88 -0
- package/test/helpers/convert.ts +543 -0
- package/test/helpers/tmp.ts +25 -0
- package/test/imports.test.ts +141 -0
- package/test/iteration.test.ts +342 -0
- package/test/jsiiLanguage.test.ts +73 -0
- package/test/locals.test.ts +47 -0
- package/test/modules.test.ts +143 -0
- package/test/outputs.test.ts +69 -0
- package/test/partialCode.test.ts +25 -0
- package/test/provider.test.ts +106 -0
- package/test/references.test.ts +287 -0
- package/test/resource-meta-properties.test.ts +288 -0
- package/test/resources.test.ts +551 -0
- package/test/tfExpressions.test.ts +300 -0
- package/test/typeCoercion.test.ts +154 -0
- package/test/variables.test.ts +96 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) HashiCorp, Inc.
|
|
3
|
+
* SPDX-License-Identifier: MPL-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { ProviderSchema } from "@cdktn/commons";
|
|
7
|
+
import { getTypeAtPath } from "../terraformSchema";
|
|
8
|
+
|
|
9
|
+
describe("getTypeAtPath", () => {
|
|
10
|
+
const schema: ProviderSchema = {
|
|
11
|
+
provider_schemas: {
|
|
12
|
+
test: {
|
|
13
|
+
provider: {} as any,
|
|
14
|
+
data_source_schemas: {},
|
|
15
|
+
resource_schemas: {
|
|
16
|
+
test_resource: {
|
|
17
|
+
version: 1,
|
|
18
|
+
block: {
|
|
19
|
+
attributes: {
|
|
20
|
+
stringAttribute: {
|
|
21
|
+
type: "string",
|
|
22
|
+
},
|
|
23
|
+
numberAttribute: {
|
|
24
|
+
type: "number",
|
|
25
|
+
},
|
|
26
|
+
booleanAttribute: {
|
|
27
|
+
type: "bool",
|
|
28
|
+
},
|
|
29
|
+
stringListAttribute: {
|
|
30
|
+
type: ["list", "string"],
|
|
31
|
+
},
|
|
32
|
+
stringSetAttribute: {
|
|
33
|
+
type: ["set", "string"],
|
|
34
|
+
},
|
|
35
|
+
stringMapAttribute: {
|
|
36
|
+
type: ["map", "string"],
|
|
37
|
+
},
|
|
38
|
+
stringListListAttribute: {
|
|
39
|
+
type: ["list", ["list", "string"]],
|
|
40
|
+
},
|
|
41
|
+
objectAttribute: {
|
|
42
|
+
type: [
|
|
43
|
+
"object",
|
|
44
|
+
{
|
|
45
|
+
stringAttribute: "string",
|
|
46
|
+
},
|
|
47
|
+
],
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
block_types: {
|
|
51
|
+
listMode: {
|
|
52
|
+
nesting_mode: "list",
|
|
53
|
+
block: {
|
|
54
|
+
attributes: {
|
|
55
|
+
stringAttribute: {
|
|
56
|
+
type: "string",
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
block_types: {
|
|
60
|
+
listMode: {
|
|
61
|
+
nesting_mode: "list",
|
|
62
|
+
block: {
|
|
63
|
+
attributes: {
|
|
64
|
+
stringAttribute: {
|
|
65
|
+
type: "string",
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
block_types: {},
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
it("should return null if it can not find the resource", () => {
|
|
83
|
+
expect(getTypeAtPath(schema, "test.nonExistantResource")).toBeNull();
|
|
84
|
+
});
|
|
85
|
+
it("should return null if it can not find the attribute on the resource", () => {
|
|
86
|
+
expect(getTypeAtPath(schema, "test.resource.ponyfoo")).toBeNull();
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
describe("known types", () => {
|
|
90
|
+
it.each([
|
|
91
|
+
["stringAttribute", "string"],
|
|
92
|
+
["numberAttribute", "number"],
|
|
93
|
+
["booleanAttribute", "bool"],
|
|
94
|
+
["stringListAttribute", ["list", "string"]],
|
|
95
|
+
["stringSetAttribute", ["set", "string"]],
|
|
96
|
+
["stringMapAttribute", ["map", "string"]],
|
|
97
|
+
["stringMapAttribute.whateverkey", "string"],
|
|
98
|
+
["stringListListAttribute", ["list", ["list", "string"]]],
|
|
99
|
+
["stringListListAttribute.[]", ["list", "string"]],
|
|
100
|
+
["stringListListAttribute.[].[]", "string"],
|
|
101
|
+
["listMode.[].listMode.[].stringAttribute", "string"],
|
|
102
|
+
["objectAttribute.stringAttribute", "string"],
|
|
103
|
+
])(`access to %s returns %s`, (attr, res) => {
|
|
104
|
+
expect(getTypeAtPath(schema, `test.resource.${attr}`)).toEqual(res);
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
});
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
// Copyright (c) HashiCorp, Inc
|
|
2
|
+
// SPDX-License-Identifier: MPL-2.0
|
|
3
|
+
import template from "@babel/template";
|
|
4
|
+
import * as t from "@babel/types";
|
|
5
|
+
import { logger, AttributeType } from "@cdktn/commons";
|
|
6
|
+
import { ProgramScope } from "./types";
|
|
7
|
+
import deepEqual from "deep-equal";
|
|
8
|
+
import { tsFunctionsMap } from "./function-bindings/functions";
|
|
9
|
+
import { getTypeAtPath } from "./terraformSchema";
|
|
10
|
+
import { toSnakeCase } from "codemaker";
|
|
11
|
+
|
|
12
|
+
function changeValueAccessor(
|
|
13
|
+
ast: t.MemberExpression,
|
|
14
|
+
newAccessor: string,
|
|
15
|
+
): t.MemberExpression {
|
|
16
|
+
const propertyIdentifier: t.Identifier = {
|
|
17
|
+
...(ast.property as t.Identifier),
|
|
18
|
+
name: newAccessor,
|
|
19
|
+
};
|
|
20
|
+
return {
|
|
21
|
+
...ast,
|
|
22
|
+
property: propertyIdentifier,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function typeForCallExpression(ast: t.CallExpression): AttributeType {
|
|
27
|
+
// Find all cdktf.Fn.* || cdktn.Fn.* calls
|
|
28
|
+
if (
|
|
29
|
+
t.isMemberExpression(ast.callee) &&
|
|
30
|
+
t.isMemberExpression(ast.callee.object) &&
|
|
31
|
+
t.isIdentifier(ast.callee.object.object) &&
|
|
32
|
+
(ast.callee.object.object.name === "cdktf" ||
|
|
33
|
+
ast.callee.object.object.name === "cdktn") &&
|
|
34
|
+
t.isIdentifier(ast.callee.object.property) &&
|
|
35
|
+
ast.callee.object.property.name === "Fn" &&
|
|
36
|
+
t.isIdentifier(ast.callee.property)
|
|
37
|
+
) {
|
|
38
|
+
const meta = tsFunctionsMap[ast.callee.property.name];
|
|
39
|
+
if (meta) {
|
|
40
|
+
return meta.returnType;
|
|
41
|
+
} else {
|
|
42
|
+
return "dynamic";
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// cdktf.conditional, cdktf.propertyAccess, cdktf.Op.* are all dynamic
|
|
47
|
+
// By default we assume dynamic
|
|
48
|
+
return "dynamic";
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export const coerceType = (
|
|
52
|
+
scope: ProgramScope,
|
|
53
|
+
ast: t.Expression,
|
|
54
|
+
from: AttributeType,
|
|
55
|
+
to: AttributeType | undefined,
|
|
56
|
+
): t.Expression => {
|
|
57
|
+
if (to === undefined) {
|
|
58
|
+
return ast;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (deepEqual(to, from)) {
|
|
62
|
+
return ast;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const isTerraformVariableOrLocal =
|
|
66
|
+
ast.type === "MemberExpression" &&
|
|
67
|
+
ast.property.type === "Identifier" &&
|
|
68
|
+
ast.property.name === "value" &&
|
|
69
|
+
ast.object.type === "Identifier" &&
|
|
70
|
+
Object.values(scope.variables).some(
|
|
71
|
+
(knownVars) =>
|
|
72
|
+
knownVars.variableName === (ast.object as t.Identifier).name &&
|
|
73
|
+
["var", "local"].includes(knownVars.resource),
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
const addTokenToImports = () =>
|
|
77
|
+
scope.importables.push({
|
|
78
|
+
constructName: "Token",
|
|
79
|
+
provider: "cdktn",
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
if (Array.isArray(to)) {
|
|
83
|
+
if (to[0] === "list" || to[0] === "set") {
|
|
84
|
+
switch (to[1]) {
|
|
85
|
+
case "string":
|
|
86
|
+
if (isTerraformVariableOrLocal) {
|
|
87
|
+
return changeValueAccessor(ast as t.MemberExpression, "listValue");
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
addTokenToImports();
|
|
91
|
+
return template.expression(`Token.asList(%%ast%%)`)({
|
|
92
|
+
ast: ast,
|
|
93
|
+
}) as t.Expression;
|
|
94
|
+
case "number":
|
|
95
|
+
addTokenToImports();
|
|
96
|
+
return template.expression(`Token.asNumberList(%%ast%%)`)({
|
|
97
|
+
ast: ast,
|
|
98
|
+
}) as t.Expression;
|
|
99
|
+
case "bool":
|
|
100
|
+
addTokenToImports();
|
|
101
|
+
return template.expression(`Token.asAny(%%ast%%)`)({
|
|
102
|
+
ast: ast,
|
|
103
|
+
}) as t.Expression;
|
|
104
|
+
default:
|
|
105
|
+
addTokenToImports();
|
|
106
|
+
return template.expression(`Token.asAny(%%ast%%)`)({
|
|
107
|
+
ast: ast,
|
|
108
|
+
}) as t.Expression;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (to[0] === "map") {
|
|
113
|
+
switch (to[1]) {
|
|
114
|
+
case "string":
|
|
115
|
+
addTokenToImports();
|
|
116
|
+
return template.expression(`Token.asStringMap(%%ast%%)`)({
|
|
117
|
+
ast: ast,
|
|
118
|
+
}) as t.Expression;
|
|
119
|
+
case "number":
|
|
120
|
+
addTokenToImports();
|
|
121
|
+
return template.expression(`Token.asNumberMap(%%ast%%)`)({
|
|
122
|
+
ast: ast,
|
|
123
|
+
}) as t.Expression;
|
|
124
|
+
case "bool":
|
|
125
|
+
addTokenToImports();
|
|
126
|
+
return template.expression(`Token.asBooleanMap(%%ast%%)`)({
|
|
127
|
+
ast: ast,
|
|
128
|
+
}) as t.Expression;
|
|
129
|
+
default:
|
|
130
|
+
addTokenToImports();
|
|
131
|
+
return template.expression(`Token.asAnyMap(%%ast%%)`)({
|
|
132
|
+
ast: ast,
|
|
133
|
+
}) as t.Expression;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
switch (to) {
|
|
139
|
+
case "number":
|
|
140
|
+
if (isTerraformVariableOrLocal) {
|
|
141
|
+
return changeValueAccessor(ast as t.MemberExpression, "numberValue");
|
|
142
|
+
}
|
|
143
|
+
addTokenToImports();
|
|
144
|
+
return template.expression(`Token.asNumber(%%ast%%)`)({
|
|
145
|
+
ast: ast,
|
|
146
|
+
}) as t.Expression;
|
|
147
|
+
case "string":
|
|
148
|
+
if (isTerraformVariableOrLocal) {
|
|
149
|
+
return changeValueAccessor(ast as t.MemberExpression, "stringValue");
|
|
150
|
+
}
|
|
151
|
+
addTokenToImports();
|
|
152
|
+
return template.expression(`Token.asString(%%ast%%)`)({
|
|
153
|
+
ast: ast,
|
|
154
|
+
}) as t.Expression;
|
|
155
|
+
case "bool":
|
|
156
|
+
if (isTerraformVariableOrLocal) {
|
|
157
|
+
return changeValueAccessor(ast as t.MemberExpression, "booleanValue");
|
|
158
|
+
}
|
|
159
|
+
addTokenToImports();
|
|
160
|
+
return template.expression(`Token.asBoolean(%%ast%%)`)({
|
|
161
|
+
ast: ast,
|
|
162
|
+
}) as t.Expression;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
logger.debug(`Could not coerce from ${from} to ${to} for ${ast}`);
|
|
166
|
+
return ast;
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
export function findExpressionType(
|
|
170
|
+
scope: ProgramScope,
|
|
171
|
+
ast: t.Expression,
|
|
172
|
+
): AttributeType {
|
|
173
|
+
const isReferenceWithoutTemplateString =
|
|
174
|
+
ast.type === "MemberExpression" && ast.object.type === "Identifier";
|
|
175
|
+
|
|
176
|
+
// If we have a property to cdktf.propertyAccess call it's dynamic
|
|
177
|
+
if (ast.type === "CallExpression") {
|
|
178
|
+
return typeForCallExpression(ast);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (ast.type === "StringLiteral") {
|
|
182
|
+
return "string";
|
|
183
|
+
}
|
|
184
|
+
if (ast.type === "NumericLiteral") {
|
|
185
|
+
return "number";
|
|
186
|
+
}
|
|
187
|
+
if (ast.type === "BooleanLiteral") {
|
|
188
|
+
return "bool";
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// If we only have one reference this is a
|
|
192
|
+
if (isReferenceWithoutTemplateString) {
|
|
193
|
+
const destructuredAst = destructureAst(ast);
|
|
194
|
+
if (!destructuredAst) {
|
|
195
|
+
logger.debug(
|
|
196
|
+
`Could not destructure ast: ${JSON.stringify(ast, null, 2)}`,
|
|
197
|
+
);
|
|
198
|
+
return "dynamic";
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const [astVariableName, ...attributes] = destructuredAst;
|
|
202
|
+
const variable = Object.values(scope.variables).find(
|
|
203
|
+
(x) => x.variableName === astVariableName,
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
if (!variable) {
|
|
207
|
+
logger.debug(
|
|
208
|
+
`Could not find variable ${astVariableName} given scope: ${JSON.stringify(
|
|
209
|
+
scope.variables,
|
|
210
|
+
null,
|
|
211
|
+
2,
|
|
212
|
+
)}`,
|
|
213
|
+
);
|
|
214
|
+
// We don't know, this should not happen, but if it does we assume the worst case and make it dynamic
|
|
215
|
+
return "dynamic";
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (variable.resource === "var") {
|
|
219
|
+
return "dynamic";
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const { resource: resourceType } = variable;
|
|
223
|
+
const [provider, ...resourceNameFragments] = resourceType.split("_");
|
|
224
|
+
const tfResourcePath = `${provider}.${resourceNameFragments.join(
|
|
225
|
+
"_",
|
|
226
|
+
)}.${attributes.map((x) => toSnakeCase(x)).join(".")}`;
|
|
227
|
+
const type = getTypeAtPath(scope.providerSchema, tfResourcePath);
|
|
228
|
+
|
|
229
|
+
// If this is an attribute type we can return it
|
|
230
|
+
if (typeof type === "string" || Array.isArray(type)) {
|
|
231
|
+
return type;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Either nothing is found or it's a block type
|
|
235
|
+
return "dynamic";
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
return "string";
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/*
|
|
242
|
+
* Transforms a babel AST into a list of string accessors
|
|
243
|
+
* e.g. foo.bar.baz -> ["foo", "bar", "baz"]
|
|
244
|
+
*/
|
|
245
|
+
function destructureAst(ast: t.Expression): string[] | undefined {
|
|
246
|
+
switch (ast.type) {
|
|
247
|
+
case "Identifier":
|
|
248
|
+
return [ast.name];
|
|
249
|
+
case "MemberExpression": {
|
|
250
|
+
const object = destructureAst(ast.object);
|
|
251
|
+
const property = destructureAst(ast.property as t.Expression);
|
|
252
|
+
if (object && property) {
|
|
253
|
+
return [...object, ...property];
|
|
254
|
+
} else {
|
|
255
|
+
return undefined;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
default:
|
|
259
|
+
return undefined;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) HashiCorp, Inc.
|
|
3
|
+
* SPDX-License-Identifier: MPL-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { DynamicBlock, TerraformResourceBlock } from "./types";
|
|
7
|
+
|
|
8
|
+
export function isNestedDynamicBlock(
|
|
9
|
+
dynBlocks: DynamicBlock[],
|
|
10
|
+
block: DynamicBlock,
|
|
11
|
+
): boolean {
|
|
12
|
+
return dynBlocks.some(
|
|
13
|
+
(dyn) => dyn.path !== block.path && block.path.startsWith(dyn.path),
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const extractDynamicBlocks = (
|
|
18
|
+
config: TerraformResourceBlock,
|
|
19
|
+
path = "",
|
|
20
|
+
): DynamicBlock[] => {
|
|
21
|
+
if (typeof config !== "object") {
|
|
22
|
+
return [];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (!config) {
|
|
26
|
+
return [];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (Array.isArray(config)) {
|
|
30
|
+
return config.reduce(
|
|
31
|
+
(carry, item, index) => [
|
|
32
|
+
...carry,
|
|
33
|
+
...extractDynamicBlocks(item, `${path}.${index}`),
|
|
34
|
+
],
|
|
35
|
+
[],
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if ("dynamic" in config) {
|
|
40
|
+
const dynamic = (config as any).dynamic;
|
|
41
|
+
const scopedVar = Object.keys(dynamic)[0];
|
|
42
|
+
const { for_each, content } = dynamic[scopedVar][0];
|
|
43
|
+
|
|
44
|
+
return [
|
|
45
|
+
{
|
|
46
|
+
path: `${path}.dynamic.${scopedVar}`,
|
|
47
|
+
for_each,
|
|
48
|
+
content,
|
|
49
|
+
scopedVar,
|
|
50
|
+
},
|
|
51
|
+
...extractDynamicBlocks(
|
|
52
|
+
content,
|
|
53
|
+
`${path}.dynamic.${scopedVar}.0.content`,
|
|
54
|
+
),
|
|
55
|
+
];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return Object.entries(config).reduce((carry, [key, value]) => {
|
|
59
|
+
return [...carry, ...extractDynamicBlocks(value as any, `${path}.${key}`)];
|
|
60
|
+
}, [] as DynamicBlock[]);
|
|
61
|
+
};
|