@pikku/inspector 0.6.2

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.
Files changed (48) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.md +3 -0
  3. package/dist/add-channel.d.ts +3 -0
  4. package/dist/add-channel.js +122 -0
  5. package/dist/add-file-extends-core-type.d.ts +3 -0
  6. package/dist/add-file-extends-core-type.js +38 -0
  7. package/dist/add-file-with-config.d.ts +3 -0
  8. package/dist/add-file-with-config.js +31 -0
  9. package/dist/add-file-with-factory.d.ts +3 -0
  10. package/dist/add-file-with-factory.js +48 -0
  11. package/dist/add-route.d.ts +4 -0
  12. package/dist/add-route.js +89 -0
  13. package/dist/add-schedule.d.ts +3 -0
  14. package/dist/add-schedule.js +32 -0
  15. package/dist/does-type-extend-core-type.d.ts +2 -0
  16. package/dist/does-type-extend-core-type.js +41 -0
  17. package/dist/get-property-value.d.ts +3 -0
  18. package/dist/get-property-value.js +60 -0
  19. package/dist/index.d.ts +4 -0
  20. package/dist/index.js +1 -0
  21. package/dist/inspector.d.ts +3 -0
  22. package/dist/inspector.js +43 -0
  23. package/dist/types-map.d.ts +18 -0
  24. package/dist/types-map.js +103 -0
  25. package/dist/types.d.ts +49 -0
  26. package/dist/types.js +1 -0
  27. package/dist/utils.d.ts +30 -0
  28. package/dist/utils.js +245 -0
  29. package/dist/visit.d.ts +3 -0
  30. package/dist/visit.js +17 -0
  31. package/package.json +30 -0
  32. package/run-tests.sh +53 -0
  33. package/src/add-channel.ts +168 -0
  34. package/src/add-file-extends-core-type.ts +50 -0
  35. package/src/add-file-with-config.ts +45 -0
  36. package/src/add-file-with-factory.ts +65 -0
  37. package/src/add-route.ts +131 -0
  38. package/src/add-schedule.ts +47 -0
  39. package/src/does-type-extend-core-type.ts +53 -0
  40. package/src/get-property-value.ts +81 -0
  41. package/src/index.ts +4 -0
  42. package/src/inspector.ts +53 -0
  43. package/src/types-map.ts +130 -0
  44. package/src/types.ts +58 -0
  45. package/src/utils.ts +349 -0
  46. package/src/visit.ts +49 -0
  47. package/tsconfig.json +19 -0
  48. package/tsconfig.tsbuildinfo +1 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,17 @@
