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.
- package/lib/assembler.d.ts +1 -0
- package/lib/assembler.js +9 -1
- package/lib/assembler.js.map +1 -1
- package/lib/jsii-diagnostic.d.ts +1 -0
- package/lib/jsii-diagnostic.js +5 -0
- package/lib/jsii-diagnostic.js.map +1 -1
- package/lib/transforms/deprecation-warnings.d.ts +25 -1
- package/lib/transforms/deprecation-warnings.js +223 -109
- package/lib/transforms/deprecation-warnings.js.map +1 -1
- package/lib/type-tracker.d.ts +13 -0
- package/lib/type-tracker.js +36 -0
- package/lib/type-tracker.js.map +1 -0
- package/lib/version.d.ts +2 -2
- package/lib/version.js +2 -2
- package/lib/version.js.map +1 -1
- package/package.json +3 -3
|
@@ -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
|
|
30
|
+
const validationFunctions = [];
|
|
28
31
|
const types = assembly.types ?? {};
|
|
29
32
|
for (const type of Object.values(types)) {
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
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
|
|
67
|
-
|
|
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,
|
|
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
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
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
|
-
|
|
104
|
-
|
|
105
|
-
|
|
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
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
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
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
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.
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
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
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
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,
|
|
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
|
-
|
|
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 =
|
|
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.
|