@knapsack/spec-utils 4.75.8--canary.5662.4aaa414.0 → 4.75.8

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.
@@ -1,4 +1,4 @@
1
1
 
2
- > @knapsack/spec-utils@4.75.6 build /home/runner/work/app-monorepo/app-monorepo/apps/client/libs/spec-utils
2
+ > @knapsack/spec-utils@4.75.7 build /home/runner/work/app-monorepo/app-monorepo/apps/client/libs/spec-utils
3
3
  > tsc
4
4
 
@@ -1,4 +1,4 @@
1
1
 
2
- > @knapsack/spec-utils@4.75.6 lint /home/runner/work/app-monorepo/app-monorepo/apps/client/libs/spec-utils
2
+ > @knapsack/spec-utils@4.75.7 lint /home/runner/work/app-monorepo/app-monorepo/apps/client/libs/spec-utils
3
3
  > eslint ./
4
4
 
@@ -1,5 +1,5 @@
1
1
 
2
- > @knapsack/spec-utils@4.75.6 test /home/runner/work/app-monorepo/app-monorepo/apps/client/libs/spec-utils
2
+ > @knapsack/spec-utils@4.75.7 test /home/runner/work/app-monorepo/app-monorepo/apps/client/libs/spec-utils
3
3
  > ava
4
4
 
5
5
 
@@ -23,8 +23,8 @@
23
23
  ✔ analyze-exports › function with basic parameters: .d.ts
24
24
  ✔ analyze-exports › async function with basic parameters: .d.ts
25
25
  ✔ analyze-exports › function with complex parameters: .d.ts
26
- ✔ analyze-exports.sandbox-components › analyzeExports react (3.6s)
27
- ✔ analyze-exports.sandbox-components › analyzeExports angular (2.3s)
26
+ ✔ analyze-exports.sandbox-components › analyzeExports react (3.4s)
27
+ ✔ analyze-exports.sandbox-components › analyzeExports angular (2.1s)
28
28
  ✔ convert-to-spec › string
29
29
  ✔ convert-to-spec › boolean
30
30
  ✔ convert-to-spec › object