1
+ # @pikku/inspector
2
+
3
+ ## 0.6.2
4
+
5
+ ### Patch Changes
6
+
7
+ - a40a508: fix: Fixing some generation bugs and other minors
8
+ - Updated dependencies [a40a508]
9
+ - @pikku/core@0.6.5
10
+
11
+ ## 0.6.1
12
+
13
+ ### Patch Changes
14
+
15
+ - f26880f: feat: extracting inspector and adding unique type references
16
+ - Updated dependencies [f26880f]
17
+ - @pikku/core@0.6.4
package/README.md ADDED
@@ -0,0 +1,3 @@
1
+ # Pikku CLI
2
+
3
+ This package is part of @pikku/mono and is responsible for all the intropsection and file generations.
@@ -0,0 +1,3 @@
1
+ import * as ts from 'typescript';
2
+ import { InspectorState } from './types.js';
3
+ export declare const addChannel: (node: ts.Node, checker: ts.TypeChecker, state: InspectorState) => void;
@@ -0,0 +1,122 @@
1
+ import * as ts from 'typescript';
2
+ import { getPropertyValue } from './get-property-value.js';
3
+ import { pathToRegexp } from 'path-to-regexp';
4
+ import { getInputTypes } from './add-route.js';
5
+ import { getPropertyAssignment, getFunctionTypes } from './utils.js';
6
+ const addMessagesRoutes = (obj, checker, typesMap) => {
7
+ const messageTypes = {};
8
+ // Find the onMessageRoute property
9
+ const messagesProperty = obj.properties.find((p) => ts.isPropertyAssignment(p) &&
10
+ ts.isIdentifier(p.name) &&
11
+ p.name.text === 'onMessageRoute');
12
+ if (!messagesProperty || !ts.isPropertyAssignment(messagesProperty)) {
13
+ console.log('onMessageRoute property not found or is not a valid assignment.');
14
+ return {};
15
+ }
16
+ const initializer = messagesProperty.initializer;
17
+ // Ensure initializer is an object literal expression
18
+ if (!ts.isObjectLiteralExpression(initializer)) {
19
+ console.log('onMessageRoute is not an object literal.');
20
+ return {};
21
+ }
22
+ // Iterate over the first level properties (like 'event')
23
+ initializer.properties.forEach((property) => {
24
+ const channel = property.name.getText();
25
+ messageTypes[channel] = {};
26
+ if (ts.isPropertyAssignment(property)) {
27
+ const nestedObject = property.initializer;
28
+ if (ts.isObjectLiteralExpression(nestedObject)) {
29
+ const keys = nestedObject.properties.map((p) => p.name?.getText());
30
+ for (const route of keys) {
31
+ if (route) {
32
+ const result = getFunctionTypes(checker, nestedObject, {
33
+ funcName: route,
34
+ inputIndex: 0,
35
+ outputIndex: 1,
36
+ typesMap,
37
+ });
38
+ const inputs = result?.inputs || null;
39
+ const outputs = result?.outputs || null;
40
+ messageTypes[channel][route] = { inputs, outputs };
41
+ }
42
+ }
43
+ }
44
+ else {
45
+ console.warn('Nested property is not an object literal:', nestedObject);
46
+ }
47
+ }
48
+ else {
49
+ console.warn(`Property "${property.getText()}" is a ${ts.SyntaxKind[property.kind]}`);
50
+ }
51
+ });
52
+ return messageTypes;
53
+ };
54
+ export const addChannel = (node, checker, state) => {
55
+ if (!ts.isCallExpression(node)) {
56
+ return;
57
+ }
58
+ const args = node.arguments;
59
+ const firstArg = args[0];
60
+ const expression = node.expression;
61
+ // Check if the call is to addRoute
62
+ if (!ts.isIdentifier(expression) || expression.text !== 'addChannel') {
63
+ return;
64
+ }
65
+ if (!firstArg) {
66
+ return;
67
+ }
68
+ let docs;
69
+ let paramsValues = [];
70
+ let queryValues = [];
71
+ let inputType = null;
72
+ let route = null;
73
+ let name = null;
74
+ state.channels.files.add(node.getSourceFile().fileName);
75
+ // Check if the first argument is an object literal
76
+ if (ts.isObjectLiteralExpression(firstArg)) {
77
+ const obj = firstArg;
78
+ name = getPropertyValue(obj, 'name');
79
+ route = getPropertyValue(obj, 'route');
80
+ if (!name) {
81
+ console.error('Channel name is required');
82
+ return;
83
+ }
84
+ if (route) {
85
+ const { keys } = pathToRegexp(route);
86
+ paramsValues = keys.reduce((result, { type, name }) => {
87
+ if (type === 'param') {
88
+ result.push(name);
89
+ }
90
+ return result;
91
+ }, []);
92
+ }
93
+ else {
94
+ route = '';
95
+ }
96
+ docs = getPropertyValue(obj, 'docs') || undefined;
97
+ queryValues = getPropertyValue(obj, 'query') || [];
98
+ const connect = !!getPropertyAssignment(obj, 'onConnect');
99
+ const disconnect = !!getPropertyAssignment(obj, 'onDisconnect');
100
+ const { inputs, outputs } = getFunctionTypes(checker, obj, {
101
+ funcName: 'onMessage',
102
+ inputIndex: 0,
103
+ outputIndex: 1,
104
+ typesMap: state.channels.typesMap,
105
+ });
106
+ const message = { inputs, outputs };
107
+ const messageRoutes = addMessagesRoutes(obj, checker, state.channels.typesMap);
108
+ state.channels.meta.push({
109
+ name,
110
+ route,
111
+ input: inputType,
112
+ params: paramsValues.length > 0 ? paramsValues : undefined,
113
+ query: queryValues.length > 0 ? queryValues : undefined,
114
+ inputTypes: getInputTypes(state.channels.metaInputTypes, 'get', inputType, queryValues, paramsValues),
115
+ connect,
116
+ disconnect,
117
+ message,
118
+ messageRoutes,
119
+ docs,
120
+ });
121
+ }
122
+ };
@@ -0,0 +1,3 @@
1
+ import * as ts from 'typescript';
2
+ import { PathToNameAndType } from './types.js';
3
+ export declare const addFileExtendsCoreType: (node: ts.Node, checker: ts.TypeChecker, methods: PathToNameAndType, expectedTypeName: string) => void;
@@ -0,0 +1,38 @@
1
+ import * as ts from 'typescript';
2
+ // const VRAMEWORK_TYPES = ['CoreConfig', 'CoreService', 'CoreServices', 'CoreSingletonService', 'CoreSessionService']
3
+ export const addFileExtendsCoreType = (node, checker, methods, expectedTypeName) => {
4
+ if (ts.isClassDeclaration(node) || ts.isInterfaceDeclaration(node)) {
5
+ const fileName = node.getSourceFile().fileName;
6
+ const typeName = node.name?.getText();
7
+ // Check if the class or interface extends the expected type
8
+ if (node.heritageClauses) {
9
+ for (const clause of node.heritageClauses) {
10
+ if (clause.token === ts.SyntaxKind.ExtendsKeyword) {
11
+ for (const type of clause.types) {
12
+ const extendedTypeName = type.expression.getText();
13
+ let extendedTypeDeclarationPath = null;
14
+ // Check if the extended type matches the expected type name
15
+ if (extendedTypeName === expectedTypeName) {
16
+ // Retrieve the symbol of the extended type
17
+ const typeSymbol = checker.getSymbolAtLocation(type.expression);
18
+ const declaration = typeSymbol &&
19
+ typeSymbol.declarations &&
20
+ typeSymbol.declarations[0];
21
+ if (declaration) {
22
+ const sourceFile = declaration.getSourceFile();
23
+ extendedTypeDeclarationPath = sourceFile.fileName; // Get the path of the file where the extended type was declared
24
+ }
25
+ const variables = methods[fileName] || [];
26
+ variables.push({
27
+ variable: undefined,
28
+ type: typeName,
29
+ typePath: extendedTypeDeclarationPath,
30
+ });
31
+ methods[fileName] = variables;
32
+ }
33
+ }
34
+ }
35
+ }
36
+ }
37
+ }
38
+ };
@@ -0,0 +1,3 @@
1
+ import * as ts from 'typescript';
2
+ import { PathToNameAndType } from './types.js';
3
+ export declare const addFileWithConfig: (node: ts.Node, checker: ts.TypeChecker, configs: PathToNameAndType) => void;
@@ -0,0 +1,31 @@
1
+ import * as ts from 'typescript';
2
+ import { doesTypeExtendsCore } from './does-type-extend-core-type.js';
3
+ export const addFileWithConfig = (node, checker, configs) => {
4
+ if (ts.isVariableDeclaration(node)) {
5
+ const fileName = node.getSourceFile().fileName;
6
+ const variableSymbol = checker.getSymbolAtLocation(node.name);
7
+ if (variableSymbol) {
8
+ const variableType = checker.getTypeOfSymbolAtLocation(variableSymbol, node.name);
9
+ const variableName = node.name.getText();
10
+ const variableTypeText = node.type?.getText();
11
+ // Check if the type extends CoreConfig
12
+ if (doesTypeExtendsCore(variableType, checker, new Set(), 'CoreConfig')) {
13
+ // Retrieve the symbol of the type (if it has one)
14
+ const typeSymbol = variableType.symbol;
15
+ let typeDeclarationPath = null;
16
+ const declaration = typeSymbol && typeSymbol.declarations && typeSymbol.declarations[0];
17
+ if (declaration) {
18
+ const sourceFile = declaration.getSourceFile();
19
+ typeDeclarationPath = sourceFile.fileName; // Get the path of the file where the type was declared
20
+ }
21
+ const variables = configs[fileName] || [];
22
+ variables.push({
23
+ variable: variableName,
24
+ type: variableTypeText || null,
25
+ typePath: typeDeclarationPath,
26
+ });
27
+ configs[fileName] = variables;
28
+ }
29
+ }
30
+ }
31
+ };
@@ -0,0 +1,3 @@
1
+ import * as ts from 'typescript';
2
+ import { PathToNameAndType } from './types.js';
3
+ export declare const addFileWithFactory: (node: ts.Node, checker: ts.TypeChecker, methods: PathToNameAndType | undefined, expectedTypeName: string) => void;
@@ -0,0 +1,48 @@
1
+ import * as ts from 'typescript';
2
+ export const addFileWithFactory = (node, checker, methods = new Map(), expectedTypeName) => {
3
+ if (ts.isVariableDeclaration(node)) {
4
+ const fileName = node.getSourceFile().fileName;
5
+ const variableTypeNode = node.type;
6
+ const variableName = node.name.getText();
7
+ if (variableTypeNode && ts.isTypeReferenceNode(variableTypeNode)) {
8
+ const typeNameNode = variableTypeNode.typeName || null;
9
+ let typeDeclarationPath = null;
10
+ // Check if the type name matches the expected type name
11
+ if (ts.isIdentifier(typeNameNode) &&
12
+ typeNameNode.text === expectedTypeName) {
13
+ const typeSymbol = checker.getSymbolAtLocation(typeNameNode);
14
+ const declaration = typeSymbol && typeSymbol.declarations && typeSymbol.declarations[0];
15
+ if (declaration) {
16
+ const sourceFile = declaration.getSourceFile();
17
+ typeDeclarationPath = sourceFile.fileName; // Get the path of the file where the type was declared
18
+ }
19
+ const variables = methods[fileName] || [];
20
+ variables.push({
21
+ variable: variableName,
22
+ type: typeNameNode.getText(),
23
+ typePath: typeDeclarationPath,
24
+ });
25
+ methods[fileName] = variables;
26
+ }
27
+ // Handle qualified type names if necessary
28
+ else if (ts.isQualifiedName(typeNameNode)) {
29
+ const lastName = typeNameNode.right.text;
30
+ if (lastName === expectedTypeName) {
31
+ const typeSymbol = checker.getSymbolAtLocation(typeNameNode.right);
32
+ const declaration = typeSymbol && typeSymbol.declarations && typeSymbol.declarations[0];
33
+ if (declaration) {
34
+ const sourceFile = declaration.getSourceFile();
35
+ typeDeclarationPath = sourceFile.fileName; // Get the path of the file where the type was declared
36
+ }
37
+ const variables = methods[fileName] || [];
38
+ variables.push({
39
+ variable: variableName,
40
+ type: typeNameNode.getText(),
41
+ typePath: typeDeclarationPath,
42
+ });
43
+ methods[fileName] = variables;
44
+ }
45
+ }
46
+ }
47
+ }
48
+ };
@@ -0,0 +1,4 @@
1
+ import * as ts from 'typescript';
2
+ import { MetaInputTypes, InspectorState } from './types.js';
3
+ export declare const getInputTypes: (metaTypes: MetaInputTypes, methodType: string, inputType: string | null, queryValues: string[], paramsValues: string[]) => undefined;
4
+ export declare const addRoute: (node: ts.Node, checker: ts.TypeChecker, state: InspectorState) => void;
@@ -0,0 +1,89 @@
1
+ import * as ts from 'typescript';
2
+ import { getPropertyValue } from './get-property-value.js';
3
+ import { pathToRegexp } from 'path-to-regexp';
4
+ import { extractTypeKeys, getFunctionTypes } from './utils.js';
5
+ export const getInputTypes = (metaTypes, methodType, inputType, queryValues, paramsValues) => {
6
+ if (!inputType) {
7
+ return undefined;
8
+ }
9
+ if (inputType) {
10
+ metaTypes.set(inputType, {
11
+ query: queryValues,
12
+ params: paramsValues,
13
+ body: ['post', 'put', 'patch'].includes(methodType)
14
+ ? [...new Set([...queryValues, ...paramsValues])]
15
+ : [],
16
+ });
17
+ }
18
+ return undefined;
19
+ };
20
+ export const addRoute = (node, checker, state) => {
21
+ if (!ts.isCallExpression(node)) {
22
+ return;
23
+ }
24
+ const args = node.arguments;
25
+ const firstArg = args[0];
26
+ const expression = node.expression;
27
+ // Check if the call is to addRoute
28
+ if (!ts.isIdentifier(expression) || expression.text !== 'addRoute') {
29
+ return;
30
+ }
31
+ if (!firstArg) {
32
+ return;
33
+ }
34
+ let docs;
35
+ let methodValue = null;
36
+ let paramsValues = [];
37
+ let queryValues = [];
38
+ let routeValue = null;
39
+ state.http.files.add(node.getSourceFile().fileName);
40
+ // Check if the first argument is an object literal
41
+ if (ts.isObjectLiteralExpression(firstArg)) {
42
+ const obj = firstArg;
43
+ routeValue = getPropertyValue(obj, 'route');
44
+ if (routeValue) {
45
+ const { keys } = pathToRegexp(routeValue);
46
+ paramsValues = keys.reduce((result, { type, name }) => {
47
+ if (type === 'param') {
48
+ result.push(name);
49
+ }
50
+ return result;
51
+ }, []);
52
+ }
53
+ docs = getPropertyValue(obj, 'docs') || undefined;
54
+ methodValue = getPropertyValue(obj, 'method');
55
+ queryValues = getPropertyValue(obj, 'query') || [];
56
+ let { inputs, outputs, inputTypes } = getFunctionTypes(checker, obj, {
57
+ funcName: 'func',
58
+ inputIndex: 0,
59
+ outputIndex: 1,
60
+ typesMap: state.http.typesMap,
61
+ });
62
+ const input = inputs ? inputs[0] || null : null;
63
+ const output = outputs ? outputs[0] || null : null;
64
+ if (inputs && inputs?.length > 1) {
65
+ console.error(`Only one input type is currently allowed for method '${methodValue}' and route '${routeValue}': \n\t${inputs.join('\n\t')}`);
66
+ }
67
+ if (outputs && outputs?.length > 1) {
68
+ console.error(`Only one output type is currently allowed for method '${methodValue}' and route '${routeValue}': \n\t${outputs.join('\n\t')}`);
69
+ }
70
+ if (inputTypes[0] && !['post', 'put', 'patch'].includes(methodValue)) {
71
+ queryValues = [
72
+ ...new Set([...queryValues, ...extractTypeKeys(inputTypes[0])]),
73
+ ].filter((query) => !paramsValues?.includes(query));
74
+ }
75
+ if (!routeValue) {
76
+ return;
77
+ }
78
+ state.http.meta.push({
79
+ route: routeValue,
80
+ method: methodValue,
81
+ input,
82
+ output,
83
+ params: paramsValues.length > 0 ? paramsValues : undefined,
84
+ query: queryValues.length > 0 ? queryValues : undefined,
85
+ inputTypes: getInputTypes(state.http.metaInputTypes, methodValue, input, queryValues, paramsValues),
86
+ docs,
87
+ });
88
+ }
89
+ };
@@ -0,0 +1,3 @@
1
+ import * as ts from 'typescript';
2
+ import { InspectorState } from './types.js';
3
+ export declare const addSchedule: (node: ts.Node, _checker: ts.TypeChecker, state: InspectorState) => void;
@@ -0,0 +1,32 @@
1
+ import * as ts from 'typescript';
2
+ import { getPropertyValue } from './get-property-value.js';
3
+ export const addSchedule = (node, _checker, state) => {
4
+ if (!ts.isCallExpression(node)) {
5
+ return;
6
+ }
7
+ const args = node.arguments;
8
+ const firstArg = args[0];
9
+ const expression = node.expression;
10
+ // Check if the call is to addScheduledTask
11
+ if (!ts.isIdentifier(expression) || expression.text !== 'addScheduledTask') {
12
+ return;
13
+ }
14
+ if (!firstArg) {
15
+ return;
16
+ }
17
+ state.scheduledTasks.files.add(node.getSourceFile().fileName);
18
+ if (ts.isObjectLiteralExpression(firstArg)) {
19
+ const obj = firstArg;
20
+ const nameValue = getPropertyValue(obj, 'name');
21
+ const scheduleValue = getPropertyValue(obj, 'schedule');
22
+ const docs = getPropertyValue(obj, 'docs') || undefined;
23
+ if (!nameValue || !scheduleValue) {
24
+ return;
25
+ }
26
+ state.scheduledTasks.meta.push({
27
+ name: nameValue,
28
+ schedule: scheduleValue,
29
+ docs,
30
+ });
31
+ }
32
+ };
@@ -0,0 +1,2 @@
1
+ import * as ts from 'typescript';
2
+ export declare const doesTypeExtendsCore: (type: ts.Type, checker: ts.TypeChecker, visitedTypes: Set<ts.Type>, coreType: string) => boolean;
@@ -0,0 +1,41 @@
1
+ export const doesTypeExtendsCore = (type, checker, visitedTypes, coreType) => {
2
+ if (!type || !checker)
3
+ return false;
4
+ // Avoid infinite recursion by checking if we've already visited this type
5
+ if (visitedTypes.has(type)) {
6
+ return false;
7
+ }
8
+ visitedTypes.add(type);
9
+ const typeSymbol = type.getSymbol();
10
+ if (typeSymbol) {
11
+ // Check if the type is the core type
12
+ if (typeSymbol.getName() === coreType) {
13
+ return true;
14
+ }
15
+ // For interface and class types, check their base types
16
+ if (type.isClassOrInterface()) {
17
+ const baseTypes = type.getBaseTypes() || [];
18
+ for (const baseType of baseTypes) {
19
+ if (doesTypeExtendsCore(baseType, checker, visitedTypes, coreType)) {
20
+ return true;
21
+ }
22
+ }
23
+ }
24
+ }
25
+ // For type aliases, get the aliased type
26
+ if (type.aliasSymbol) {
27
+ const aliasedType = checker.getDeclaredTypeOfSymbol(type.aliasSymbol);
28
+ if (doesTypeExtendsCore(aliasedType, checker, visitedTypes, coreType)) {
29
+ return true;
30
+ }
31
+ }
32
+ // For union and intersection types, check all constituent types
33
+ if (type.isUnionOrIntersection()) {
34
+ for (const subType of type.types) {
35
+ if (doesTypeExtendsCore(subType, checker, visitedTypes, coreType)) {
36
+ return true;
37
+ }
38
+ }
39
+ }
40
+ return false;
41
+ };
@@ -0,0 +1,3 @@
1
+ import { APIDocs } from '@pikku/core';
2
+ import * as ts from 'typescript';
3
+ export declare const getPropertyValue: (obj: ts.ObjectLiteralExpression, propertyName: string) => string | string[] | null | APIDocs;
@@ -0,0 +1,60 @@
1
+ import * as ts from 'typescript';
2
+ export const getPropertyValue = (obj, propertyName) => {
3
+ const property = obj.properties.find((p) => ts.isPropertyAssignment(p) &&
4
+ ts.isIdentifier(p.name) &&
5
+ p.name.text === propertyName);
6
+ if (property && ts.isPropertyAssignment(property)) {
7
+ const initializer = property.initializer;
8
+ // Special handling for 'query' -> expect an array of strings
9
+ if (propertyName === 'query' && ts.isArrayLiteralExpression(initializer)) {
10
+ const stringArray = initializer.elements
11
+ .map((element) => {
12
+ if (ts.isStringLiteral(element)) {
13
+ return element.text;
14
+ }
15
+ return null;
16
+ })
17
+ .filter((item) => item !== null); // Filter non-null and assert type
18
+ return stringArray.length > 0 ? stringArray : null;
19
+ }
20
+ // Special handling for 'docs' -> expect RouteDocs
21
+ if (propertyName === 'docs' && ts.isObjectLiteralExpression(initializer)) {
22
+ const docs = {};
23
+ initializer.properties.forEach((prop) => {
24
+ if (ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name)) {
25
+ const propName = prop.name.text;
26
+ if (propName === 'summary' && ts.isStringLiteral(prop.initializer)) {
27
+ docs.summary = prop.initializer.text;
28
+ }
29
+ else if (propName === 'description' &&
30
+ ts.isStringLiteral(prop.initializer)) {
31
+ docs.description = prop.initializer.text;
32
+ }
33
+ else if (propName === 'tags' &&
34
+ ts.isArrayLiteralExpression(prop.initializer)) {
35
+ docs.tags = prop.initializer.elements
36
+ .filter(ts.isStringLiteral)
37
+ .map((element) => element.text);
38
+ }
39
+ else if (propName === 'errors' &&
40
+ ts.isArrayLiteralExpression(prop.initializer)) {
41
+ docs.errors = prop.initializer.elements
42
+ .filter(ts.isIdentifier)
43
+ .map((element) => element.text);
44
+ }
45
+ }
46
+ });
47
+ return docs;
48
+ }
49
+ // Handle string literals for other properties
50
+ if (ts.isStringLiteral(initializer) ||
51
+ ts.isNoSubstitutionTemplateLiteral(initializer)) {
52
+ return initializer.text;
53
+ }
54
+ else {
55
+ // Handle other initializer types if necessary
56
+ return initializer.getText();
57
+ }
58
+ }
59
+ return null;
60
+ };
@@ -0,0 +1,4 @@
1
+ export { inspect } from './inspector.js';
2
+ export type { TypesMap } from './types-map.js';
3
+ export type * from './types.js';
4
+ export type { InspectorState } from './types.js';
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ export { inspect } from './inspector.js';
@@ -0,0 +1,3 @@
1
+ import { InspectorState, InspectorHTTPState } from './types.js';
2
+ export declare const normalizeHTTPTypes: (httpState: InspectorHTTPState) => InspectorHTTPState;
3
+ export declare const inspect: (routeFiles: string[]) => InspectorState;
@@ -0,0 +1,43 @@
1
+ import * as ts from 'typescript';
2
+ import { visit } from './visit.js';
3
+ import { TypesMap } from './types-map.js';
4
+ export const normalizeHTTPTypes = (httpState) => {
5
+ return httpState;
6
+ };
7
+ export const inspect = (routeFiles) => {
8
+ const program = ts.createProgram(routeFiles, {
9
+ target: ts.ScriptTarget.ESNext,
10
+ module: ts.ModuleKind.CommonJS,
11
+ });
12
+ const checker = program.getTypeChecker();
13
+ const sourceFiles = program.getSourceFiles();
14
+ const state = {
15
+ sessionServicesTypeImportMap: new Map(),
16
+ userSessionTypeImportMap: new Map(),
17
+ singletonServicesFactories: new Map(),
18
+ sessionServicesFactories: new Map(),
19
+ configFactories: new Map(),
20
+ http: {
21
+ typesMap: new TypesMap(),
22
+ metaInputTypes: new Map(),
23
+ meta: [],
24
+ files: new Set(),
25
+ },
26
+ channels: {
27
+ typesMap: new TypesMap(),
28
+ metaInputTypes: new Map(),
29
+ files: new Set(),
30
+ meta: [],
31
+ },
32
+ scheduledTasks: {
33
+ meta: [],
34
+ files: new Set(),
35
+ },
36
+ };
37
+ for (const sourceFile of sourceFiles) {
38
+ ts.forEachChild(sourceFile, (child) => visit(checker, child, state));
39
+ }
40
+ // Normalise the typesMap
41
+ state.http = normalizeHTTPTypes(state.http);
42
+ return state;
43
+ };
@@ -0,0 +1,18 @@
1
+ export declare class TypesMap {
2
+ private map;
3
+ customTypes: Map<string, {
4
+ type: string;
5
+ references: string[];
6
+ }>;
7
+ addCustomType(name: string, type: string, references: string[]): void;
8
+ addType(originalName: string, path: string): void;
9
+ addUniqueType(originalName: string, path: string): string;
10
+ getUniqueName(name: string): string;
11
+ getTypeMeta(name: string): {
12
+ originalName: string;
13
+ uniqueName: string;
14
+ path: string | null;
15
+ };
16
+ exists(originalName: string, path: string): string | undefined;
17
+ private squash;
18
+ }