@pikku/inspector 0.6.3 → 0.7.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.
- package/CHANGELOG.md +18 -0
- package/dist/add-channel.d.ts +12 -2
- package/dist/add-channel.js +336 -108
- package/dist/add-functions.d.ts +7 -0
- package/dist/add-functions.js +260 -0
- package/dist/add-http-route.d.ts +15 -3
- package/dist/add-http-route.js +69 -80
- package/dist/add-schedule.d.ts +1 -1
- package/dist/add-schedule.js +14 -4
- package/dist/events/add-channel.d.ts +1 -0
- package/dist/events/add-channel.js +170 -0
- package/dist/events/add-http-route.d.ts +16 -0
- package/dist/events/add-http-route.js +83 -0
- package/dist/events/add-schedule.d.ts +3 -0
- package/dist/events/add-schedule.js +38 -0
- package/dist/inspector.js +14 -4
- package/dist/types.d.ts +7 -10
- package/dist/utils.d.ts +21 -26
- package/dist/utils.js +635 -213
- package/dist/visit.d.ts +2 -1
- package/dist/visit.js +9 -4
- package/package.json +2 -2
- package/src/add-channel.ts +0 -179
- package/src/add-file-extends-core-type.ts +0 -50
- package/src/add-file-with-config.ts +0 -45
- package/src/add-file-with-factory.ts +0 -65
- package/src/add-http-route.ts +0 -138
- package/src/add-schedule.ts +0 -56
- package/src/does-type-extend-core-type.ts +0 -53
- package/src/get-property-value.ts +0 -84
- package/src/index.ts +0 -4
- package/src/inspector.ts +0 -63
- package/src/types-map.ts +0 -130
- package/src/types.ts +0 -62
- package/src/utils.ts +0 -367
- package/src/visit.ts +0 -57
- package/tsconfig.json +0 -19
- package/tsconfig.tsbuildinfo +0 -1
package/dist/visit.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import * as ts from 'typescript';
|
|
2
2
|
import { InspectorFilters, InspectorState } from './types.js';
|
|
3
|
-
export declare const
|
|
3
|
+
export declare const visitSetup: (checker: ts.TypeChecker, node: ts.Node, state: InspectorState, filters: InspectorFilters) => void;
|
|
4
|
+
export declare const visitRoutes: (checker: ts.TypeChecker, node: ts.Node, state: InspectorState, filters: InspectorFilters) => void;
|
package/dist/visit.js
CHANGED
|
@@ -1,18 +1,23 @@
|
|
|
1
1
|
import * as ts from 'typescript';
|
|
2
2
|
import { addFileWithFactory } from './add-file-with-factory.js';
|
|
3
3
|
import { addFileExtendsCoreType } from './add-file-extends-core-type.js';
|
|
4
|
-
import {
|
|
4
|
+
import { addHTTPRoute } from './add-http-route.js';
|
|
5
5
|
import { addSchedule } from './add-schedule.js';
|
|
6
|
+
import { addFunctions } from './add-functions.js';
|
|
6
7
|
import { addChannel } from './add-channel.js';
|
|
7
|
-
export const
|
|
8
|
+
export const visitSetup = (checker, node, state, filters) => {
|
|
8
9
|
addFileExtendsCoreType(node, checker, state.singletonServicesTypeImportMap, 'CoreSingletonServices');
|
|
9
10
|
addFileExtendsCoreType(node, checker, state.sessionServicesTypeImportMap, 'CoreServices');
|
|
10
11
|
addFileExtendsCoreType(node, checker, state.userSessionTypeImportMap, 'CoreUserSession');
|
|
11
12
|
addFileWithFactory(node, checker, state.singletonServicesFactories, 'CreateSingletonServices');
|
|
12
13
|
addFileWithFactory(node, checker, state.sessionServicesFactories, 'CreateSessionServices');
|
|
13
14
|
addFileWithFactory(node, checker, state.configFactories, 'CreateConfig');
|
|
14
|
-
|
|
15
|
+
addFunctions(node, checker, state, filters);
|
|
16
|
+
ts.forEachChild(node, (child) => visitSetup(checker, child, state, filters));
|
|
17
|
+
};
|
|
18
|
+
export const visitRoutes = (checker, node, state, filters) => {
|
|
19
|
+
addHTTPRoute(node, checker, state, filters);
|
|
15
20
|
addSchedule(node, checker, state, filters);
|
|
16
21
|
addChannel(node, checker, state, filters);
|
|
17
|
-
ts.forEachChild(node, (child) =>
|
|
22
|
+
ts.forEachChild(node, (child) => visitRoutes(checker, child, state, filters));
|
|
18
23
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pikku/inspector",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"author": "yasser.fadl@gmail.com",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"test:coverage": "bash run-tests.sh --coverage"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@pikku/core": "^0.
|
|
20
|
+
"@pikku/core": "^0.7.0",
|
|
21
21
|
"path-to-regexp": "^8.2.0",
|
|
22
22
|
"typescript": "^5.6"
|
|
23
23
|
},
|
package/src/add-channel.ts
DELETED
|
@@ -1,179 +0,0 @@
|
|
|
1
|
-
import * as ts from 'typescript'
|
|
2
|
-
import { getPropertyValue } from './get-property-value.js'
|
|
3
|
-
import { pathToRegexp } from 'path-to-regexp'
|
|
4
|
-
import { APIDocs } from '@pikku/core'
|
|
5
|
-
import { getInputTypes } from './add-http-route.js'
|
|
6
|
-
import {
|
|
7
|
-
getPropertyAssignment,
|
|
8
|
-
getFunctionTypes,
|
|
9
|
-
matchesFilters,
|
|
10
|
-
} from './utils.js'
|
|
11
|
-
import { ChannelMeta } from '@pikku/core/channel'
|
|
12
|
-
import { TypesMap } from './types-map.js'
|
|
13
|
-
import { InspectorFilters, InspectorState } from './types.js'
|
|
14
|
-
|
|
15
|
-
const addMessagesRoutes = (
|
|
16
|
-
obj: ts.ObjectLiteralExpression,
|
|
17
|
-
checker: ts.TypeChecker,
|
|
18
|
-
typesMap: TypesMap
|
|
19
|
-
) => {
|
|
20
|
-
const messageTypes: ChannelMeta['messageRoutes'] = {}
|
|
21
|
-
|
|
22
|
-
// Find the onMessageRoute property
|
|
23
|
-
const messagesProperty = obj.properties.find(
|
|
24
|
-
(p) =>
|
|
25
|
-
ts.isPropertyAssignment(p) &&
|
|
26
|
-
ts.isIdentifier(p.name) &&
|
|
27
|
-
p.name.text === 'onMessageRoute'
|
|
28
|
-
)
|
|
29
|
-
|
|
30
|
-
if (!messagesProperty || !ts.isPropertyAssignment(messagesProperty)) {
|
|
31
|
-
console.log(
|
|
32
|
-
'onMessageRoute property not found or is not a valid assignment.'
|
|
33
|
-
)
|
|
34
|
-
return {}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const initializer = messagesProperty.initializer
|
|
38
|
-
// Ensure initializer is an object literal expression
|
|
39
|
-
if (!ts.isObjectLiteralExpression(initializer)) {
|
|
40
|
-
console.log('onMessageRoute is not an object literal.')
|
|
41
|
-
return {}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// Iterate over the first level properties (like 'event')
|
|
45
|
-
initializer.properties.forEach((property) => {
|
|
46
|
-
const channel = property.name!.getText()
|
|
47
|
-
messageTypes[channel] = {}
|
|
48
|
-
|
|
49
|
-
if (ts.isPropertyAssignment(property)) {
|
|
50
|
-
const nestedObject = property.initializer
|
|
51
|
-
if (ts.isObjectLiteralExpression(nestedObject)) {
|
|
52
|
-
const keys = nestedObject.properties.map((p) => p.name?.getText())
|
|
53
|
-
for (const route of keys) {
|
|
54
|
-
if (route) {
|
|
55
|
-
const result = getFunctionTypes(checker, nestedObject, {
|
|
56
|
-
funcName: route,
|
|
57
|
-
inputIndex: 0,
|
|
58
|
-
outputIndex: 1,
|
|
59
|
-
typesMap,
|
|
60
|
-
})
|
|
61
|
-
const inputs = result?.inputs || null
|
|
62
|
-
const outputs = result?.outputs || null
|
|
63
|
-
messageTypes[channel][route] = { inputs, outputs }
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
} else {
|
|
67
|
-
console.warn('Nested property is not an object literal:', nestedObject)
|
|
68
|
-
}
|
|
69
|
-
} else {
|
|
70
|
-
console.warn(
|
|
71
|
-
`Property "${property.getText()}" is a ${ts.SyntaxKind[property.kind]}`
|
|
72
|
-
)
|
|
73
|
-
}
|
|
74
|
-
})
|
|
75
|
-
|
|
76
|
-
return messageTypes
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
export const addChannel = (
|
|
80
|
-
node: ts.Node,
|
|
81
|
-
checker: ts.TypeChecker,
|
|
82
|
-
state: InspectorState,
|
|
83
|
-
filters: InspectorFilters
|
|
84
|
-
) => {
|
|
85
|
-
if (!ts.isCallExpression(node)) {
|
|
86
|
-
return
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
const args = node.arguments
|
|
90
|
-
const firstArg = args[0]
|
|
91
|
-
const expression = node.expression
|
|
92
|
-
|
|
93
|
-
// Check if the call is to addRoute
|
|
94
|
-
if (!ts.isIdentifier(expression) || expression.text !== 'addChannel') {
|
|
95
|
-
return
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
if (!firstArg) {
|
|
99
|
-
return
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
let docs: APIDocs | undefined
|
|
103
|
-
let paramsValues: string[] | null = []
|
|
104
|
-
let queryValues: string[] | [] = []
|
|
105
|
-
let tags: string[] | undefined = undefined
|
|
106
|
-
let inputType: string | null = null
|
|
107
|
-
let route: string | null = null
|
|
108
|
-
let name: string | null = null
|
|
109
|
-
|
|
110
|
-
// Check if the first argument is an object literal
|
|
111
|
-
if (ts.isObjectLiteralExpression(firstArg)) {
|
|
112
|
-
const obj = firstArg
|
|
113
|
-
|
|
114
|
-
name = getPropertyValue(obj, 'name') as string | null
|
|
115
|
-
route = getPropertyValue(obj, 'route') as string | null
|
|
116
|
-
|
|
117
|
-
if (!name) {
|
|
118
|
-
console.error('Channel name is required')
|
|
119
|
-
return
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
if (route) {
|
|
123
|
-
const { keys } = pathToRegexp(route)
|
|
124
|
-
paramsValues = keys.reduce((result, { type, name }) => {
|
|
125
|
-
if (type === 'param') {
|
|
126
|
-
result.push(name)
|
|
127
|
-
}
|
|
128
|
-
return result
|
|
129
|
-
}, [] as string[])
|
|
130
|
-
} else {
|
|
131
|
-
route = ''
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
docs = (getPropertyValue(obj, 'docs') as APIDocs) || undefined
|
|
135
|
-
queryValues = (getPropertyValue(obj, 'query') as string[]) || []
|
|
136
|
-
tags = (getPropertyValue(obj, 'tags') as string[]) || undefined
|
|
137
|
-
|
|
138
|
-
const connect = !!getPropertyAssignment(obj, 'onConnect')
|
|
139
|
-
const disconnect = !!getPropertyAssignment(obj, 'onDisconnect')
|
|
140
|
-
const { inputs, outputs } = getFunctionTypes(checker, obj, {
|
|
141
|
-
funcName: 'onMessage',
|
|
142
|
-
inputIndex: 0,
|
|
143
|
-
outputIndex: 1,
|
|
144
|
-
typesMap: state.channels.typesMap,
|
|
145
|
-
})
|
|
146
|
-
const message = { inputs, outputs }
|
|
147
|
-
const messageRoutes = addMessagesRoutes(
|
|
148
|
-
obj,
|
|
149
|
-
checker,
|
|
150
|
-
state.channels.typesMap
|
|
151
|
-
)
|
|
152
|
-
|
|
153
|
-
if (!matchesFilters(filters, { tags }, { type: 'channel', name })) {
|
|
154
|
-
return
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
state.channels.files.add(node.getSourceFile().fileName)
|
|
158
|
-
state.channels.meta.push({
|
|
159
|
-
name,
|
|
160
|
-
route,
|
|
161
|
-
input: inputType,
|
|
162
|
-
params: paramsValues.length > 0 ? paramsValues : undefined,
|
|
163
|
-
query: queryValues.length > 0 ? queryValues : undefined,
|
|
164
|
-
inputTypes: getInputTypes(
|
|
165
|
-
state.channels.metaInputTypes,
|
|
166
|
-
'get',
|
|
167
|
-
inputType,
|
|
168
|
-
queryValues,
|
|
169
|
-
paramsValues
|
|
170
|
-
),
|
|
171
|
-
connect,
|
|
172
|
-
disconnect,
|
|
173
|
-
message,
|
|
174
|
-
messageRoutes,
|
|
175
|
-
docs,
|
|
176
|
-
tags,
|
|
177
|
-
})
|
|
178
|
-
}
|
|
179
|
-
}
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import * as ts from 'typescript'
|
|
2
|
-
import { PathToNameAndType } from './types.js'
|
|
3
|
-
|
|
4
|
-
// const VRAMEWORK_TYPES = ['CoreConfig', 'CoreService', 'CoreServices', 'CoreSingletonService', 'CoreSessionService']
|
|
5
|
-
|
|
6
|
-
export const addFileExtendsCoreType = (
|
|
7
|
-
node: ts.Node,
|
|
8
|
-
checker: ts.TypeChecker,
|
|
9
|
-
methods: PathToNameAndType,
|
|
10
|
-
expectedTypeName: string
|
|
11
|
-
) => {
|
|
12
|
-
if (ts.isClassDeclaration(node) || ts.isInterfaceDeclaration(node)) {
|
|
13
|
-
const fileName = node.getSourceFile().fileName
|
|
14
|
-
const typeName = node.name?.getText()
|
|
15
|
-
|
|
16
|
-
// Check if the class or interface extends the expected type
|
|
17
|
-
if (node.heritageClauses) {
|
|
18
|
-
for (const clause of node.heritageClauses) {
|
|
19
|
-
if (clause.token === ts.SyntaxKind.ExtendsKeyword) {
|
|
20
|
-
for (const type of clause.types) {
|
|
21
|
-
const extendedTypeName = type.expression.getText()
|
|
22
|
-
let extendedTypeDeclarationPath: string | null = null
|
|
23
|
-
|
|
24
|
-
// Check if the extended type matches the expected type name
|
|
25
|
-
if (extendedTypeName === expectedTypeName) {
|
|
26
|
-
// Retrieve the symbol of the extended type
|
|
27
|
-
const typeSymbol = checker.getSymbolAtLocation(type.expression)
|
|
28
|
-
const declaration =
|
|
29
|
-
typeSymbol &&
|
|
30
|
-
typeSymbol.declarations &&
|
|
31
|
-
typeSymbol.declarations[0]
|
|
32
|
-
if (declaration) {
|
|
33
|
-
const sourceFile = declaration.getSourceFile()
|
|
34
|
-
extendedTypeDeclarationPath = sourceFile.fileName // Get the path of the file where the extended type was declared
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const variables = methods[fileName] || []
|
|
38
|
-
variables.push({
|
|
39
|
-
variable: undefined,
|
|
40
|
-
type: typeName,
|
|
41
|
-
typePath: extendedTypeDeclarationPath,
|
|
42
|
-
})
|
|
43
|
-
methods[fileName] = variables
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
}
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import * as ts from 'typescript'
|
|
2
|
-
import { doesTypeExtendsCore } from './does-type-extend-core-type.js'
|
|
3
|
-
import { PathToNameAndType } from './types.js'
|
|
4
|
-
|
|
5
|
-
export const addFileWithConfig = (
|
|
6
|
-
node: ts.Node,
|
|
7
|
-
checker: ts.TypeChecker,
|
|
8
|
-
configs: PathToNameAndType
|
|
9
|
-
) => {
|
|
10
|
-
if (ts.isVariableDeclaration(node)) {
|
|
11
|
-
const fileName = node.getSourceFile().fileName
|
|
12
|
-
const variableSymbol = checker.getSymbolAtLocation(node.name)
|
|
13
|
-
|
|
14
|
-
if (variableSymbol) {
|
|
15
|
-
const variableType = checker.getTypeOfSymbolAtLocation(
|
|
16
|
-
variableSymbol,
|
|
17
|
-
node.name
|
|
18
|
-
)
|
|
19
|
-
const variableName = node.name.getText()
|
|
20
|
-
const variableTypeText = node.type?.getText()
|
|
21
|
-
|
|
22
|
-
// Check if the type extends CoreConfig
|
|
23
|
-
if (doesTypeExtendsCore(variableType, checker, new Set(), 'CoreConfig')) {
|
|
24
|
-
// Retrieve the symbol of the type (if it has one)
|
|
25
|
-
const typeSymbol = variableType.symbol
|
|
26
|
-
let typeDeclarationPath: string | null = null
|
|
27
|
-
const declaration =
|
|
28
|
-
typeSymbol && typeSymbol.declarations && typeSymbol.declarations[0]
|
|
29
|
-
|
|
30
|
-
if (declaration) {
|
|
31
|
-
const sourceFile = declaration.getSourceFile()
|
|
32
|
-
typeDeclarationPath = sourceFile.fileName // Get the path of the file where the type was declared
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const variables = configs[fileName] || []
|
|
36
|
-
variables.push({
|
|
37
|
-
variable: variableName,
|
|
38
|
-
type: variableTypeText || null,
|
|
39
|
-
typePath: typeDeclarationPath,
|
|
40
|
-
})
|
|
41
|
-
configs[fileName] = variables
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
}
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import * as ts from 'typescript'
|
|
2
|
-
import { PathToNameAndType } from './types.js'
|
|
3
|
-
|
|
4
|
-
export const addFileWithFactory = (
|
|
5
|
-
node: ts.Node,
|
|
6
|
-
checker: ts.TypeChecker,
|
|
7
|
-
methods: PathToNameAndType = new Map(),
|
|
8
|
-
expectedTypeName: string
|
|
9
|
-
) => {
|
|
10
|
-
if (ts.isVariableDeclaration(node)) {
|
|
11
|
-
const fileName = node.getSourceFile().fileName
|
|
12
|
-
const variableTypeNode = node.type
|
|
13
|
-
const variableName = node.name.getText()
|
|
14
|
-
|
|
15
|
-
if (variableTypeNode && ts.isTypeReferenceNode(variableTypeNode)) {
|
|
16
|
-
const typeNameNode = variableTypeNode.typeName || null
|
|
17
|
-
|
|
18
|
-
let typeDeclarationPath: string | null = null
|
|
19
|
-
|
|
20
|
-
// Check if the type name matches the expected type name
|
|
21
|
-
if (
|
|
22
|
-
ts.isIdentifier(typeNameNode) &&
|
|
23
|
-
typeNameNode.text === expectedTypeName
|
|
24
|
-
) {
|
|
25
|
-
const typeSymbol = checker.getSymbolAtLocation(typeNameNode)
|
|
26
|
-
const declaration =
|
|
27
|
-
typeSymbol && typeSymbol.declarations && typeSymbol.declarations[0]
|
|
28
|
-
if (declaration) {
|
|
29
|
-
const sourceFile = declaration.getSourceFile()
|
|
30
|
-
typeDeclarationPath = sourceFile.fileName // Get the path of the file where the type was declared
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const variables = methods[fileName] || []
|
|
34
|
-
variables.push({
|
|
35
|
-
variable: variableName,
|
|
36
|
-
type: typeNameNode.getText(),
|
|
37
|
-
typePath: typeDeclarationPath,
|
|
38
|
-
})
|
|
39
|
-
methods[fileName] = variables
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// Handle qualified type names if necessary
|
|
43
|
-
else if (ts.isQualifiedName(typeNameNode)) {
|
|
44
|
-
const lastName = typeNameNode.right.text
|
|
45
|
-
if (lastName === expectedTypeName) {
|
|
46
|
-
const typeSymbol = checker.getSymbolAtLocation(typeNameNode.right)
|
|
47
|
-
const declaration =
|
|
48
|
-
typeSymbol && typeSymbol.declarations && typeSymbol.declarations[0]
|
|
49
|
-
if (declaration) {
|
|
50
|
-
const sourceFile = declaration.getSourceFile()
|
|
51
|
-
typeDeclarationPath = sourceFile.fileName // Get the path of the file where the type was declared
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const variables = methods[fileName] || []
|
|
55
|
-
variables.push({
|
|
56
|
-
variable: variableName,
|
|
57
|
-
type: typeNameNode.getText(),
|
|
58
|
-
typePath: typeDeclarationPath,
|
|
59
|
-
})
|
|
60
|
-
methods[fileName] = variables
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
}
|
package/src/add-http-route.ts
DELETED
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
import * as ts from 'typescript'
|
|
2
|
-
import { getPropertyValue } from './get-property-value.js'
|
|
3
|
-
import { pathToRegexp } from 'path-to-regexp'
|
|
4
|
-
import { HTTPMethod } from '@pikku/core/http'
|
|
5
|
-
import { APIDocs } from '@pikku/core'
|
|
6
|
-
import { extractTypeKeys, getFunctionTypes, matchesFilters } from './utils.js'
|
|
7
|
-
import { MetaInputTypes, InspectorState, InspectorFilters } from './types.js'
|
|
8
|
-
|
|
9
|
-
export const getInputTypes = (
|
|
10
|
-
metaTypes: MetaInputTypes,
|
|
11
|
-
methodType: string,
|
|
12
|
-
inputType: string | null,
|
|
13
|
-
queryValues: string[],
|
|
14
|
-
paramsValues: string[]
|
|
15
|
-
) => {
|
|
16
|
-
if (!inputType) {
|
|
17
|
-
return undefined
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
if (inputType) {
|
|
21
|
-
metaTypes.set(inputType, {
|
|
22
|
-
query: queryValues,
|
|
23
|
-
params: paramsValues,
|
|
24
|
-
body: ['post', 'put', 'patch'].includes(methodType)
|
|
25
|
-
? [...new Set([...queryValues, ...paramsValues])]
|
|
26
|
-
: [],
|
|
27
|
-
})
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
return undefined
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export const addRoute = (
|
|
34
|
-
node: ts.Node,
|
|
35
|
-
checker: ts.TypeChecker,
|
|
36
|
-
state: InspectorState,
|
|
37
|
-
filters: InspectorFilters
|
|
38
|
-
) => {
|
|
39
|
-
if (!ts.isCallExpression(node)) {
|
|
40
|
-
return
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const args = node.arguments
|
|
44
|
-
const firstArg = args[0]
|
|
45
|
-
const expression = node.expression
|
|
46
|
-
|
|
47
|
-
// Check if the call is to addRoute
|
|
48
|
-
if (!ts.isIdentifier(expression) || expression.text !== 'addRoute') {
|
|
49
|
-
return
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
if (!firstArg) {
|
|
53
|
-
return
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
let docs: APIDocs | undefined
|
|
57
|
-
let methodValue: string | null = null
|
|
58
|
-
let paramsValues: string[] | null = []
|
|
59
|
-
let queryValues: string[] | [] = []
|
|
60
|
-
let tags: string[] | [] = []
|
|
61
|
-
let routeValue: string | null = null
|
|
62
|
-
|
|
63
|
-
// Check if the first argument is an object literal
|
|
64
|
-
if (ts.isObjectLiteralExpression(firstArg)) {
|
|
65
|
-
const obj = firstArg
|
|
66
|
-
|
|
67
|
-
routeValue = getPropertyValue(obj, 'route') as string | null
|
|
68
|
-
if (!routeValue) {
|
|
69
|
-
return
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const { keys } = pathToRegexp(routeValue)
|
|
73
|
-
paramsValues = keys.reduce((result, { type, name }) => {
|
|
74
|
-
if (type === 'param') {
|
|
75
|
-
result.push(name)
|
|
76
|
-
}
|
|
77
|
-
return result
|
|
78
|
-
}, [] as string[])
|
|
79
|
-
|
|
80
|
-
docs = (getPropertyValue(obj, 'docs') as APIDocs) || undefined
|
|
81
|
-
methodValue = getPropertyValue(obj, 'method') as string
|
|
82
|
-
queryValues = (getPropertyValue(obj, 'query') as string[]) || []
|
|
83
|
-
tags = (getPropertyValue(obj, 'tags') as string[]) || undefined
|
|
84
|
-
|
|
85
|
-
if (
|
|
86
|
-
!matchesFilters(filters, { tags }, { type: 'http', name: routeValue })
|
|
87
|
-
) {
|
|
88
|
-
return
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
let { inputs, outputs, inputTypes } = getFunctionTypes(checker, obj, {
|
|
92
|
-
funcName: 'func',
|
|
93
|
-
inputIndex: 0,
|
|
94
|
-
outputIndex: 1,
|
|
95
|
-
typesMap: state.http.typesMap,
|
|
96
|
-
})
|
|
97
|
-
|
|
98
|
-
const input = inputs ? inputs[0] || null : null
|
|
99
|
-
const output = outputs ? outputs[0] || null : null
|
|
100
|
-
|
|
101
|
-
if (inputs && inputs?.length > 1) {
|
|
102
|
-
console.error(
|
|
103
|
-
`Only one input type is currently allowed for method '${methodValue}' and route '${routeValue}': \n\t${inputs.join('\n\t')}`
|
|
104
|
-
)
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
if (outputs && outputs?.length > 1) {
|
|
108
|
-
console.error(
|
|
109
|
-
`Only one output type is currently allowed for method '${methodValue}' and route '${routeValue}': \n\t${outputs.join('\n\t')}`
|
|
110
|
-
)
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
if (inputTypes[0] && !['post', 'put', 'patch'].includes(methodValue)) {
|
|
114
|
-
queryValues = [
|
|
115
|
-
...new Set([...queryValues, ...extractTypeKeys(inputTypes[0])]),
|
|
116
|
-
].filter((query) => !paramsValues?.includes(query))
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
state.http.files.add(node.getSourceFile().fileName)
|
|
120
|
-
state.http.meta.push({
|
|
121
|
-
route: routeValue,
|
|
122
|
-
method: methodValue! as HTTPMethod,
|
|
123
|
-
input,
|
|
124
|
-
output,
|
|
125
|
-
params: paramsValues.length > 0 ? paramsValues : undefined,
|
|
126
|
-
query: queryValues.length > 0 ? queryValues : undefined,
|
|
127
|
-
inputTypes: getInputTypes(
|
|
128
|
-
state.http.metaInputTypes,
|
|
129
|
-
methodValue,
|
|
130
|
-
input,
|
|
131
|
-
queryValues,
|
|
132
|
-
paramsValues
|
|
133
|
-
),
|
|
134
|
-
docs,
|
|
135
|
-
tags,
|
|
136
|
-
})
|
|
137
|
-
}
|
|
138
|
-
}
|
package/src/add-schedule.ts
DELETED
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import * as ts from 'typescript'
|
|
2
|
-
import { getPropertyValue } from './get-property-value.js'
|
|
3
|
-
import { APIDocs } from '@pikku/core'
|
|
4
|
-
import { InspectorFilters, InspectorState } from './types.js'
|
|
5
|
-
import { matchesFilters } from './utils.js'
|
|
6
|
-
|
|
7
|
-
export const addSchedule = (
|
|
8
|
-
node: ts.Node,
|
|
9
|
-
_checker: ts.TypeChecker,
|
|
10
|
-
state: InspectorState,
|
|
11
|
-
filters: InspectorFilters
|
|
12
|
-
) => {
|
|
13
|
-
if (!ts.isCallExpression(node)) {
|
|
14
|
-
return
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const args = node.arguments
|
|
18
|
-
const firstArg = args[0]
|
|
19
|
-
const expression = node.expression
|
|
20
|
-
|
|
21
|
-
// Check if the call is to addScheduledTask
|
|
22
|
-
if (!ts.isIdentifier(expression) || expression.text !== 'addScheduledTask') {
|
|
23
|
-
return
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
if (!firstArg) {
|
|
27
|
-
return
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
if (ts.isObjectLiteralExpression(firstArg)) {
|
|
31
|
-
const obj = firstArg
|
|
32
|
-
|
|
33
|
-
const nameValue = getPropertyValue(obj, 'name') as string | null
|
|
34
|
-
const scheduleValue = getPropertyValue(obj, 'schedule') as string | null
|
|
35
|
-
const docs = (getPropertyValue(obj, 'docs') as APIDocs) || undefined
|
|
36
|
-
const tags = (getPropertyValue(obj, 'tags') as string[]) || undefined
|
|
37
|
-
|
|
38
|
-
if (!nameValue || !scheduleValue) {
|
|
39
|
-
return
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
if (
|
|
43
|
-
!matchesFilters(filters, { tags }, { type: 'schedule', name: nameValue })
|
|
44
|
-
) {
|
|
45
|
-
return
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
state.scheduledTasks.files.add(node.getSourceFile().fileName)
|
|
49
|
-
state.scheduledTasks.meta.push({
|
|
50
|
-
name: nameValue,
|
|
51
|
-
schedule: scheduleValue,
|
|
52
|
-
docs,
|
|
53
|
-
tags,
|
|
54
|
-
})
|
|
55
|
-
}
|
|
56
|
-
}
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import * as ts from 'typescript'
|
|
2
|
-
|
|
3
|
-
export const doesTypeExtendsCore = (
|
|
4
|
-
type: ts.Type,
|
|
5
|
-
checker: ts.TypeChecker,
|
|
6
|
-
visitedTypes: Set<ts.Type>,
|
|
7
|
-
coreType: string
|
|
8
|
-
): boolean => {
|
|
9
|
-
if (!type || !checker) return false
|
|
10
|
-
|
|
11
|
-
// Avoid infinite recursion by checking if we've already visited this type
|
|
12
|
-
if (visitedTypes.has(type)) {
|
|
13
|
-
return false
|
|
14
|
-
}
|
|
15
|
-
visitedTypes.add(type)
|
|
16
|
-
|
|
17
|
-
const typeSymbol = type.getSymbol()
|
|
18
|
-
if (typeSymbol) {
|
|
19
|
-
// Check if the type is the core type
|
|
20
|
-
if (typeSymbol.getName() === coreType) {
|
|
21
|
-
return true
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
// For interface and class types, check their base types
|
|
25
|
-
if (type.isClassOrInterface()) {
|
|
26
|
-
const baseTypes = type.getBaseTypes() || []
|
|
27
|
-
for (const baseType of baseTypes) {
|
|
28
|
-
if (doesTypeExtendsCore(baseType, checker, visitedTypes, coreType)) {
|
|
29
|
-
return true
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// For type aliases, get the aliased type
|
|
36
|
-
if (type.aliasSymbol) {
|
|
37
|
-
const aliasedType = checker.getDeclaredTypeOfSymbol(type.aliasSymbol)
|
|
38
|
-
if (doesTypeExtendsCore(aliasedType, checker, visitedTypes, coreType)) {
|
|
39
|
-
return true
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// For union and intersection types, check all constituent types
|
|
44
|
-
if (type.isUnionOrIntersection()) {
|
|
45
|
-
for (const subType of type.types) {
|
|
46
|
-
if (doesTypeExtendsCore(subType, checker, visitedTypes, coreType)) {
|
|
47
|
-
return true
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
return false
|
|
53
|
-
}
|