package/CHANGELOG.md CHANGED
@@ -1,3 +1,38 @@
1
+ # v4.75.8 (Tue Feb 25 2025)
2
+
3
+ #### 🐛 Bug Fix
4
+
5
+ - updates handling of visited types, adds depth limit [#5694](https://github.com/knapsack-labs/app-monorepo/pull/5694) ([@mabry1985](https://github.com/mabry1985))
6
+
7
+ #### Authors: 1
8
+
9
+ - Josh Mabry ([@mabry1985](https://github.com/mabry1985))
10
+
11
+ ---
12
+
13
+ # v4.75.7 (Mon Feb 24 2025)
14
+
15
+ #### 🐛 Bug Fix
16
+
17
+ - merge from latest ([@illepic](https://github.com/illepic))
18
+ - Merge branch 'latest' into feature/strictNullChecks-gitlab-rest-api ([@illepic](https://github.com/illepic))
19
+ - Merge branch 'latest' into feature/strictNullChecks-github-api-2 ([@illepic](https://github.com/illepic))
20
+ - Merge branch 'latest' into feature/strictNullChecks-bitbucket-cloud-api ([@illepic](https://github.com/illepic))
21
+ - Merge branch 'latest' into 12-12-start_of_strictNullChecks_in_lambdas ([@illepic](https://github.com/illepic))
22
+
23
+ #### 🏠 Internal
24
+
25
+ - strictNullChecks: github-api [#5376](https://github.com/knapsack-labs/app-monorepo/pull/5376) ([@illepic](https://github.com/illepic))
26
+ - strictNullChecks: gitlab-rest-api [#5386](https://github.com/knapsack-labs/app-monorepo/pull/5386) ([@illepic](https://github.com/illepic) [@freneticpixel](https://github.com/freneticpixel))
27
+ - strictNullChecks: bitbucket-cloud-api [#5333](https://github.com/knapsack-labs/app-monorepo/pull/5333) ([@illepic](https://github.com/illepic))
28
+
29
+ #### Authors: 2
30
+
31
+ - Christopher Bloom ([@illepic](https://github.com/illepic))
32
+ - Jim Frenette ([@freneticpixel](https://github.com/freneticpixel))
33
+
34
+ ---
35
+
1
36
  # v4.75.6 (Fri Feb 14 2025)
2
37
 
3
38
  #### 🏠 Internal
@@ -1,9 +1,19 @@
1
1
  import ts from 'typescript';
2
2
  import { TypeInfo } from './types.js';
3
- export declare function analyzeType({ type, checker, isOptional, visitedTypes, }: {
3
+ /**
4
+ * Analyzes a TypeScript type and returns a structured TypeInfo representation
5
+ */
6
+ export declare function analyzeType({ type, checker, isOptional, visitedTypes, depth,
7
+ /**
8
+ * The maximum depth of type analysis.
9
+ * This is used to avoid infinite recursion.
10
+ */
11
+ maxDepth, }: {
4
12
  type: ts.Type;
5
13
  checker: ts.TypeChecker;
6
14
  isOptional?: boolean;
7
- visitedTypes?: Set<ts.Type>;
15
+ visitedTypes?: Map<string, TypeInfo>;
16
+ depth?: number;
17
+ maxDepth?: number;
8
18
  }): TypeInfo;
9
19
  //# sourceMappingURL=analyze-type.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"analyze-type.d.ts","sourceRoot":"","sources":["../src/analyze-type.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAC;AAC5B,OAAO,EAAE,QAAQ,EAAkB,MAAM,YAAY,CAAC;AAGtD,wBAAgB,WAAW,CAAC,EAC1B,IAAI,EACJ,OAAO,EACP,UAAU,EACV,YAAwB,GACzB,EAAE;IACD,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC;IACd,OAAO,EAAE,EAAE,CAAC,WAAW,CAAC;IACxB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,YAAY,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC;CAC7B,GAAG,QAAQ,CA8NX"}
1
+ {"version":3,"file":"analyze-type.d.ts","sourceRoot":"","sources":["../src/analyze-type.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAC;AAC5B,OAAO,EAAE,QAAQ,EAAkB,MAAM,YAAY,CAAC;AA4CtD;;GAEG;AACH,wBAAgB,WAAW,CAAC,EAC1B,IAAI,EACJ,OAAO,EACP,UAAU,EACV,YAA0C,EAC1C,KAAS;AACT;;;GAGG;AACH,QAAY,GACb,EAAE;IACD,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC;IACd,OAAO,EAAE,EAAE,CAAC,WAAW,CAAC;IACxB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,YAAY,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IACrC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG,QAAQ,CAyPX"}
@@ -1,26 +1,52 @@
1
1
  import ts from 'typescript';
2
2
  import { getSetFlags } from './utils.js';
3
- export function analyzeType({ type, checker, isOptional, visitedTypes = new Set(), }) {
3
+ // Cache for type IDs to improve performance
4
+ const typeIdMap = new WeakMap();
5
+ /**
6
+ * Generates a consistent identifier for a TypeScript type
7
+ * Uses the type's string representation to ensure identical types get the same ID
8
+ */
9
+ function getTypeId(type, checker) {
10
+ // Check if we already have an ID for this type
11
+ const existingId = typeIdMap.get(type);
12
+ if (existingId) {
13
+ return existingId;
14
+ }
15
+ // Generate a new ID based on the type's string representation
16
+ // This ensures identical types get the same ID
17
+ const typeString = checker.typeToString(type, undefined,
18
+ // eslint-disable-next-line no-bitwise
19
+ ts.TypeFormatFlags.NoTruncation);
20
+ // Add some additional information to help distinguish types
21
+ // that might have the same string representation
22
+ const idComponents = [typeString];
23
+ // Add symbol name if available
24
+ const symbol = type.getSymbol();
25
+ if (symbol) {
26
+ idComponents.push(symbol.getName());
27
+ }
28
+ // Add type flags to help distinguish types
29
+ idComponents.push(String(type.flags));
30
+ const newId = idComponents.join('_');
31
+ typeIdMap.set(type, newId);
32
+ return newId;
33
+ }
34
+ /**
35
+ * Analyzes a TypeScript type and returns a structured TypeInfo representation
36
+ */
37
+ export function analyzeType({ type, checker, isOptional, visitedTypes = new Map(), depth = 0,
38
+ /**
39
+ * The maximum depth of type analysis.
40
+ * This is used to avoid infinite recursion.
41
+ */
42
+ maxDepth = 3, }) {
4
43
  var _a;
5
- // Prevent infinite recursion
6
- if (visitedTypes.has(type)) {
7
- return {
8
- type: 'misc',
9
- tsRawType: checker.typeToString(type, undefined,
10
- // eslint-disable-next-line no-bitwise
11
- ts.TypeFormatFlags.MultilineObjectLiterals |
12
- ts.TypeFormatFlags.NoTruncation),
13
- tsMetadata: {
14
- typeFlags: getSetFlags({
15
- flags: type.flags,
16
- enumObject: ts.TypeFlags,
17
- }),
18
- type,
19
- },
20
- isOptional,
21
- };
44
+ // Generate a unique key for the type
45
+ const typeId = getTypeId(type, checker);
46
+ // Check if we've already analyzed this type
47
+ if (visitedTypes.has(typeId)) {
48
+ return visitedTypes.get(typeId);
22
49
  }
23
- visitedTypes.add(type);
24
50
  const tsRawType = checker.typeToString(type, undefined,
25
51
  // eslint-disable-next-line no-bitwise
26
52
  ts.TypeFormatFlags.MultilineObjectLiterals |
@@ -37,42 +63,71 @@ export function analyzeType({ type, checker, isOptional, visitedTypes = new Set(
37
63
  },
38
64
  isOptional,
39
65
  };
66
+ // If we've reached max depth, return a simplified type
67
+ if (depth >= maxDepth) {
68
+ const result = {
69
+ type: 'misc',
70
+ ...typeInfoCommon,
71
+ };
72
+ visitedTypes.set(typeId, result);
73
+ return result;
74
+ }
75
+ let result;
40
76
  // Handle primitive types first
41
77
  if (tsRawType === 'number' ||
42
78
  tsRawType === 'boolean' ||
43
79
  tsRawType === 'string') {
44
- return {
80
+ result = {
45
81
  type: tsRawType,
46
82
  ...typeInfoCommon,
47
83
  };
84
+ visitedTypes.set(typeId, result);
85
+ return result;
48
86
  }
49
87
  // Handle literal types
50
88
  if (type.isStringLiteral()) {
51
- return {
89
+ result = {
52
90
  type: 'stringLiteral',
53
91
  value: type.value,
54
92
  ...typeInfoCommon,
55
93
  };
94
+ visitedTypes.set(typeId, result);
95
+ return result;
56
96
  }
57
97
  if (type.isNumberLiteral()) {
58
- return {
98
+ result = {
59
99
  type: 'numberLiteral',
60
100
  value: type.value,
61
101
  ...typeInfoCommon,
62
102
  };
103
+ visitedTypes.set(typeId, result);
104
+ return result;
63
105
  }
106
+ // Create a placeholder to avoid infinite recursion
107
+ const placeholder = {
108
+ type: 'misc',
109
+ ...typeInfoCommon,
110
+ };
111
+ visitedTypes.set(typeId, placeholder);
64
112
  // Handle unions
65
113
  if (type.isUnion()) {
66
- return {
114
+ result = {
67
115
  type: 'union',
68
- items: type.types.map((t) => analyzeType({ type: t, checker, isOptional, visitedTypes })),
116
+ items: type.types.map((t) => analyzeType({
117
+ type: t,
118
+ checker,
119
+ isOptional,
120
+ visitedTypes,
121
+ depth: depth + 1,
122
+ maxDepth,
123
+ })),
69
124
  ...typeInfoCommon,
70
125
  };
71
126
  }
72
127
  // Handle arrays
73
- if (checker.isArrayType(type) || checker.isArrayLikeType(type)) {
128
+ else if (checker.isArrayType(type) || checker.isArrayLikeType(type)) {
74
129
  const indexInfos = checker.getIndexInfosOfType(type);
75
- return {
130
+ result = {
76
131
  type: 'array',
77
132
  items: indexInfos.length === 1
78
133
  ? analyzeType({
@@ -80,6 +135,8 @@ export function analyzeType({ type, checker, isOptional, visitedTypes = new Set(
80
135
  checker,
81
136
  isOptional,
82
137
  visitedTypes,
138
+ depth: depth + 1,
139
+ maxDepth,
83
140
  })
84
141
  : { type: 'misc', ...typeInfoCommon },
85
142
  ...typeInfoCommon,
@@ -91,7 +148,7 @@ export function analyzeType({ type, checker, isOptional, visitedTypes = new Set(
91
148
  // from each type in the intersection, but we may want to limit the depth of the intersection
92
149
  // to avoid bringing in too much.
93
150
  // https://linear.app/knapsack/issue/KSP-5885/update-handling-of-intersection-types-in-spec-utils
94
- if (type.isIntersection()) {
151
+ else if (type.isIntersection()) {
95
152
  const properties = type.getProperties();
96
153
  if (properties.length > 0) {
97
154
  const analyzedProperties = properties.reduce((acc, prop) => {
@@ -105,6 +162,8 @@ export function analyzeType({ type, checker, isOptional, visitedTypes = new Set(
105
162
  // eslint-disable-next-line no-bitwise
106
163
  isOptional: !!(prop.flags & ts.SymbolFlags.Optional),
107
164
  visitedTypes,
165
+ depth: depth + 1,
166
+ maxDepth,
108
167
  });
109
168
  acc[prop.getName()] = {
110
169
  name: prop.getName(),
@@ -126,17 +185,24 @@ export function analyzeType({ type, checker, isOptional, visitedTypes = new Set(
126
185
  return acc;
127
186
  }, {});
128
187
  if (Object.keys(analyzedProperties).length > 0) {
129
- return {
188
+ result = {
130
189
  type: 'object',
131
190
  properties: analyzedProperties,
132
191
  ...typeInfoCommon,
133
192
  };
134
193
  }
194
+ else {
195
+ result = placeholder;
196
+ }
197
+ }
198
+ else {
199
+ result = placeholder;
135
200
  }
136
201
  }
137
202
  // Handle function types
138
- if (typeFlags.includes('Object') && type.getCallSignatures().length > 0) {
139
- return {
203
+ else if (typeFlags.includes('Object') &&
204
+ type.getCallSignatures().length > 0) {
205
+ result = {
140
206
  type: 'function',
141
207
  parameters: [],
142
208
  returnType: { type: 'misc', ...typeInfoCommon },
@@ -146,57 +212,59 @@ export function analyzeType({ type, checker, isOptional, visitedTypes = new Set(
146
212
  // Handle object types
147
213
  // @TODO: update handling of object type inference
148
214
  // https://linear.app/knapsack/issue/KSP-5880/handle-arrays-of-objects-in-react-renderer-infer-spec
149
- // if (typeFlags.includes('Object')) {
215
+ // else if (typeFlags.includes('Object')) {
150
216
  // const properties = type.getProperties();
151
217
  // if (properties.length > 0) {
152
- // const analyzedProperties = properties.reduce<Record<string, SymbolInfo>>(
153
- // (acc, prop) => {
154
- // try {
155
- // const propType = checker.getTypeOfSymbol(prop);
156
- // if (propType) {
157
- // const propAnalysis = analyzeType({
218
+ // const analyzedProperties = properties.reduce((acc, prop) => {
219
+ // try {
220
+ // const propType = checker.getTypeOfSymbol(prop);
221
+ // if (propType) {
222
+ // const propAnalysis = analyzeType({
223
+ // type: propType,
224
+ // checker,
225
+ // // eslint-disable-next-line no-bitwise
226
+ // isOptional: !!(prop.flags & ts.SymbolFlags.Optional),
227
+ // visitedTypes,
228
+ // depth: depth + 1,
229
+ // maxDepth,
230
+ // });
231
+ // acc[prop.getName()] = {
232
+ // name: prop.getName(),
233
+ // typeInfo: propAnalysis,
234
+ // tsMetadata: {
235
+ // symbol: prop,
158
236
  // type: propType,
159
- // checker,
160
- // // eslint-disable-next-line no-bitwise
161
- // isOptional: !!(prop.flags & ts.SymbolFlags.Optional),
162
- // visitedTypes,
163
- // depth: depth + 1,
164
- // });
165
- // acc[prop.getName()] = {
166
- // name: prop.getName(),
167
- // typeInfo: propAnalysis,
168
- // tsMetadata: {
169
- // symbol: prop,
170
- // type: propType,
171
- // symbolFlags: getSetFlags({
172
- // flags: prop.flags,
173
- // enumObject: ts.SymbolFlags,
174
- // }),
175
- // },
176
- // };
177
- // }
178
- // } catch (error) {
179
- // console.debug(
180
- // `Error analyzing property ${prop.getName()}: ${error}`,
181
- // );
237
+ // symbolFlags: getSetFlags({
238
+ // flags: prop.flags,
239
+ // enumObject: ts.SymbolFlags,
240
+ // }),
241
+ // },
242
+ // };
182
243
  // }
183
- // return acc;
184
- // },
185
- // {},
186
- // );
244
+ // } catch (error) {
245
+ // console.debug(`Error analyzing property ${prop.getName()}: ${error}`);
246
+ // }
247
+ // return acc;
248
+ // }, {} as Record<string, any>);
187
249
  // if (Object.keys(analyzedProperties).length > 0) {
188
- // return {
250
+ // result = {
189
251
  // type: 'object',
190
252
  // properties: analyzedProperties,
191
253
  // ...typeInfoCommon,
192
254
  // };
255
+ // } else {
256
+ // result = placeholder;
193
257
  // }
258
+ // } else {
259
+ // result = placeholder;
194
260
  // }
195
261
  // }
196
262
  // Default to misc type for anything we can't properly analyze
197
- return {
198
- type: 'misc',
199
- ...typeInfoCommon,
200
- };
263
+ else {
264
+ result = placeholder;
265
+ }
266
+ // Update the cache with the actual result
267
+ visitedTypes.set(typeId, result);
268
+ return result;
201
269
  }
202
270
  //# sourceMappingURL=analyze-type.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"analyze-type.js","sourceRoot":"","sources":["../src/analyze-type.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"analyze-type.js","sourceRoot":"","sources":["../src/analyze-type.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAC;AAE5B,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEzC,4CAA4C;AAC5C,MAAM,SAAS,GAAG,IAAI,OAAO,EAAmB,CAAC;AAEjD;;;GAGG;AACH,SAAS,SAAS,CAAC,IAAa,EAAE,OAAuB;IACvD,+CAA+C;IAC/C,MAAM,UAAU,GAAG,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACvC,IAAI,UAAU,EAAE,CAAC;QACf,OAAO,UAAU,CAAC;IACpB,CAAC;IAED,8DAA8D;IAC9D,+CAA+C;IAC/C,MAAM,UAAU,GAAG,OAAO,CAAC,YAAY,CACrC,IAAI,EACJ,SAAS;IACT,sCAAsC;IACtC,EAAE,CAAC,eAAe,CAAC,YAAY,CAChC,CAAC;IAEF,4DAA4D;IAC5D,iDAAiD;IACjD,MAAM,YAAY,GAAG,CAAC,UAAU,CAAC,CAAC;IAElC,+BAA+B;IAC/B,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;IAChC,IAAI,MAAM,EAAE,CAAC;QACX,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC;IACtC,CAAC;IAED,2CAA2C;IAC3C,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;IAEtC,MAAM,KAAK,GAAG,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACrC,SAAS,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAC3B,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,WAAW,CAAC,EAC1B,IAAI,EACJ,OAAO,EACP,UAAU,EACV,YAAY,GAAG,IAAI,GAAG,EAAoB,EAC1C,KAAK,GAAG,CAAC;AACT;;;GAGG;AACH,QAAQ,GAAG,CAAC,GAQb;;IACC,qCAAqC;IACrC,MAAM,MAAM,GAAG,SAAS,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAExC,4CAA4C;IAC5C,IAAI,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;QAC7B,OAAO,YAAY,CAAC,GAAG,CAAC,MAAM,CAAE,CAAC;IACnC,CAAC;IAED,MAAM,SAAS,GAAG,OAAO,CAAC,YAAY,CACpC,IAAI,EACJ,SAAS;IACT,sCAAsC;IACtC,EAAE,CAAC,eAAe,CAAC,uBAAuB;QACxC,EAAE,CAAC,eAAe,CAAC,YAAY,CAClC,CAAC;IACF,MAAM,SAAS,GAAG,WAAW,CAAC;QAC5B,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,UAAU,EAAE,EAAE,CAAC,SAAS;KACzB,CAAC,CAAC;IACH,MAAM,cAAc,GAAmB;QACrC,SAAS;QACT,UAAU,EAAE;YACV,SAAS;YACT,IAAI;SACL;QACD,UAAU;KACX,CAAC;IAEF,uDAAuD;IACvD,IAAI,KAAK,IAAI,QAAQ,EAAE,CAAC;QACtB,MAAM,MAAM,GAAa;YACvB,IAAI,EAAE,MAAM;YACZ,GAAG,cAAc;SAClB,CAAC;QACF,YAAY,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACjC,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,IAAI,MAAgB,CAAC;IAErB,+BAA+B;IAC/B,IACE,SAAS,KAAK,QAAQ;QACtB,SAAS,KAAK,SAAS;QACvB,SAAS,KAAK,QAAQ,EACtB,CAAC;QACD,MAAM,GAAG;YACP,IAAI,EAAE,SAAS;YACf,GAAG,cAAc;SAClB,CAAC;QACF,YAAY,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACjC,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,uBAAuB;IACvB,IAAI,IAAI,CAAC,eAAe,EAAE,EAAE,CAAC;QAC3B,MAAM,GAAG;YACP,IAAI,EAAE,eAAe;YACrB,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,GAAG,cAAc;SAClB,CAAC;QACF,YAAY,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACjC,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,IAAI,IAAI,CAAC,eAAe,EAAE,EAAE,CAAC;QAC3B,MAAM,GAAG;YACP,IAAI,EAAE,eAAe;YACrB,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,GAAG,cAAc;SAClB,CAAC;QACF,YAAY,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACjC,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,mDAAmD;IACnD,MAAM,WAAW,GAAa;QAC5B,IAAI,EAAE,MAAM;QACZ,GAAG,cAAc;KAClB,CAAC;IACF,YAAY,CAAC,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IAEtC,gBAAgB;IAChB,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,CAAC;QACnB,MAAM,GAAG;YACP,IAAI,EAAE,OAAO;YACb,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAC1B,WAAW,CAAC;gBACV,IAAI,EAAE,CAAC;gBACP,OAAO;gBACP,UAAU;gBACV,YAAY;gBACZ,KAAK,EAAE,KAAK,GAAG,CAAC;gBAChB,QAAQ;aACT,CAAC,CACH;YACD,GAAG,cAAc;SAClB,CAAC;IACJ,CAAC;IACD,gBAAgB;SACX,IAAI,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,OAAO,CAAC,eAAe,CAAC,IAAI,CAAC,EAAE,CAAC;QACpE,MAAM,UAAU,GAAG,OAAO,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC;QAErD,MAAM,GAAG;YACP,IAAI,EAAE,OAAO;YACb,KAAK,EACH,UAAU,CAAC,MAAM,KAAK,CAAC;gBACrB,CAAC,CAAC,WAAW,CAAC;oBACV,IAAI,EAAE,MAAA,UAAU,CAAC,CAAC,CAAC,0CAAE,IAAI;oBACzB,OAAO;oBACP,UAAU;oBACV,YAAY;oBACZ,KAAK,EAAE,KAAK,GAAG,CAAC;oBAChB,QAAQ;iBACT,CAAC;gBACJ,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,cAAc,EAAE;YACzC,GAAG,cAAc;SAClB,CAAC;IACJ,CAAC;IACD,uBAAuB;IACvB,6CAA6C;IAC7C,iGAAiG;IACjG,6FAA6F;IAC7F,iCAAiC;IACjC,iGAAiG;SAC5F,IAAI,IAAI,CAAC,cAAc,EAAE,EAAE,CAAC;QAC/B,MAAM,UAAU,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC;QACxC,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC1B,MAAM,kBAAkB,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE;gBACzD,IAAI,CAAC;oBACH,2EAA2E;oBAC3E,MAAM,QAAQ,GAAG,OAAO,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;oBAE/C,IAAI,QAAQ,EAAE,CAAC;wBACb,MAAM,YAAY,GAAG,WAAW,CAAC;4BAC/B,IAAI,EAAE,QAAQ;4BACd,OAAO;4BACP,sCAAsC;4BACtC,UAAU,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC,WAAW,CAAC,QAAQ,CAAC;4BACpD,YAAY;4BACZ,KAAK,EAAE,KAAK,GAAG,CAAC;4BAChB,QAAQ;yBACT,CAAC,CAAC;wBAEH,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,GAAG;4BACpB,IAAI,EAAE,IAAI,CAAC,OAAO,EAAE;4BACpB,QAAQ,EAAE,YAAY;4BACtB,UAAU,EAAE;gCACV,MAAM,EAAE,IAAI;gCACZ,IAAI,EAAE,QAAQ;gCACd,WAAW,EAAE,WAAW,CAAC;oCACvB,KAAK,EAAE,IAAI,CAAC,KAAK;oCACjB,UAAU,EAAE,EAAE,CAAC,WAAW;iCAC3B,CAAC;6BACH;yBACF,CAAC;oBACJ,CAAC;gBACH,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBACf,OAAO,CAAC,KAAK,CAAC,4BAA4B,IAAI,CAAC,OAAO,EAAE,KAAK,KAAK,EAAE,CAAC,CAAC;gBACxE,CAAC;gBACD,OAAO,GAAG,CAAC;YACb,CAAC,EAAE,EAAyB,CAAC,CAAC;YAE9B,IAAI,MAAM,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC/C,MAAM,GAAG;oBACP,IAAI,EAAE,QAAQ;oBACd,UAAU,EAAE,kBAAkB;oBAC9B,GAAG,cAAc;iBAClB,CAAC;YACJ,CAAC;iBAAM,CAAC;gBACN,MAAM,GAAG,WAAW,CAAC;YACvB,CAAC;QACH,CAAC;aAAM,CAAC;YACN,MAAM,GAAG,WAAW,CAAC;QACvB,CAAC;IACH,CAAC;IACD,wBAAwB;SACnB,IACH,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC;QAC5B,IAAI,CAAC,iBAAiB,EAAE,CAAC,MAAM,GAAG,CAAC,EACnC,CAAC;QACD,MAAM,GAAG;YACP,IAAI,EAAE,UAAU;YAChB,UAAU,EAAE,EAAE;YACd,UAAU,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,cAAc,EAAE;YAC/C,GAAG,cAAc;SAClB,CAAC;IACJ,CAAC;IAED,sBAAsB;IACtB,kDAAkD;IAClD,mGAAmG;IACnG,2CAA2C;IAC3C,6CAA6C;IAC7C,iCAAiC;IACjC,oEAAoE;IACpE,cAAc;IACd,0DAA0D;IAC1D,0BAA0B;IAC1B,+CAA+C;IAC/C,8BAA8B;IAC9B,uBAAuB;IACvB,qDAAqD;IACrD,oEAAoE;IACpE,4BAA4B;IAC5B,gCAAgC;IAChC,wBAAwB;IACxB,gBAAgB;IAChB,oCAAoC;IACpC,oCAAoC;IACpC,sCAAsC;IACtC,4BAA4B;IAC5B,8BAA8B;IAC9B,gCAAgC;IAChC,2CAA2C;IAC3C,qCAAqC;IACrC,8CAA8C;IAC9C,oBAAoB;IACpB,iBAAiB;IACjB,eAAe;IACf,YAAY;IACZ,0BAA0B;IAC1B,iFAAiF;IACjF,UAAU;IACV,oBAAoB;IACpB,qCAAqC;IAErC,wDAAwD;IACxD,mBAAmB;IACnB,0BAA0B;IAC1B,0CAA0C;IAC1C,6BAA6B;IAC7B,WAAW;IACX,eAAe;IACf,8BAA8B;IAC9B,QAAQ;IACR,aAAa;IACb,4BAA4B;IAC5B,MAAM;IACN,IAAI;IACJ,8DAA8D;SACzD,CAAC;QACJ,MAAM,GAAG,WAAW,CAAC;IACvB,CAAC;IAED,0CAA0C;IAC1C,YAAY,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,OAAO,MAAM,CAAC;AAChB,CAAC"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@knapsack/spec-utils",
3
3
  "description": "",
4
- "version": "4.75.8--canary.5662.4aaa414.0",
4
+ "version": "4.75.8",
5
5
  "type": "module",
6
6
  "exports": {
7
7
  ".": {
@@ -19,17 +19,17 @@
19
19
  "test": "ava"
20
20
  },
21
21
  "dependencies": {
22
- "@knapsack/utils": "4.75.8--canary.5662.4aaa414.0",
22
+ "@knapsack/utils": "4.75.8",
23
23
  "typescript": "^5.7.3"
24
24
  },
25
25
  "devDependencies": {
26
- "@knapsack/angular-sandbox-components": "4.75.8--canary.5662.4aaa414.0",
27
- "@knapsack/eslint-config-starter": "4.75.8--canary.5662.4aaa414.0",
28
- "@knapsack/prettier-config": "4.75.8--canary.5662.4aaa414.0",
29
- "@knapsack/sandbox-components": "4.75.8--canary.5662.4aaa414.0",
30
- "@knapsack/test-ava": "4.75.8--canary.5662.4aaa414.0",
31
- "@knapsack/types": "4.75.8--canary.5662.4aaa414.0",
32
- "@knapsack/typescript-config-starter": "4.75.8--canary.5662.4aaa414.0",
26
+ "@knapsack/angular-sandbox-components": "4.75.8",
27
+ "@knapsack/eslint-config-starter": "4.75.8",
28
+ "@knapsack/prettier-config": "4.75.8",
29
+ "@knapsack/sandbox-components": "4.75.8",
30
+ "@knapsack/test-ava": "4.75.8",
31
+ "@knapsack/types": "4.75.8",
32
+ "@knapsack/typescript-config-starter": "4.75.8",
33
33
  "@types/node": "^20.17.17",
34
34
  "ava": "^6.2.0",
35
35
  "eslint": "^8.57.0"
@@ -43,5 +43,5 @@
43
43
  "directory": "apps/client/libs/spec-utils",
44
44
  "type": "git"
45
45
  },
46
- "gitHead": "4aaa4141d29cd422ab27528c231cc2740e531dfb"
46
+ "gitHead": "56fb9c1d8b39ce275d2dba24b9b93d97c9a213ca"
47
47
  }
@@ -2,39 +2,76 @@ import ts from 'typescript';
2
2
  import { TypeInfo, TypeInfoCommon } from './types.js';
3
3
  import { getSetFlags } from './utils.js';
4
4
 
5
+ // Cache for type IDs to improve performance
6
+ const typeIdMap = new WeakMap<ts.Type, string>();
7
+
8
+ /**
9
+ * Generates a consistent identifier for a TypeScript type
10
+ * Uses the type's string representation to ensure identical types get the same ID
11
+ */
12
+ function getTypeId(type: ts.Type, checker: ts.TypeChecker): string {
13
+ // Check if we already have an ID for this type
14
+ const existingId = typeIdMap.get(type);
15
+ if (existingId) {
16
+ return existingId;
17
+ }
18
+
19
+ // Generate a new ID based on the type's string representation
20
+ // This ensures identical types get the same ID
21
+ const typeString = checker.typeToString(
22
+ type,
23
+ undefined,
24
+ // eslint-disable-next-line no-bitwise
25
+ ts.TypeFormatFlags.NoTruncation,
26
+ );
27
+
28
+ // Add some additional information to help distinguish types
29
+ // that might have the same string representation
30
+ const idComponents = [typeString];
31
+
32
+ // Add symbol name if available
33
+ const symbol = type.getSymbol();
34
+ if (symbol) {
35
+ idComponents.push(symbol.getName());
36
+ }
37
+
38
+ // Add type flags to help distinguish types
39
+ idComponents.push(String(type.flags));
40
+
41
+ const newId = idComponents.join('_');
42
+ typeIdMap.set(type, newId);
43
+ return newId;
44
+ }
45
+
46
+ /**
47
+ * Analyzes a TypeScript type and returns a structured TypeInfo representation
48
+ */
5
49
  export function analyzeType({
6
50
  type,
7
51
  checker,
8
52
  isOptional,
9
- visitedTypes = new Set(),
53
+ visitedTypes = new Map<string, TypeInfo>(),
54
+ depth = 0,
55
+ /**
56
+ * The maximum depth of type analysis.
57
+ * This is used to avoid infinite recursion.
58
+ */
59
+ maxDepth = 3,
10
60
  }: {
11
61
  type: ts.Type;
12
62
  checker: ts.TypeChecker;
13
63
  isOptional?: boolean;
14
- visitedTypes?: Set<ts.Type>;
64
+ visitedTypes?: Map<string, TypeInfo>;
65
+ depth?: number;
66
+ maxDepth?: number;
15
67
  }): TypeInfo {
16
- // Prevent infinite recursion
17
- if (visitedTypes.has(type)) {
18
- return {
19
- type: 'misc',
20
- tsRawType: checker.typeToString(
21
- type,
22
- undefined,
23
- // eslint-disable-next-line no-bitwise
24
- ts.TypeFormatFlags.MultilineObjectLiterals |
25
- ts.TypeFormatFlags.NoTruncation,
26
- ),
27
- tsMetadata: {
28
- typeFlags: getSetFlags({
29
- flags: type.flags,
30
- enumObject: ts.TypeFlags,
31
- }),
32
- type,
33
- },
34
- isOptional,
35
- };
68
+ // Generate a unique key for the type
69
+ const typeId = getTypeId(type, checker);
70
+
71
+ // Check if we've already analyzed this type
72
+ if (visitedTypes.has(typeId)) {
73
+ return visitedTypes.get(typeId)!;
36
74
  }
37
- visitedTypes.add(type);
38
75
 
39
76
  const tsRawType = checker.typeToString(
40
77
  type,
@@ -56,51 +93,82 @@ export function analyzeType({
56
93
  isOptional,
57
94
  };
58
95
 
96
+ // If we've reached max depth, return a simplified type
97
+ if (depth >= maxDepth) {
98
+ const result: TypeInfo = {
99
+ type: 'misc',
100
+ ...typeInfoCommon,
101
+ };
102
+ visitedTypes.set(typeId, result);
103
+ return result;
104
+ }
105
+
106
+ let result: TypeInfo;
107
+
59
108
  // Handle primitive types first
60
109
  if (
61
110
  tsRawType === 'number' ||
62
111
  tsRawType === 'boolean' ||
63
112
  tsRawType === 'string'
64
113
  ) {
65
- return {
114
+ result = {
66
115
  type: tsRawType,
67
116
  ...typeInfoCommon,
68
117
  };
118
+ visitedTypes.set(typeId, result);
119
+ return result;
69
120
  }
70
121
 
71
122
  // Handle literal types
72
123
  if (type.isStringLiteral()) {
73
- return {
124
+ result = {
74
125
  type: 'stringLiteral',
75
126
  value: type.value,
76
127
  ...typeInfoCommon,
77
128
  };
129
+ visitedTypes.set(typeId, result);
130
+ return result;
78
131
  }
79
132
 
80
133
  if (type.isNumberLiteral()) {
81
- return {
134
+ result = {
82
135
  type: 'numberLiteral',
83
136
  value: type.value,
84
137
  ...typeInfoCommon,
85
138
  };
139
+ visitedTypes.set(typeId, result);
140
+ return result;
86
141
  }
87
142
 
143
+ // Create a placeholder to avoid infinite recursion
144
+ const placeholder: TypeInfo = {
145
+ type: 'misc',
146
+ ...typeInfoCommon,
147
+ };
148
+ visitedTypes.set(typeId, placeholder);
149
+
88
150
  // Handle unions
89
151
  if (type.isUnion()) {
90
- return {
152
+ result = {
91
153
  type: 'union',
92
154
  items: type.types.map((t) =>
93
- analyzeType({ type: t, checker, isOptional, visitedTypes }),
155
+ analyzeType({
156
+ type: t,
157
+ checker,
158
+ isOptional,
159
+ visitedTypes,
160
+ depth: depth + 1,
161
+ maxDepth,
162
+ }),
94
163
  ),
95
164
  ...typeInfoCommon,
96
165
  };
97
166
  }
98
-
99
167
  // Handle arrays
100
- if (checker.isArrayType(type) || checker.isArrayLikeType(type)) {
168
+ else if (checker.isArrayType(type) || checker.isArrayLikeType(type)) {
101
169
  const indexInfos = checker.getIndexInfosOfType(type);
102
170
 
103
- return {
171
+ result = {
104
172
  type: 'array',
105
173
  items:
106
174
  indexInfos.length === 1
@@ -109,19 +177,20 @@ export function analyzeType({
109
177
  checker,
110
178
  isOptional,
111
179
  visitedTypes,
180
+ depth: depth + 1,
181
+ maxDepth,
112
182
  })
113
183
  : { type: 'misc', ...typeInfoCommon },
114
184
  ...typeInfoCommon,
115
185
  };
116
186
  }
117
-
118
187
  // Handle intersections
119
188
  // This is where ForwardRef is being inferred
120
189
  // @TODO: We need to find a way to handle depth here. Currently we bring in all of the properties
121
190
  // from each type in the intersection, but we may want to limit the depth of the intersection
122
191
  // to avoid bringing in too much.
123
192
  // https://linear.app/knapsack/issue/KSP-5885/update-handling-of-intersection-types-in-spec-utils
124
- if (type.isIntersection()) {
193
+ else if (type.isIntersection()) {
125
194
  const properties = type.getProperties();
126
195
  if (properties.length > 0) {
127
196
  const analyzedProperties = properties.reduce((acc, prop) => {
@@ -136,6 +205,8 @@ export function analyzeType({
136
205
  // eslint-disable-next-line no-bitwise
137
206
  isOptional: !!(prop.flags & ts.SymbolFlags.Optional),
138
207
  visitedTypes,
208
+ depth: depth + 1,
209
+ maxDepth,
139
210
  });
140
211
 
141
212
  acc[prop.getName()] = {
@@ -158,18 +229,24 @@ export function analyzeType({
158
229
  }, {} as Record<string, any>);
159
230
 
160
231
  if (Object.keys(analyzedProperties).length > 0) {
161
- return {
232
+ result = {
162
233
  type: 'object',
163
234
  properties: analyzedProperties,
164
235
  ...typeInfoCommon,
165
236
  };
237
+ } else {
238
+ result = placeholder;
166
239
  }
240
+ } else {
241
+ result = placeholder;
167
242
  }
168
243
  }
169
-
170
244
  // Handle function types
171
- if (typeFlags.includes('Object') && type.getCallSignatures().length > 0) {
172
- return {
245
+ else if (
246
+ typeFlags.includes('Object') &&
247
+ type.getCallSignatures().length > 0
248
+ ) {
249
+ result = {
173
250
  type: 'function',
174
251
  parameters: [],
175
252
  returnType: { type: 'misc', ...typeInfoCommon },
@@ -180,58 +257,60 @@ export function analyzeType({
180
257
  // Handle object types
181
258
  // @TODO: update handling of object type inference
182
259
  // https://linear.app/knapsack/issue/KSP-5880/handle-arrays-of-objects-in-react-renderer-infer-spec
183
- // if (typeFlags.includes('Object')) {
260
+ // else if (typeFlags.includes('Object')) {
184
261
  // const properties = type.getProperties();
185
262
  // if (properties.length > 0) {
186
- // const analyzedProperties = properties.reduce<Record<string, SymbolInfo>>(
187
- // (acc, prop) => {
188
- // try {
189
- // const propType = checker.getTypeOfSymbol(prop);
190
- // if (propType) {
191
- // const propAnalysis = analyzeType({
263
+ // const analyzedProperties = properties.reduce((acc, prop) => {
264
+ // try {
265
+ // const propType = checker.getTypeOfSymbol(prop);
266
+ // if (propType) {
267
+ // const propAnalysis = analyzeType({
268
+ // type: propType,
269
+ // checker,
270
+ // // eslint-disable-next-line no-bitwise
271
+ // isOptional: !!(prop.flags & ts.SymbolFlags.Optional),
272
+ // visitedTypes,
273
+ // depth: depth + 1,
274
+ // maxDepth,
275
+ // });
276
+ // acc[prop.getName()] = {
277
+ // name: prop.getName(),
278
+ // typeInfo: propAnalysis,
279
+ // tsMetadata: {
280
+ // symbol: prop,
192
281
  // type: propType,
193
- // checker,
194
- // // eslint-disable-next-line no-bitwise
195
- // isOptional: !!(prop.flags & ts.SymbolFlags.Optional),
196
- // visitedTypes,
197
- // depth: depth + 1,
198
- // });
199
- // acc[prop.getName()] = {
200
- // name: prop.getName(),
201
- // typeInfo: propAnalysis,
202
- // tsMetadata: {
203
- // symbol: prop,
204
- // type: propType,
205
- // symbolFlags: getSetFlags({
206
- // flags: prop.flags,
207
- // enumObject: ts.SymbolFlags,
208
- // }),
209
- // },
210
- // };
211
- // }
212
- // } catch (error) {
213
- // console.debug(
214
- // `Error analyzing property ${prop.getName()}: ${error}`,
215
- // );
282
+ // symbolFlags: getSetFlags({
283
+ // flags: prop.flags,
284
+ // enumObject: ts.SymbolFlags,
285
+ // }),
286
+ // },
287
+ // };
216
288
  // }
217
- // return acc;
218
- // },
219
- // {},
220
- // );
289
+ // } catch (error) {
290
+ // console.debug(`Error analyzing property ${prop.getName()}: ${error}`);
291
+ // }
292
+ // return acc;
293
+ // }, {} as Record<string, any>);
221
294
 
222
295
  // if (Object.keys(analyzedProperties).length > 0) {
223
- // return {
296
+ // result = {
224
297
  // type: 'object',
225
298
  // properties: analyzedProperties,
226
299
  // ...typeInfoCommon,
227
300
  // };
301
+ // } else {
302
+ // result = placeholder;
228
303
  // }
304
+ // } else {
305
+ // result = placeholder;
229
306
  // }
230
307
  // }
231
-
232
308
  // Default to misc type for anything we can't properly analyze
233
- return {
234
- type: 'misc',
235
- ...typeInfoCommon,
236
- };
309
+ else {
310
+ result = placeholder;
311
+ }
312
+
313
+ // Update the cache with the actual result
314
+ visitedTypes.set(typeId, result);
315
+ return result;
237
316
  }