jsii 5.9.36-dev.0 → 5.9.36

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.
@@ -11,61 +11,38 @@ const WARNING_FUNCTION_NAME = 'print';
11
11
  const PARAMETER_NAME = 'p';
12
12
  const FOR_LOOP_ITEM_NAME = 'o';
13
13
  const NAMESPACE = 'jsiiDeprecationWarnings';
14
- const LOCAL_ENUM_NAMESPACE = 'ns';
15
14
  const VISITED_OBJECTS_SET_NAME = 'visitedObjects';
16
15
  const DEPRECATION_ERROR = 'DeprecationError';
17
16
  const GET_PROPERTY_DESCRIPTOR = 'getPropertyDescriptor';
17
+ const VALIDATORS_OBJ = 'VALIDATORS';
18
18
  class DeprecationWarningsInjector {
19
- constructor(typeChecker) {
19
+ constructor(typeChecker, typeTracker) {
20
20
  this.typeChecker = typeChecker;
21
+ this.typeTracker = typeTracker;
21
22
  this.transformers = {
22
23
  before: [],
23
24
  };
25
+ this.shouldRenderValidatorCache = {};
26
+ this.validatorCacheSeenSet = new Set();
24
27
  }
25
28
  process(assembly, projectInfo) {
26
29
  const projectRoot = projectInfo.projectRoot;
27
- const functionDeclarations = [];
30
+ const validationFunctions = [];
28
31
  const types = assembly.types ?? {};
29
32
  for (const type of Object.values(types)) {
30
- const statements = [];
31
- let isEmpty = true;
32
- // This will add the parameter to the set of visited objects, to prevent infinite recursion
33
- statements.push(ts.factory.createExpressionStatement(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier(VISITED_OBJECTS_SET_NAME), 'add'), undefined, [ts.factory.createIdentifier(PARAMETER_NAME)])));
34
- const tryStatements = [];
35
- if (spec.isDeprecated(type) && spec.isEnumType(type)) {
36
- // The type is deprecated
37
- tryStatements.push(createWarningFunctionCall(type.fqn, type.docs?.deprecated));
38
- isEmpty = false;
39
- }
40
- if (spec.isEnumType(type) && type.locationInModule?.filename) {
41
- tryStatements.push(createEnumRequireStatement(type.locationInModule?.filename));
42
- tryStatements.push(createDuplicateEnumValuesCheck(type));
43
- for (const member of Object.values(type.members ?? [])) {
44
- if (spec.isDeprecated(member)) {
45
- // The enum member is deprecated
46
- const condition = ts.factory.createBinaryExpression(ts.factory.createIdentifier(PARAMETER_NAME), ts.SyntaxKind.EqualsEqualsEqualsToken, ts.factory.createPropertyAccessExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier(LOCAL_ENUM_NAMESPACE), type.name), member.name));
47
- tryStatements.push(createWarningFunctionCall(`${type.fqn}#${member.name}`, member.docs?.deprecated, condition));
48
- isEmpty = false;
49
- }
50
- }
51
- }
52
- else if (spec.isInterfaceType(type) && type.datatype) {
53
- const { statementsByProp, excludedProps } = processInterfaceType(type, types, assembly, projectInfo, undefined, undefined);
54
- for (const [name, statement] of statementsByProp.entries()) {
55
- if (!excludedProps.has(name)) {
56
- tryStatements.push(statement);
57
- isEmpty = false;
58
- }
59
- }
33
+ const fnStatements = this.generateTypeValidation(type, assembly, projectInfo, types);
34
+ if (fnStatements.length === 0) {
35
+ continue;
60
36
  }
61
- statements.push(ts.factory.createTryStatement(ts.factory.createBlock(tryStatements), undefined, ts.factory.createBlock([
62
- ts.factory.createExpressionStatement(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier(VISITED_OBJECTS_SET_NAME), 'delete'), undefined, [ts.factory.createIdentifier(PARAMETER_NAME)])),
63
- ])));
64
37
  const paramValue = ts.factory.createParameterDeclaration(undefined, undefined, PARAMETER_NAME);
65
38
  const functionName = fnName(type.fqn);
66
- const functionDeclaration = ts.factory.createFunctionDeclaration(undefined, undefined, ts.factory.createIdentifier(functionName), [], [paramValue], undefined, createFunctionBlock(isEmpty ? [] : statements));
67
- functionDeclarations.push(functionDeclaration);
39
+ const functionExpr = ts.factory.createFunctionExpression(undefined, undefined, ts.factory.createIdentifier(functionName), [], [paramValue], undefined, createFunctionBlock(fnStatements));
40
+ validationFunctions.push(ts.factory.createPropertyAssignment(functionName, functionExpr));
68
41
  }
