@xlr-lib/xlr-utils 0.2.0--canary.2.131 → 1.0.0

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,3 @@
1
- import ts from "typescript";
2
1
  import type {
3
2
  NamedType,
4
3
  NodeType,
@@ -10,167 +9,6 @@ import type {
10
9
  import { computeExtends, resolveConditional } from "./validation-helpers";
11
10
  import { isGenericNamedType, isGenericNodeType } from "./type-checks";
12
11
 
13
- /**
14
- * Returns the required type or the optionally required type
15
- */
16
- export function tsStripOptionalType(node: ts.TypeNode): ts.TypeNode {
17
- return ts.isOptionalTypeNode(node) ? node.type : node;
18
- }
19
-
20
- /**
21
- * Returns if the top level declaration is exported
22
- */
23
- export function isExportedDeclaration(node: ts.Statement): boolean {
24
- const modifiers = ts.canHaveModifiers(node)
25
- ? ts.getModifiers(node)
26
- : undefined;
27
-
28
- if (modifiers) {
29
- return modifiers.some((m) => m.kind === ts.SyntaxKind.ExportKeyword);
30
- }
31
- return false;
32
- }
33
-
34
- /**
35
- * Returns if the node is exported from the source file
36
- */
37
- export function isNodeExported(node: ts.Node): boolean {
38
- return (
39
- (ts.getCombinedModifierFlags(node as ts.Declaration) &
40
- ts.ModifierFlags.Export) !==
41
- 0 ||
42
- (!!node.parent && node.parent.kind === ts.SyntaxKind.SourceFile)
43
- );
44
- }
45
-
46
- /**
47
- * Returns the actual type and will following import chains if needed
48
- */
49
- export function getReferencedType(
50
- node: ts.TypeReferenceNode,
51
- typeChecker: ts.TypeChecker,
52
- ) {
53
- let symbol = typeChecker.getSymbolAtLocation(node.typeName);
54
-
55
- if (
56
- symbol &&
57
- (symbol.flags & ts.SymbolFlags.Alias) === ts.SymbolFlags.Alias
58
- ) {
59
- // follow alias if it is a symbol
60
- symbol = typeChecker.getAliasedSymbol(symbol);
61
- }
62
-
63
- const varDecl = symbol?.declarations?.[0];
64
- if (
65
- varDecl &&
66
- (ts.isInterfaceDeclaration(varDecl) || ts.isTypeAliasDeclaration(varDecl))
67
- ) {
68
- return { declaration: varDecl, exported: isNodeExported(varDecl) };
69
- }
70
- }
71
-
72
- /**
73
- * Checks if a type reference points to a TypeScript built-in type
74
- * by examining whether its declaration comes from TypeScript's lib files.
75
- *
76
- * This is more robust than maintaining a hardcoded list of built-in types
77
- * as it automatically handles all TypeScript lib types (Map, Set, WeakMap,
78
- * Promise, Array, Date, Error, RegExp, etc.).
79
- */
80
- export function isTypeScriptLibType(
81
- node: ts.TypeReferenceNode,
82
- typeChecker: ts.TypeChecker,
83
- ): boolean {
84
- let symbol = typeChecker.getSymbolAtLocation(node.typeName);
85
-
86
- if (!symbol) return false;
87
-
88
- // Follow alias if it is a symbol
89
- if ((symbol.flags & ts.SymbolFlags.Alias) === ts.SymbolFlags.Alias) {
90
- symbol = typeChecker.getAliasedSymbol(symbol);
91
- }
92
-
93
- const declarations = symbol.getDeclarations();
94
- if (!declarations || declarations.length === 0) return false;
95
-
96
- // Check if any declaration comes from TypeScript lib files
97
- return declarations.some((decl) => {
98
- const sourceFile = decl.getSourceFile();
99
- if (!sourceFile) return false;
100
-
101
- const filePath = sourceFile.fileName;
102
- return (
103
- filePath.includes("/typescript/lib/") ||
104
- filePath.includes("\\typescript\\lib\\") ||
105
- (filePath.endsWith(".d.ts") && filePath.includes("lib."))
106
- );
107
- });
108
- }
109
-
110
- /**
111
- * Returns list of string literals from potential union of strings
112
- */
113
- export function getStringLiteralsFromUnion(node: ts.Node): Set<string> {
114
- if (ts.isUnionTypeNode(node)) {
115
- return new Set(
116
- node.types.map((type) => {
117
- if (ts.isLiteralTypeNode(type) && ts.isStringLiteral(type.literal)) {
118
- return type.literal.text;
119
- }
120
-
121
- return "";
122
- }),
123
- );
124
- }
125
-
126
- if (ts.isLiteralTypeNode(node) && ts.isStringLiteral(node.literal)) {
127
- return new Set([node.literal.text]);
128
- }
129
-
130
- return new Set();
131
- }
132
-
133
- /**
134
- * Converts a format string into a regex that can be used to validate a given string matches the template
135
- */
136
- export function buildTemplateRegex(
137
- node: ts.TemplateLiteralTypeNode,
138
- typeChecker: ts.TypeChecker,
139
- ): string {
140
- let regex = node.head.text;
141
- node.templateSpans.forEach((span) => {
142
- // process template tag
143
- let type = span.type.kind;
144
- if (ts.isTypeReferenceNode(span.type)) {
145
- let symbol = typeChecker.getSymbolAtLocation(
146
- span.type.typeName,
147
- ) as ts.Symbol;
148
-
149
- if (
150
- symbol &&
151
- (symbol.flags & ts.SymbolFlags.Alias) === ts.SymbolFlags.Alias
152
- ) {
153
- // follow alias if it is a symbol
154
- symbol = typeChecker.getAliasedSymbol(symbol);
155
- }
156
-
157
- type = (symbol?.declarations?.[0] as ts.TypeAliasDeclaration).type.kind;
158
- }
159
-
160
- if (type === ts.SyntaxKind.StringKeyword) {
161
- regex += ".*";
162
- } else if (type === ts.SyntaxKind.NumberKeyword) {
163
- regex += "[0-9]*";
164
- } else if (type === ts.SyntaxKind.BooleanKeyword) {
165
- regex += "true|false";
166
- }
167
-
168
- // add non-tag element
169
- regex += span.literal.text;
170
- });
171
- return regex;
172
- }
173
-
174
12
  /**
175
13
  * Walks generics to fill in values from a combination of the default, constraint, and passed in map values
176
14
  * TODO convert this to use simpleTransformGenerator
@@ -178,12 +16,22 @@ export function buildTemplateRegex(
178
16
  export function fillInGenerics(
179
17
  xlrNode: NodeType,
180
18
  generics?: Map<string, NodeType>,
19
+ preferLocalGenerics = false,
181
20
  ): NodeType {
182
21
  // Need to make sure not to set generics in passed in map to avoid using generics outside of tree
183
22
  let localGenerics: Map<string, NodeType>;
184
23
 
185
24
  if (generics) {
186
25
  localGenerics = new Map(generics);
26
+ if (preferLocalGenerics && isGenericNodeType(xlrNode)) {
27
+ xlrNode.genericTokens?.forEach((token) => {
28
+ const genericValue = (token.default ?? token.constraints) as NodeType;
29
+ localGenerics.set(
30
+ token.symbol,
31
+ fillInGenerics(genericValue, localGenerics, preferLocalGenerics),
32
+ );
33
+ });
34
+ }
187
35
  } else {
188
36
  localGenerics = new Map();
189
37
  if (isGenericNodeType(xlrNode)) {
@@ -191,7 +39,7 @@ export function fillInGenerics(
191
39
  const genericValue = (token.default ?? token.constraints) as NodeType;
192
40
  localGenerics.set(
193
41
  token.symbol,
194
- fillInGenerics(genericValue, localGenerics),
42
+ fillInGenerics(genericValue, localGenerics, preferLocalGenerics),
195
43
  );
196
44
  });
197
45
  }
@@ -204,7 +52,7 @@ export function fillInGenerics(
204
52
  ...(xlrNode.genericArguments
205
53
  ? {
206
54
  genericArguments: xlrNode.genericArguments.map((ga) =>
207
- fillInGenerics(ga, localGenerics),
55
+ fillInGenerics(ga, localGenerics, preferLocalGenerics),
208
56
  ),
209
57
  }
210
58
  : {}),
@@ -220,7 +68,7 @@ export function fillInGenerics(
220
68
  ...(xlrNode.genericArguments
221
69
  ? {
222
70
  genericArguments: xlrNode.genericArguments.map((ga) =>
223
- fillInGenerics(ga, localGenerics),
71
+ fillInGenerics(ga, localGenerics, preferLocalGenerics),
224
72
  ),
225
73
  }
226
74
  : {}),
@@ -233,7 +81,7 @@ export function fillInGenerics(
233
81
  const prop = xlrNode.properties[propName];
234
82
  newProperties[propName] = {
235
83
  required: prop.required,
236
- node: fillInGenerics(prop.node, localGenerics),
84
+ node: fillInGenerics(prop.node, localGenerics, preferLocalGenerics),
237
85
  };
238
86
  });
239
87
 
@@ -246,20 +94,36 @@ export function fillInGenerics(
246
94
  return {
247
95
  ...token,
248
96
  constraints: token.constraints
249
- ? fillInGenerics(token.constraints, localGenerics)
97
+ ? fillInGenerics(
98
+ token.constraints,
99
+ localGenerics,
100
+ preferLocalGenerics,
101
+ )
250
102
  : undefined,
251
103
  default: token.default
252
- ? fillInGenerics(token.default, localGenerics)
104
+ ? fillInGenerics(
105
+ token.default,
106
+ localGenerics,
107
+ preferLocalGenerics,
108
+ )
253
109
  : undefined,
254
110
  };
255
111
  }),
256
112
  }
257
113
  : {}),
258
114
  extends: xlrNode.extends
259
- ? (fillInGenerics(xlrNode.extends, localGenerics) as RefNode)
115
+ ? (fillInGenerics(
116
+ xlrNode.extends,
117
+ localGenerics,
118
+ preferLocalGenerics,
119
+ ) as RefNode)
260
120
  : undefined,
261
121
  additionalProperties: xlrNode.additionalProperties
262
- ? fillInGenerics(xlrNode.additionalProperties, localGenerics)
122
+ ? fillInGenerics(
123
+ xlrNode.additionalProperties,
124
+ localGenerics,
125
+ preferLocalGenerics,
126
+ )
263
127
  : false,
264
128
  };
265
129
  }
@@ -267,7 +131,11 @@ export function fillInGenerics(
267
131
  if (xlrNode.type === "array") {
268
132
  return {
269
133
  ...xlrNode,
270
- elementType: fillInGenerics(xlrNode.elementType, localGenerics),
134
+ elementType: fillInGenerics(
135
+ xlrNode.elementType,
136
+ localGenerics,
137
+ preferLocalGenerics,
138
+ ),
271
139
  };
272
140
  } else if (xlrNode.type === "or" || xlrNode.type === "and") {
273
141
  let pointer;
@@ -280,25 +148,49 @@ export function fillInGenerics(
280
148
  return {
281
149
  ...xlrNode,
282
150
  [xlrNode.type]: pointer.map((prop) => {
283
- return fillInGenerics(prop, localGenerics);
151
+ return fillInGenerics(prop, localGenerics, preferLocalGenerics);
284
152
  }),
285
153
  };
286
154
  } else if (xlrNode.type === "record") {
287
155
  return {
288
156
  ...xlrNode,
289
- keyType: fillInGenerics(xlrNode.keyType, localGenerics),
290
- valueType: fillInGenerics(xlrNode.valueType, localGenerics),
157
+ keyType: fillInGenerics(
158
+ xlrNode.keyType,
159
+ localGenerics,
160
+ preferLocalGenerics,
161
+ ),
162
+ valueType: fillInGenerics(
163
+ xlrNode.valueType,
164
+ localGenerics,
165
+ preferLocalGenerics,
166
+ ),
291
167
  };
292
168
  } else if (xlrNode.type === "conditional") {
293
169
  const filledInConditional = {
294
170
  ...xlrNode,
295
171
  check: {
296
- left: fillInGenerics(xlrNode.check.left, localGenerics),
297
- right: fillInGenerics(xlrNode.check.right, localGenerics),
172
+ left: fillInGenerics(
173
+ xlrNode.check.left,
174
+ localGenerics,
175
+ preferLocalGenerics,
176
+ ),
177
+ right: fillInGenerics(
178
+ xlrNode.check.right,
179
+ localGenerics,
180
+ preferLocalGenerics,
181
+ ),
298
182
  },
299
183
  value: {
300
- true: fillInGenerics(xlrNode.value.true, localGenerics),
301
- false: fillInGenerics(xlrNode.value.false, localGenerics),
184
+ true: fillInGenerics(
185
+ xlrNode.value.true,
186
+ localGenerics,
187
+ preferLocalGenerics,
188
+ ),
189
+ false: fillInGenerics(
190
+ xlrNode.value.false,
191
+ localGenerics,
192
+ preferLocalGenerics,
193
+ ),
302
194
  },
303
195
  };
304
196
 
package/types/index.d.ts CHANGED
@@ -1,6 +1,4 @@
1
- export * from "./annotations";
2
- export * from "./ts-helpers";
1
+ export * from "./xlr-helpers";
3
2
  export * from "./type-checks";
4
3
  export * from "./validation-helpers";
5
- export * from "./documentation";
6
4
  //# sourceMappingURL=index.d.ts.map
@@ -1,31 +1,4 @@
1
- import ts from "typescript";
2
1
  import type { NamedType, NamedTypeWithGenerics, NodeType, NodeTypeWithGenerics, PrimitiveTypes, StringType, NumberType, BooleanType, ObjectType, ArrayType, RefType, OrType, AndType, RecordType } from "@xlr-lib/xlr";
3
- /**
4
- * Returns if the Object Property is optional
5
- */
6
- export declare function isOptionalProperty(node: ts.PropertySignature): boolean;
7
- /**
8
- * Returns if the node is an Interface or Type with Generics
9
- */
10
- export declare function isGenericInterfaceDeclaration(node: ts.InterfaceDeclaration): boolean;
11
- /**
12
- * Returns if the node is an Type Declaration with Generics
13
- */
14
- export declare function isGenericTypeDeclaration(node: ts.TypeAliasDeclaration): boolean;
15
- /**
16
- * Returns if the referenced type is a generic
17
- */
18
- export declare function isTypeReferenceGeneric(node: ts.TypeReferenceNode, typeChecker: ts.TypeChecker): boolean;
19
- export type TopLevelDeclaration = ts.InterfaceDeclaration | ts.TypeAliasDeclaration;
20
- /**
21
- * Returns if the node is an interface or a type declaration
22
- */
23
- export declare function isTopLevelDeclaration(node: ts.Node): node is TopLevelDeclaration;
24
- export type TopLevelNode = TopLevelDeclaration | ts.VariableStatement;
25
- /**
26
- * Returns if the node is an interface or a type declaration
27
- */
28
- export declare function isTopLevelNode(node: ts.Node): node is TopLevelNode;
29
2
  /**
30
3
  * Returns if the NodeType has generic tokens
31
4
  */
@@ -0,0 +1,13 @@
1
+ import type { NodeType, OrType } from "@xlr-lib/xlr";
2
+ /**
3
+ * Walks generics to fill in values from a combination of the default, constraint, and passed in map values
4
+ * TODO convert this to use simpleTransformGenerator
5
+ */
6
+ export declare function fillInGenerics(xlrNode: NodeType, generics?: Map<string, NodeType>, preferLocalGenerics?: boolean): NodeType;
7
+ /** Applies the TS `Pick` or `Omit` type to an interface/union/intersection */
8
+ export declare function applyPickOrOmitToNodeType(baseObject: NodeType, operation: "Pick" | "Omit", properties: Set<string>): NodeType | undefined;
9
+ /** Applies the TS `Partial` or `Required` type to an interface/union/intersection */
10
+ export declare function applyPartialOrRequiredToNodeType(baseObject: NodeType, modifier: boolean): NodeType;
11
+ /** Applies the TS `Exclude` type to a union */
12
+ export declare function applyExcludeToNodeType(baseObject: OrType, filters: NodeType | OrType): NodeType;
13
+ //# sourceMappingURL=xlr-helpers.d.ts.map
@@ -1,40 +0,0 @@
1
- import { test, expect, describe } from "vitest";
2
- import { setupTestEnv } from "@xlr-lib/test-utils";
3
- import { decorateNode } from "../annotations";
4
-
5
- describe("Annotations", () => {
6
- test("JSDoc comments to strings", () => {
7
- const sc = `
8
- /**
9
- * An asset is the smallest unit of user interaction in a player view
10
- * @example Example usage of interface Asset
11
- * @see Asset for implementation details
12
- * @default default value
13
- */
14
- export interface Asset<T extends string = string> {
15
- id: string;
16
- [key: string]: unknown;
17
- }
18
- `;
19
-
20
- const { sf } = setupTestEnv(sc);
21
- expect(decorateNode(sf.statements[0])).toMatchSnapshot();
22
- });
23
-
24
- test("JSDoc @meta", () => {
25
- const sc = `
26
- /**
27
- * An asset is the smallest unit of user interaction in a player view
28
- * @meta category:views
29
- * @meta screenshot:/path/image.png
30
- */
31
- export interface Asset<T extends string = string> {
32
- id: string;
33
- [key: string]: unknown;
34
- }
35
- `;
36
-
37
- const { sf } = setupTestEnv(sc);
38
- expect(decorateNode(sf.statements[0])).toMatchSnapshot();
39
- });
40
- });
@@ -1,116 +0,0 @@
1
- import { test, expect, describe } from "vitest";
2
- import type { FunctionType, OrType, TupleType } from "@xlr-lib/xlr";
3
- import { createDocString } from "../documentation";
4
-
5
- describe("docs", () => {
6
- test("or", () => {
7
- const type1: OrType = {
8
- type: "or",
9
- or: [
10
- {
11
- type: "string",
12
- },
13
- {
14
- type: "array",
15
- elementType: {
16
- type: "string",
17
- },
18
- },
19
- ],
20
- };
21
-
22
- expect(createDocString(type1)).toMatchInlineSnapshot(
23
- `"string | Array<string>"`,
24
- );
25
- });
26
-
27
- test("function", () => {
28
- const type1: FunctionType = {
29
- type: "function",
30
- name: "testABC",
31
- parameters: [
32
- {
33
- name: "a",
34
- type: {
35
- type: "string",
36
- },
37
- },
38
- {
39
- name: "b",
40
- type: {
41
- type: "array",
42
- elementType: {
43
- type: "string",
44
- },
45
- },
46
- },
47
- ],
48
- returnType: {
49
- type: "string",
50
- },
51
- };
52
-
53
- expect(createDocString(type1)).toMatchInlineSnapshot(
54
- `"function testABC(a: string, b: Array<string>): string"`,
55
- );
56
- });
57
-
58
- test("tuple", () => {
59
- const type1: TupleType = {
60
- type: "tuple",
61
- name: "testABC",
62
- elementTypes: [
63
- {
64
- name: "a",
65
- type: {
66
- type: "string",
67
- },
68
- },
69
- {
70
- type: {
71
- type: "array",
72
- elementType: {
73
- type: "string",
74
- },
75
- },
76
- },
77
- ],
78
- minItems: 2,
79
- additionalItems: false,
80
- };
81
-
82
- expect(createDocString(type1)).toMatchInlineSnapshot(
83
- `"[a: string, Array<string>]"`,
84
- );
85
- });
86
-
87
- test("const", () => {
88
- const type1: OrType = {
89
- type: "or",
90
- or: [
91
- {
92
- type: "string",
93
- const: "abc",
94
- },
95
- {
96
- type: "number",
97
- const: 123,
98
- },
99
- {
100
- type: "boolean",
101
- const: true,
102
- },
103
- {
104
- type: "array",
105
- elementType: {
106
- type: "string",
107
- },
108
- },
109
- ],
110
- };
111
-
112
- expect(createDocString(type1)).toMatchInlineSnapshot(
113
- `""abc" | 123 | true | Array<string>"`,
114
- );
115
- });
116
- });