42
+ const fileStatements = [];
43
+ fileStatements.push(ts.factory.createVariableStatement(undefined, ts.factory.createVariableDeclarationList([
44
+ ts.factory.createVariableDeclaration(VALIDATORS_OBJ, undefined, undefined, ts.factory.createObjectLiteralExpression(validationFunctions)),
45
+ ], ts.NodeFlags.Const)));
69
46
  this.transformers = {
70
47
  before: [
71
48
  (context) => {
@@ -74,7 +51,7 @@ class DeprecationWarningsInjector {
74
51
  },
75
52
  ],
76
53
  };
77
- generateWarningsFile(projectRoot, functionDeclarations);
54
+ generateWarningsFile(projectRoot, fileStatements);
78
55
  }
79
56
  get customTransformers() {
80
57
  return this.transformers;
@@ -89,58 +66,199 @@ class DeprecationWarningsInjector {
89
66
  }
90
67
  return result;
91
68
  }
92
- }
93
- exports.DeprecationWarningsInjector = DeprecationWarningsInjector;
94
- function processInterfaceType(type, types, assembly, projectInfo, statementsByProp = new Map(), excludedProps = new Set()) {
95
- for (const prop of Object.values(type.properties ?? {})) {
96
- const fqn = `${type.fqn}#${prop.name}`;
97
- if (spec.isDeprecated(prop) || spec.isDeprecated(type)) {
98
- // If the property individually is deprecated, or the entire type is deprecated
99
- const deprecatedDocs = prop.docs?.deprecated ?? type.docs?.deprecated;
100
- const statement = createWarningFunctionCall(fqn, deprecatedDocs, ts.factory.createBinaryExpression(ts.factory.createStringLiteral(prop.name), ts.SyntaxKind.InKeyword, ts.factory.createIdentifier(PARAMETER_NAME)), undefined);
101
- statementsByProp.set(prop.name, statement);
69
+ /**
70
+ * Whether the validator for the given type should be rendered
71
+ *
72
+ * A validator should be rendered if:
73
+ *
74
+ * - It contains any deprecated members (base case).
75
+ * - It references any other types whose validators should be rendered.
76
+ * - It inherits from other types whose validators should be rendered.
77
+ * - It references types that reference this type (recursive types).
78
+ * - It references types from another assembly.
79
+ *
80
+ * For the last one we technically return `true`, indicating that a validator
81
+ * *should* be rendered, but when we get to rendering no statements are
82
+ * actually produced and the validator function is never rendered. This was
83
+ * pre-existing behavior that I didn't change because introducing calls into
84
+ * other assemblies out of the blue introduces risk.
85
+ */
86
+ shouldRenderValidator(type, assembly) {
87
+ if (this.shouldRenderValidatorCache[type.fqn] !== undefined) {
88
+ return this.shouldRenderValidatorCache[type.fqn];
89
+ }
90
+ if (type.fqn === '@scope/jsii-calc-base.TypeToContainVeryBaseProps') {
91
+ debugger;
92
+ }
93
+ if (this.validatorCacheSeenSet.has(type.fqn)) {
94
+ // To be safe we need to say this is true.
95
+ return true;
102
96
  }
103
- else {
104
- /* If a prop is not deprecated, we don't want to generate a warning for it,
105
- even if another property with the same name is deprecated in another
106
- super-interface. */
107
- excludedProps.add(prop.name);
97
+ if (!type.fqn.startsWith(`${assembly.name}.`)) {
98
+ // Foreign type, always check
99
+ return true;
108
100
  }
109
- if (spec.isNamedTypeReference(prop.type) && Object.keys(types).includes(prop.type.fqn)) {
110
- const functionName = importedFunctionName(prop.type.fqn, assembly, projectInfo);
111
- if (functionName) {
112
- const statement = createTypeHandlerCall(functionName, `${PARAMETER_NAME}.${prop.name}`);
113
- statementsByProp.set(`${prop.name}_`, statement);
101
+ this.validatorCacheSeenSet.add(type.fqn);
102
+ this.shouldRenderValidatorCache[type.fqn] = calculate.call(this);
103
+ this.validatorCacheSeenSet.delete(type.fqn);
104
+ return this.shouldRenderValidatorCache[type.fqn];
105
+ function calculate() {
106
+ if (spec.isDeprecated(type)) {
107
+ return true;
114
108
  }
109
+ if (spec.isEnumType(type)) {
110
+ return (type.members ?? []).some(spec.isDeprecated);
111
+ }
112
+ if (spec.isInterfaceType(type) && type.datatype) {
113
+ for (const prop of type.properties ?? []) {
114
+ if (spec.isDeprecated(prop)) {
115
+ return true;
116
+ }
117
+ const typesToInspect = spec.isCollectionTypeReference(prop.type)
118
+ ? [prop.type.collection.elementtype]
119
+ : spec.isUnionTypeReference(prop.type)
120
+ ? prop.type.union.types
121
+ : [prop.type];
122
+ for (const typeToInspect of typesToInspect) {
123
+ if (!spec.isNamedTypeReference(typeToInspect)) {
124
+ continue;
125
+ }
126
+ // Is from a different assembly?
127
+ const typeObj = findType2(typeToInspect.fqn, assembly);
128
+ if (typeObj === 'other-assembly') {
129
+ return true;
130
+ }
131
+ if (this.shouldRenderValidator(typeObj, assembly)) {
132
+ return true;
133
+ }
134
+ }
135
+ }
136
+ for (const interfaceName of type.interfaces ?? []) {
137
+ const typeObj = findType2(interfaceName, assembly);
138
+ if (typeObj === 'other-assembly') {
139
+ return true;
140
+ }
141
+ if (this.shouldRenderValidator(typeObj, assembly)) {
142
+ return true;
143
+ }
144
+ }
145
+ }
146
+ return false;
147
+ }
148
+ }
149
+ generateTypeValidation(type, assembly, projectInfo, types) {
150
+ if (!this.shouldRenderValidator(type, assembly)) {
151
+ return [];
115
152
  }
116
- else if (spec.isCollectionTypeReference(prop.type) &&
117
- spec.isNamedTypeReference(prop.type.collection.elementtype)) {
118
- const functionName = importedFunctionName(prop.type.collection.elementtype.fqn, assembly, projectInfo);
119
- if (functionName) {
120
- const statement = createTypeHandlerCall(functionName, `${PARAMETER_NAME}.${prop.name}`, prop.type.collection.kind);
121
- statementsByProp.set(`${prop.name}_`, statement);
153
+ const statements = [];
154
+ let isEmpty = true;
155
+ // This will add the parameter to the set of visited objects, to prevent infinite recursion
156
+ statements.push(ts.factory.createExpressionStatement(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier(VISITED_OBJECTS_SET_NAME), 'add'), undefined, [ts.factory.createIdentifier(PARAMETER_NAME)])));
157
+ const tryStatements = [];
158
+ if (spec.isDeprecated(type) && spec.isEnumType(type)) {
159
+ // The type is deprecated
160
+ tryStatements.push(createWarningFunctionCall(type.fqn, type.docs?.deprecated));
161
+ isEmpty = false;
162
+ }
163
+ if (spec.isEnumType(type) && type.locationInModule?.filename) {
164
+ // Check for deprecated enum members
165
+ //
166
+ // We need to compare to the value of the deprecated enum. We can do that in one of 2 ways:
167
+ //
168
+ // - Compare against `require('./actual-file').EnumType.SOME_ENUM_MEMBER`
169
+ // - Look up the enum member value and compare against `'some-enum-member'`.
170
+ //
171
+ // The first one introduces a circular dependency between this file and `actual-file.js`, so we
172
+ // will go with the second.
173
+ //
174
+ // One complication: two enum members can have the same value (shouldn't, but can!) where
175
+ // one symbolic name is deprecated but the other isn't. In that case we don't treat it as deprecated.
176
+ const memDecls = this.typeTracker.getEnumMembers(type.fqn);
177
+ const nonDeprecatedValues = new Set((type.members ?? [])
178
+ .filter((m) => !spec.isDeprecated(m))
179
+ .map((m) => this.typeChecker.getConstantValue(memDecls[m.name])));
180
+ const deprecatedMembers = (type.members ?? []).filter(spec.isDeprecated);
181
+ for (const member of deprecatedMembers) {
182
+ const constantValue = this.typeChecker.getConstantValue(memDecls[member.name]);
183
+ if (nonDeprecatedValues.has(constantValue)) {
184
+ // Collission with non-deprecated enum member
185
+ continue;
186
+ }
187
+ const condition = ts.factory.createBinaryExpression(ts.factory.createIdentifier(PARAMETER_NAME), ts.SyntaxKind.EqualsEqualsEqualsToken, typeof constantValue === 'string'
188
+ ? ts.factory.createStringLiteral(constantValue)
189
+ : ts.factory.createNumericLiteral(constantValue));
190
+ tryStatements.push(createWarningFunctionCall(`${type.fqn}#${member.name}`, member.docs?.deprecated, condition));
191
+ isEmpty = false;
122
192
  }
123
193
  }
124
- else if (spec.isUnionTypeReference(prop.type) &&
125
- spec.isNamedTypeReference(prop.type.union.types[0]) &&
126
- Object.keys(types).includes(prop.type.union.types[0].fqn)) {
127
- const functionName = importedFunctionName(prop.type.union.types[0].fqn, assembly, projectInfo);
128
- if (functionName) {
129
- const statement = createTypeHandlerCall(functionName, `${PARAMETER_NAME}.${prop.name}`);
130
- statementsByProp.set(`${prop.name}_`, statement);
194
+ else if (spec.isInterfaceType(type) && type.datatype) {
195
+ const { statementsByProp, excludedProps } = this.processInterfaceType(type, types, assembly, projectInfo, undefined, undefined);
196
+ for (const [name, statement] of statementsByProp.entries()) {
197
+ if (!excludedProps.has(name)) {
198
+ tryStatements.push(statement);
199
+ isEmpty = false;
200
+ }
131
201
  }
132
202
  }
203
+ statements.push(ts.factory.createTryStatement(ts.factory.createBlock(tryStatements), undefined, ts.factory.createBlock([
204
+ ts.factory.createExpressionStatement(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier(VISITED_OBJECTS_SET_NAME), 'delete'), undefined, [ts.factory.createIdentifier(PARAMETER_NAME)])),
205
+ ])));
206
+ return isEmpty ? [] : statements;
133
207
  }
134
- // We also generate calls to all the supertypes
135
- for (const interfaceName of type.interfaces ?? []) {
136
- const assemblies = projectInfo.dependencyClosure.concat(assembly);
137
- const superType = findType(interfaceName, assemblies);
138
- if (superType.type) {
139
- processInterfaceType(superType.type, types, assembly, projectInfo, statementsByProp, excludedProps);
208
+ processInterfaceType(type, types, assembly, projectInfo, statementsByProp = new Map(), excludedProps = new Set()) {
209
+ for (const prop of Object.values(type.properties ?? {})) {
210
+ const fqn = `${type.fqn}#${prop.name}`;
211
+ if (spec.isDeprecated(prop) || spec.isDeprecated(type)) {
212
+ // If the property individually is deprecated, or the entire type is deprecated
213
+ const deprecatedDocs = prop.docs?.deprecated ?? type.docs?.deprecated;
214
+ const statement = createWarningFunctionCall(fqn, deprecatedDocs, ts.factory.createBinaryExpression(ts.factory.createStringLiteral(prop.name), ts.SyntaxKind.InKeyword, ts.factory.createIdentifier(PARAMETER_NAME)), undefined);
215
+ statementsByProp.set(prop.name, statement);
216
+ }
217
+ else {
218
+ /* If a prop is not deprecated, we don't want to generate a warning for it,
219
+ even if another property with the same name is deprecated in another
220
+ super-interface. */
221
+ excludedProps.add(prop.name);
222
+ }
223
+ if (spec.isNamedTypeReference(prop.type) && Object.keys(types).includes(prop.type.fqn)) {
224
+ if (this.shouldRenderValidator(types[prop.type.fqn], assembly)) {
225
+ const functionName = importedFunctionName(prop.type.fqn, assembly, projectInfo);
226
+ if (functionName) {
227
+ const statement = createTypeHandlerCall(functionName, `${PARAMETER_NAME}.${prop.name}`);
228
+ statementsByProp.set(`${prop.name}_`, statement);
229
+ }
230
+ }
231
+ }
232
+ else if (spec.isCollectionTypeReference(prop.type) &&
233
+ spec.isNamedTypeReference(prop.type.collection.elementtype)) {
234
+ const functionName = importedFunctionName(prop.type.collection.elementtype.fqn, assembly, projectInfo);
235
+ if (functionName) {
236
+ const statement = createTypeHandlerCall(functionName, `${PARAMETER_NAME}.${prop.name}`, prop.type.collection.kind);
237
+ statementsByProp.set(`${prop.name}_`, statement);
238
+ }
239
+ }
240
+ else if (spec.isUnionTypeReference(prop.type) &&
241
+ spec.isNamedTypeReference(prop.type.union.types[0]) &&
242
+ Object.keys(types).includes(prop.type.union.types[0].fqn)) {
243
+ const functionName = importedFunctionName(prop.type.union.types[0].fqn, assembly, projectInfo);
244
+ if (functionName) {
245
+ const statement = createTypeHandlerCall(functionName, `${PARAMETER_NAME}.${prop.name}`);
246
+ statementsByProp.set(`${prop.name}_`, statement);
247
+ }
248
+ }
140
249
  }
250
+ // We also generate calls to all the supertypes
251
+ for (const interfaceName of type.interfaces ?? []) {
252
+ const assemblies = projectInfo.dependencyClosure.concat(assembly);
253
+ const superType = findType(interfaceName, assemblies);
254
+ if (superType.type) {
255
+ this.processInterfaceType(superType.type, types, assembly, projectInfo, statementsByProp, excludedProps);
256
+ }
257
+ }
258
+ return { statementsByProp, excludedProps };
141
259
  }
142
- return { statementsByProp, excludedProps };
143
260
  }
261
+ exports.DeprecationWarningsInjector = DeprecationWarningsInjector;
144
262
  function fnName(fqn) {
145
263
  return fqn.replace(/[^\w\d]/g, '_');
146
264
  }
@@ -159,9 +277,7 @@ function createWarningFunctionCall(fqn, message = '', condition, includeNamespac
159
277
  ]));
160
278
  return condition ? ts.factory.createIfStatement(condition, mainStatement) : mainStatement;
161
279
  }
162
- function generateWarningsFile(projectRoot, functionDeclarations) {
163
- const names = [...functionDeclarations].map((d) => d.name?.text).filter(Boolean);
164
- const exportedSymbols = [WARNING_FUNCTION_NAME, GET_PROPERTY_DESCRIPTOR, DEPRECATION_ERROR, ...names].join(',');
280
+ function generateWarningsFile(projectRoot, validatorStatements) {
165
281
  const functionText = `function ${WARNING_FUNCTION_NAME}(name, deprecationMessage) {
166
282
  const deprecated = process.env.JSII_DEPRECATED;
167
283
  const deprecationMode = ['warn', 'fail', 'quiet'].includes(deprecated) ? deprecated : 'warn';
@@ -202,11 +318,22 @@ class ${DEPRECATION_ERROR} extends Error {
202
318
  }
203
319
  }
204
320
 
205
- module.exports = {${exportedSymbols}}
321
+ function nop() {
322
+ }
323
+
324
+ module.exports = new Proxy({}, {
325
+ get(target, prop, receiver) {
326
+ if (prop === '${WARNING_FUNCTION_NAME}') return ${WARNING_FUNCTION_NAME};
327
+ if (prop === '${GET_PROPERTY_DESCRIPTOR}') return ${GET_PROPERTY_DESCRIPTOR};
328
+ if (prop === '${DEPRECATION_ERROR}') return ${DEPRECATION_ERROR};
329
+
330
+ return ${VALIDATORS_OBJ}[prop] ?? nop;
331
+ },
332
+ });
206
333
  `;
207
334
  const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
208
335
  const resultFile = ts.createSourceFile(path.join(projectRoot, exports.WARNINGSCODE_FILE_NAME), functionText, ts.ScriptTarget.Latest, false, ts.ScriptKind.JS);
209
- const declarations = functionDeclarations.map((declaration) => printer.printNode(ts.EmitHint.Unspecified, declaration, resultFile));
336
+ const declarations = validatorStatements.map((st) => printer.printNode(ts.EmitHint.Unspecified, st, resultFile));
210
337
  const content = declarations.concat(printer.printFile(resultFile)).join('\n');
211
338
  fs.writeFileSync(path.join(projectRoot, exports.WARNINGSCODE_FILE_NAME), content);
212
339
  }
@@ -313,6 +440,17 @@ class Transformer {
313
440
  return statements;
314
441
  }
315
442
  }
443
+ function findType2(fqn, assembly) {
444
+ // Is from a different assembly?
445
+ if (!fqn.startsWith(`${assembly.name}.`)) {
446
+ return 'other-assembly';
447
+ }
448
+ const type = (assembly.types ?? {})[fqn];
449
+ if (!type) {
450
+ throw new Error(`Could not find type in same assembly: ${fqn}`);
451
+ }
452
+ return type;
453
+ }
316
454
  function createWarningStatementForElement(element, classType) {
317
455
  if (spec.isDeprecated(element)) {
318
456
  const elementName = element.name;
@@ -344,11 +482,6 @@ function insertStatements(block, newStatements) {
344
482
  result.splice(splicePoint(block.statements[0]), 0, ...newStatements);
345
483
  return ts.factory.createNodeArray(result);
346
484
  }
347
- function createEnumRequireStatement(typeLocation) {
348
- const { ext } = path.parse(typeLocation);
349
- const jsFileName = typeLocation.replace(ext, '.js');
350
- return createRequireStatement(LOCAL_ENUM_NAMESPACE, `./${jsFileName}`);
351
- }
352
485
  function createRequireStatement(name, importPath) {
353
486
  return ts.factory.createVariableStatement(undefined, ts.factory.createVariableDeclarationList([
354
487
  ts.factory.createVariableDeclaration(name, undefined, undefined, ts.factory.createCallExpression(ts.factory.createIdentifier('require'), undefined, [
@@ -393,28 +526,9 @@ function createTypeHandlerCall(functionName, parameter, collectionKind) {
393
526
  case spec.CollectionKind.Map:
394
527
  return ts.factory.createIfStatement(ts.factory.createBinaryExpression(ts.factory.createIdentifier(parameter), ts.SyntaxKind.ExclamationEqualsToken, ts.factory.createNull()), ts.factory.createForOfStatement(undefined, ts.factory.createVariableDeclarationList([ts.factory.createVariableDeclaration(FOR_LOOP_ITEM_NAME)], ts.NodeFlags.Const), ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('Object'), 'values'), undefined, [ts.factory.createIdentifier(parameter)]), createTypeHandlerCall(functionName, FOR_LOOP_ITEM_NAME)));
395
528
  case undefined:
396
- return ts.factory.createIfStatement(ts.factory.createPrefixUnaryExpression(ts.SyntaxKind.ExclamationToken, ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier(VISITED_OBJECTS_SET_NAME), ts.factory.createIdentifier('has')), undefined, [ts.factory.createIdentifier(parameter)])), ts.factory.createExpressionStatement(ts.factory.createCallExpression(ts.factory.createIdentifier(functionName), undefined, [
397
- ts.factory.createIdentifier(parameter),
398
- ])));
529
+ return ts.factory.createIfStatement(ts.factory.createPrefixUnaryExpression(ts.SyntaxKind.ExclamationToken, ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier(VISITED_OBJECTS_SET_NAME), ts.factory.createIdentifier('has')), undefined, [ts.factory.createIdentifier(parameter)])), ts.factory.createExpressionStatement(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('module.exports'), functionName), undefined, [ts.factory.createIdentifier(parameter)])));
399
530
  }
400
531
  }
401
- /**
402
- * There is a chance an enum contains duplicates values with distinct keys,
403
- * with one of those keys being deprecated. This is a potential pattern to "rename" an enum.
404
- * In this case, we can't concretely determine if the deprecated member was used or not,
405
- * so in those cases we skip the warnings altogether, rather than erroneously warning for valid usage.
406
- * This create a statement to check if the enum value is a duplicate:
407
- *
408
- * if (Object.values(Foo).filter(x => x === p).length > 1) { return; }
409
- *
410
- * Note that we can't just check the assembly for these duplicates, due to:
411
- * https://github.com/aws/jsii/issues/2782
412
- */
413
- function createDuplicateEnumValuesCheck(type) {
414
- return ts.factory.createIfStatement(ts.factory.createBinaryExpression(ts.factory.createPropertyAccessExpression(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('Object'), 'values'), undefined, [ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier(LOCAL_ENUM_NAMESPACE), type.name)]), ts.factory.createIdentifier('filter')), undefined, [
415
- ts.factory.createArrowFunction(undefined, undefined, [ts.factory.createParameterDeclaration(undefined, undefined, 'x')], undefined, ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.factory.createBinaryExpression(ts.factory.createIdentifier('x'), ts.factory.createToken(ts.SyntaxKind.EqualsEqualsEqualsToken), ts.factory.createIdentifier(PARAMETER_NAME))),
416
- ]), ts.factory.createIdentifier('length')), ts.factory.createToken(ts.SyntaxKind.GreaterThanToken), ts.factory.createNumericLiteral('1')), ts.factory.createReturnStatement());
417
- }
418
532
  // We try-then-rethrow exceptions to avoid runtimes displaying an uncanny wall of text if the place
419
533
  // where the error was thrown is webpacked. For example, jest somehow manages to capture the throw
420
534
  // location and renders the source line (which may be the whole file) when bundled.