mad-data-parser 0.0.1 → 0.0.2-beta.12
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/.vscode/launch.json +40 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/mad-data-parser-0.0.2-beta.12.tgz +0 -0
- package/dist/package.json +28 -0
- package/dist/parser/parseJsonPaths.d.ts +11 -3
- package/dist/parser/parseJsonPaths.d.ts.map +1 -1
- package/dist/parser/parseJsonPaths.js +127 -68
- package/dist/parser/parseJsonPaths.js.map +1 -1
- package/dist/utils/graphql/index.d.ts +2 -2
- package/dist/utils/graphql/index.d.ts.map +1 -1
- package/dist/utils/graphql/index.js +2 -1
- package/dist/utils/graphql/index.js.map +1 -1
- package/dist/utils/graphql-tag/collocated/gql.d.ts +47 -0
- package/dist/utils/graphql-tag/collocated/gql.d.ts.map +1 -0
- package/dist/utils/graphql-tag/collocated/gql.js +133 -0
- package/dist/utils/graphql-tag/collocated/gql.js.map +1 -0
- package/dist/utils/graphql-tag/collocated/guards.d.ts +4 -0
- package/dist/utils/graphql-tag/collocated/guards.d.ts.map +1 -0
- package/dist/utils/graphql-tag/collocated/guards.js +13 -0
- package/dist/utils/graphql-tag/collocated/guards.js.map +1 -0
- package/dist/utils/graphql-tag/collocated/stringifyDocument.d.ts +2 -0
- package/dist/utils/graphql-tag/collocated/stringifyDocument.d.ts.map +1 -0
- package/dist/utils/graphql-tag/collocated/stringifyDocument.js +38 -0
- package/dist/utils/graphql-tag/collocated/stringifyDocument.js.map +1 -0
- package/dist/utils/graphql-tag/collocated/types.d.ts +19 -0
- package/dist/utils/graphql-tag/collocated/types.d.ts.map +1 -0
- package/dist/utils/graphql-tag/collocated/types.js +2 -0
- package/dist/utils/graphql-tag/collocated/types.js.map +1 -0
- package/dist/utils/graphql-tag/index.d.ts +5 -0
- package/dist/utils/graphql-tag/index.d.ts.map +1 -0
- package/dist/utils/graphql-tag/index.js +5 -0
- package/dist/utils/graphql-tag/index.js.map +1 -0
- package/dist/utils/jsonPath/guards.d.ts +1 -1
- package/dist/utils/jsonPath/index.d.ts +3 -3
- package/dist/utils/jsonPath/index.js +3 -3
- package/dist/utils/jsonPath/visitor.d.ts +2 -2
- package/dist/utils/jsonPath/visitor.js +3 -3
- package/dist/version.txt +1 -0
- package/nodemon.json +1 -1
- package/package.json +24 -11
- package/var/cache/schema.graphql +35655 -0
- package/.swcrc +0 -27
- package/example/data/__tests__/output.ts +0 -57
- package/example/data/data.ts +0 -226
- package/example/data/output.ts +0 -74
- package/example/data/types.ts +0 -92
- package/example/example.ts +0 -26
- package/example/jsonPaths.data.ts +0 -13
- package/example/loadSchema.ts +0 -32
- package/src/index.ts +0 -2
- package/src/parser/parseJsonPaths.ts +0 -136
- package/src/utils/graphql/index.ts +0 -51
- package/src/utils/jsonPath/guards.ts +0 -37
- package/src/utils/jsonPath/index.ts +0 -3
- package/src/utils/jsonPath/types.ts +0 -56
- package/src/utils/jsonPath/visitor.ts +0 -108
- package/src/utils/ts/helpers.ts +0 -13
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
import { getNamedType, GraphQLSchema, isObjectType, isUnionType } from "graphql"
|
|
2
|
-
import type { DocumentNode, GraphQLObjectType, GraphQLOutputType, SelectionSetNode, FragmentDefinitionNode, FieldNode, InlineFragmentNode } from "graphql"
|
|
3
|
-
|
|
4
|
-
import { createFieldNode, createInlineFragment, findFieldNode, isPrimitiveType } from "~/utils/graphql"
|
|
5
|
-
import { Kind } from "graphql"
|
|
6
|
-
import * as JSONPath from '~/utils/jsonPath'
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
type StackItem = {type: GraphQLOutputType, selectionSet: SelectionSetNode, parent: StackItem|null}
|
|
10
|
-
const typeNameFieldNode = createFieldNode('__typename')
|
|
11
|
-
|
|
12
|
-
export function parseJsonPaths(jsonPaths:Array<JSONPath.ASTNode[]>, rootType: GraphQLObjectType, fragmentName?: string): DocumentNode {
|
|
13
|
-
fragmentName = fragmentName ?? `autoGen${rootType.name}`
|
|
14
|
-
|
|
15
|
-
const rootFragment:FragmentDefinitionNode = {
|
|
16
|
-
kind: Kind.FRAGMENT_DEFINITION,
|
|
17
|
-
typeCondition: { kind: Kind.NAMED_TYPE, name: { kind: Kind.NAME, value: rootType.name } },
|
|
18
|
-
name: { kind: Kind.NAME, value: fragmentName },
|
|
19
|
-
selectionSet: {
|
|
20
|
-
kind: Kind.SELECTION_SET,
|
|
21
|
-
selections: [typeNameFieldNode],
|
|
22
|
-
} satisfies SelectionSetNode,
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const doc: DocumentNode = {
|
|
26
|
-
kind: Kind.DOCUMENT,
|
|
27
|
-
definitions: [rootFragment],
|
|
28
|
-
}
|
|
29
|
-
const stack: Array<StackItem[]> = []
|
|
30
|
-
const visitor: JSONPath.NodeVisitor = {
|
|
31
|
-
Root() {
|
|
32
|
-
stack.push([{type: rootType, selectionSet: rootFragment.selectionSet!, parent: null}])
|
|
33
|
-
return
|
|
34
|
-
},
|
|
35
|
-
Identifier(node) {
|
|
36
|
-
const stackItems = stack[stack.length - 1]!
|
|
37
|
-
const passStackItems = processStackItems(stackItems, {fieldName: node.expression.value!, value: undefined, condition: undefined})
|
|
38
|
-
stack.push(passStackItems)
|
|
39
|
-
return
|
|
40
|
-
},
|
|
41
|
-
FilterExpression(node) {
|
|
42
|
-
const stackItems = stack[stack.length - 1]!
|
|
43
|
-
const regex = /\?\(@\.(?<field>\w+)(\s*(?<condition>(?:==|!=)=?)\s*(?<quote>'|")(?<value>.*?)(\4))?/
|
|
44
|
-
const matches = node.expression.value!.match(regex)
|
|
45
|
-
const fieldName = matches?.groups?.field ?? '__typename'
|
|
46
|
-
const condition = matches?.groups?.condition
|
|
47
|
-
const value = matches?.groups?.value
|
|
48
|
-
const passStackItems = processStackItems(stackItems, {fieldName, value, condition})
|
|
49
|
-
stack.push(passStackItems)
|
|
50
|
-
return
|
|
51
|
-
},
|
|
52
|
-
leave(node, parent, path, ancestors) {
|
|
53
|
-
if(needProcess(node)) {
|
|
54
|
-
stack.pop()
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
JSONPath.visit(jsonPaths, visitor)
|
|
59
|
-
return doc
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
function needProcess(node:JSONPath.ASTNode):boolean {
|
|
63
|
-
return JSONPath.isRootNode(node) || JSONPath.isIdentifierNode(node) || JSONPath.isFilterExpressionNode(node)
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
function processStackItems(stackItems: StackItem[], {fieldName, value, condition}: {fieldName: string, value?: string, condition?: string}) {
|
|
70
|
-
const passStackItems: StackItem[] = []
|
|
71
|
-
stackItems.forEach(stackItem => {
|
|
72
|
-
const parentType = stackItem.type
|
|
73
|
-
if(isPrimitiveType(parentType)) {
|
|
74
|
-
return
|
|
75
|
-
}
|
|
76
|
-
const selSet = stackItem.selectionSet
|
|
77
|
-
if (isUnionType(parentType)) {
|
|
78
|
-
const filterField = (type: GraphQLObjectType) => fieldName === '__typename'? type.name === value : Boolean(type.getFields()[fieldName!])
|
|
79
|
-
const filter = condition && !condition.includes('!')
|
|
80
|
-
? (type: GraphQLObjectType) => filterField(type)
|
|
81
|
-
: (type: GraphQLObjectType) => !filterField(type)
|
|
82
|
-
const possibleTypes = parentType.getTypes().filter(filter)
|
|
83
|
-
possibleTypes.forEach(type => {
|
|
84
|
-
let inlineFragment:InlineFragmentNode|undefined = selSet.selections.find(
|
|
85
|
-
selection => selection.kind === Kind.INLINE_FRAGMENT
|
|
86
|
-
&& (selection.typeCondition?.name.value === type.name)
|
|
87
|
-
) as InlineFragmentNode|undefined
|
|
88
|
-
|
|
89
|
-
if (!inlineFragment) {
|
|
90
|
-
inlineFragment = createInlineFragment(type, '__typename')
|
|
91
|
-
;(selSet.selections as any[]).push(inlineFragment)
|
|
92
|
-
}
|
|
93
|
-
const fragmentStackItem = {type: type, selectionSet: inlineFragment.selectionSet!, parent: stackItem}
|
|
94
|
-
const appendFieldType = type.getFields()[fieldName!]?.type
|
|
95
|
-
if(!appendFieldType) {
|
|
96
|
-
if (fieldName === '__typename') {
|
|
97
|
-
passStackItems.push(fragmentStackItem)
|
|
98
|
-
}
|
|
99
|
-
return
|
|
100
|
-
}
|
|
101
|
-
const newStackItem = processGraphqlObjectType(fragmentStackItem, appendFieldType, fieldName!)
|
|
102
|
-
passStackItems.push(newStackItem)
|
|
103
|
-
})
|
|
104
|
-
return;
|
|
105
|
-
}
|
|
106
|
-
const parentObjectType = isObjectType(parentType) ? parentType : stackItem.parent?.type as GraphQLObjectType|undefined
|
|
107
|
-
if (!parentObjectType) return;
|
|
108
|
-
const field = parentObjectType.getFields()[fieldName!]
|
|
109
|
-
if(!field) {
|
|
110
|
-
return
|
|
111
|
-
}
|
|
112
|
-
const newStackItem = processGraphqlObjectType(stackItem, field.type, fieldName!)
|
|
113
|
-
passStackItems.push(newStackItem)
|
|
114
|
-
|
|
115
|
-
})
|
|
116
|
-
return passStackItems
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
function processGraphqlObjectType(stackItem: StackItem, fieldType: GraphQLOutputType, fieldName: string): StackItem {
|
|
122
|
-
const selSet = stackItem.selectionSet
|
|
123
|
-
if(!selSet){
|
|
124
|
-
throw new Error('Selection set not found')
|
|
125
|
-
}
|
|
126
|
-
const realFieldType = getNamedType(fieldType)
|
|
127
|
-
const subFieldsSelections = [isPrimitiveType(realFieldType) ? undefined : typeNameFieldNode].filter(Boolean) as FieldNode[]
|
|
128
|
-
let fieldNode = findFieldNode(selSet, fieldName)
|
|
129
|
-
if(!fieldNode) {
|
|
130
|
-
fieldNode = createFieldNode(fieldName, subFieldsSelections.map(selection => selection.name.value!))
|
|
131
|
-
;(selSet.selections as any[]).push(fieldNode)
|
|
132
|
-
}
|
|
133
|
-
return {type: realFieldType, selectionSet: fieldNode.selectionSet!, parent: stackItem}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import { getNamedType, isScalarType, isEnumType, Kind } from "graphql"
|
|
2
|
-
import type { GraphQLOutputType, SelectionSetNode, FieldNode, InlineFragmentNode, GraphQLNamedOutputType, SelectionNode } from "graphql"
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
export function isPrimitiveType(type: GraphQLOutputType): boolean {
|
|
6
|
-
const namedType = getNamedType(type)
|
|
7
|
-
return isScalarType(namedType) || isEnumType(namedType)
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export function findFieldNode(selectionSet: SelectionSetNode, fieldName: string):FieldNode|undefined {
|
|
11
|
-
return selectionSet?.selections.find(selection => selection.kind === Kind.FIELD && selection.name.value === fieldName) as FieldNode|undefined
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
export function createFieldNode(fieldName: string, children?: string[]):FieldNode {
|
|
16
|
-
let selectionSet: SelectionSetNode|undefined = undefined
|
|
17
|
-
if(children?.length) {
|
|
18
|
-
selectionSet = {
|
|
19
|
-
kind: Kind.SELECTION_SET,
|
|
20
|
-
selections: children.map(child => createFieldNode(child)),
|
|
21
|
-
} satisfies SelectionSetNode
|
|
22
|
-
}
|
|
23
|
-
return {
|
|
24
|
-
kind: Kind.FIELD,
|
|
25
|
-
name: { kind: Kind.NAME, value: fieldName },
|
|
26
|
-
selectionSet
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
export function createFieldSelectionSet(outputType: GraphQLNamedOutputType, fieldName?: string):SelectionSetNode {
|
|
33
|
-
const ret = {
|
|
34
|
-
kind: Kind.SELECTION_SET,
|
|
35
|
-
selections: [fieldName ? createFieldNode(fieldName): undefined].filter(Boolean) as SelectionNode[],
|
|
36
|
-
} satisfies SelectionSetNode
|
|
37
|
-
|
|
38
|
-
if (!isPrimitiveType(outputType) && !ret.selections.find(selection => selection.kind === Kind.FIELD && selection.name.value === '__typename')) {
|
|
39
|
-
ret.selections.unshift(createFieldNode('__typename'))
|
|
40
|
-
}
|
|
41
|
-
return ret
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
export function createInlineFragment(outputType: GraphQLNamedOutputType, fieldName: string):InlineFragmentNode {
|
|
46
|
-
return {
|
|
47
|
-
kind: Kind.INLINE_FRAGMENT,
|
|
48
|
-
typeCondition: { kind: Kind.NAMED_TYPE, name: { kind: Kind.NAME, value: outputType.name } },
|
|
49
|
-
selectionSet: createFieldSelectionSet(outputType, fieldName),
|
|
50
|
-
} satisfies InlineFragmentNode
|
|
51
|
-
}
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
import type { BaseNode, IdentifierNode, ASTNode, NumericLiteralNode, FilterExpressionNode, ScriptExpressionNode, StringLiteralNode, WildcardNode, SliceNode, RootNode } from "./types"
|
|
3
|
-
|
|
4
|
-
export function isAstNode(node: unknown): node is ASTNode {
|
|
5
|
-
return typeof node === 'object' && node !== null && 'expression' in node && typeof node.expression === 'object' && node.expression !== null && 'type' in node.expression && typeof node.expression.type === 'string'
|
|
6
|
-
}
|
|
7
|
-
export function isBaseNode(node: ASTNode): node is BaseNode {
|
|
8
|
-
return isAstNode(node) && 'operation' in node && typeof node.operation === 'string' && 'scope' in node && typeof node.scope === 'string'
|
|
9
|
-
}
|
|
10
|
-
export function isIdentifierNode(node: ASTNode): node is IdentifierNode {
|
|
11
|
-
return isBaseNode(node) && node.operation === 'member' && node.expression.type === 'identifier'
|
|
12
|
-
}
|
|
13
|
-
export function isNumericLiteralNode(node: ASTNode): node is NumericLiteralNode {
|
|
14
|
-
return isBaseNode(node) && node.operation === 'subscript' && node.expression.type === 'numeric_literal'
|
|
15
|
-
}
|
|
16
|
-
export function isFilterExpressionNode(node: ASTNode): node is FilterExpressionNode {
|
|
17
|
-
return isBaseNode(node) && node.operation === 'subscript' && node.expression.type === 'filter_expression'
|
|
18
|
-
}
|
|
19
|
-
export function isScriptExpressionNode(node: ASTNode): node is ScriptExpressionNode {
|
|
20
|
-
return isBaseNode(node) && node.operation === 'subscript' && node.expression.type === 'script_expression'
|
|
21
|
-
}
|
|
22
|
-
export function isStringLiteralNode(node: ASTNode): node is StringLiteralNode {
|
|
23
|
-
return isBaseNode(node) && node.operation === 'subscript' && node.expression.type === 'string_literal'
|
|
24
|
-
}
|
|
25
|
-
export function isWildcardNode(node: ASTNode): node is WildcardNode {
|
|
26
|
-
return isBaseNode(node) && node.operation === 'subscript' && node.expression.type === 'wildcard'
|
|
27
|
-
}
|
|
28
|
-
export function isSliceNode(node: ASTNode): node is SliceNode {
|
|
29
|
-
return isBaseNode(node) && node.operation === 'subscript' && node.expression.type === 'slice'
|
|
30
|
-
}
|
|
31
|
-
export function isRootNode(node: ASTNode): node is RootNode {
|
|
32
|
-
return isAstNode(node) && node.expression.type === 'root' && node.expression.value === '$'
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export function isJSONPath(nodes: unknown): nodes is ASTNode[] {
|
|
36
|
-
return Array.isArray(nodes) && nodes.every(node => isAstNode(node))
|
|
37
|
-
}
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
export interface ASTNode {
|
|
2
|
-
expression: Expression
|
|
3
|
-
}
|
|
4
|
-
|
|
5
|
-
export type ExpressionType = 'root' | 'identifier' | 'numeric_literal' | 'filter_expression' | 'script_expression' | 'string_literal' | 'wildcard' | 'slice'
|
|
6
|
-
export type ExpressionValue = string | number | undefined
|
|
7
|
-
|
|
8
|
-
interface BuildExpression<T extends ExpressionType = ExpressionType, V extends ExpressionValue = ExpressionValue> {
|
|
9
|
-
type: T
|
|
10
|
-
value: V
|
|
11
|
-
}
|
|
12
|
-
/** expression types */
|
|
13
|
-
export type Expression = BuildExpression<ExpressionType, ExpressionValue>
|
|
14
|
-
export type RootExpression = BuildExpression<'root', '$'>
|
|
15
|
-
export type IdentifierExpression = BuildExpression<'identifier', string>
|
|
16
|
-
export type NumericLiteralExpression = BuildExpression<'numeric_literal', number>
|
|
17
|
-
export type FilterExpressionExpression = BuildExpression<'filter_expression', `?(${string})`>
|
|
18
|
-
export type ScriptExpressionExpression = BuildExpression<'script_expression', string>
|
|
19
|
-
export type StringLiteralExpression = BuildExpression<'string_literal', string>
|
|
20
|
-
export type WildcardExpression = BuildExpression<'wildcard', '*'>
|
|
21
|
-
export type SliceExpression = BuildExpression<'slice', `${number|''}:${number|''}`>
|
|
22
|
-
|
|
23
|
-
/** node types */
|
|
24
|
-
export interface RootNode extends ASTNode {
|
|
25
|
-
expression: RootExpression
|
|
26
|
-
}
|
|
27
|
-
export interface BaseNode<T extends 'member' | 'subscript' = 'member'|'subscript'> extends ASTNode {
|
|
28
|
-
operation: T
|
|
29
|
-
scope: 'child' | 'descendant' | 'parent'
|
|
30
|
-
}
|
|
31
|
-
export interface IdentifierNode extends BaseNode<'member'> {
|
|
32
|
-
expression: IdentifierExpression
|
|
33
|
-
}
|
|
34
|
-
export interface NumericLiteralNode extends BaseNode<'subscript'> {
|
|
35
|
-
expression: NumericLiteralExpression
|
|
36
|
-
}
|
|
37
|
-
export interface FilterExpressionNode extends BaseNode<'subscript'> {
|
|
38
|
-
expression: FilterExpressionExpression
|
|
39
|
-
}
|
|
40
|
-
export interface ScriptExpressionNode extends BaseNode<'subscript'> {
|
|
41
|
-
expression: ScriptExpressionExpression
|
|
42
|
-
}
|
|
43
|
-
export interface StringLiteralNode extends BaseNode<'subscript'> {
|
|
44
|
-
expression: StringLiteralExpression
|
|
45
|
-
}
|
|
46
|
-
export interface WildcardNode extends BaseNode<'subscript'> {
|
|
47
|
-
expression: WildcardExpression
|
|
48
|
-
}
|
|
49
|
-
export interface SliceNode extends BaseNode<'subscript'> {
|
|
50
|
-
expression: SliceExpression
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
import type { ASTNode, ExpressionType, RootNode, IdentifierNode, NumericLiteralNode, FilterExpressionNode, ScriptExpressionNode, StringLiteralNode, WildcardNode, SliceNode } from "./types"
|
|
2
|
-
import { isJSONPath } from "./guards"
|
|
3
|
-
import { isAstNode } from "./guards"
|
|
4
|
-
import { snakeToPascalCase, type SnakeToPascalCase } from "~/utils/ts/helpers"
|
|
5
|
-
|
|
6
|
-
export const BREAK = Symbol('BREAK')
|
|
7
|
-
|
|
8
|
-
type Kind = {
|
|
9
|
-
root: RootNode
|
|
10
|
-
identifier: IdentifierNode
|
|
11
|
-
numeric_literal: NumericLiteralNode
|
|
12
|
-
filter_expression: FilterExpressionNode
|
|
13
|
-
script_expression: ScriptExpressionNode
|
|
14
|
-
string_literal: StringLiteralNode
|
|
15
|
-
wildcard: WildcardNode
|
|
16
|
-
slice: SliceNode
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
/** visitor types */
|
|
21
|
-
export type VisitorFn = (node: ASTNode, parent: ASTNode|null, index: Array<number>, ancestors: ASTNode[]) => ASTNode|typeof BREAK|undefined|null|void
|
|
22
|
-
export type TypedVisitorFn<T extends ExpressionType = ExpressionType> = (node: Kind[T], parent: ASTNode|null, index: Array<number>, ancestors: ASTNode[]) => ASTNode|typeof BREAK|undefined|null|void
|
|
23
|
-
export interface IVisitor {
|
|
24
|
-
enter?:VisitorFn;
|
|
25
|
-
leave?:VisitorFn;
|
|
26
|
-
}
|
|
27
|
-
export type NodeVisitor = {
|
|
28
|
-
[T in ExpressionType as `${'leave' | ''}${SnakeToPascalCase<T>}`]?: TypedVisitorFn<T>;
|
|
29
|
-
} & IVisitor
|
|
30
|
-
|
|
31
|
-
function doVisit(nodes: ASTNode[]|Array<ASTNode[]>, visitor: IVisitor):void {
|
|
32
|
-
if (isJSONPath(nodes)){
|
|
33
|
-
nodes = [nodes]
|
|
34
|
-
}
|
|
35
|
-
for (const jsonPath of nodes) {
|
|
36
|
-
let parent: ASTNode|null = null
|
|
37
|
-
let ancestors: ASTNode[] = []
|
|
38
|
-
let path: Array<number> = []
|
|
39
|
-
for (let i = 0; i < jsonPath.length; i++) {
|
|
40
|
-
const _node = jsonPath[i]!
|
|
41
|
-
let node: ASTNode = _node
|
|
42
|
-
path.push(i)
|
|
43
|
-
const enterResult = visitor.enter?.(node, parent, path, ancestors)
|
|
44
|
-
if(enterResult === BREAK) {
|
|
45
|
-
break
|
|
46
|
-
}
|
|
47
|
-
if(isAstNode(enterResult)) {
|
|
48
|
-
node = enterResult
|
|
49
|
-
}
|
|
50
|
-
parent = node
|
|
51
|
-
ancestors.push(node)
|
|
52
|
-
jsonPath[i] = node
|
|
53
|
-
}
|
|
54
|
-
parent = ancestors.pop() ?? null
|
|
55
|
-
path.pop()
|
|
56
|
-
for (let i = jsonPath.length - 1; i >= 0; i--) {
|
|
57
|
-
const _node = jsonPath[i]!
|
|
58
|
-
let node: ASTNode = _node
|
|
59
|
-
parent = ancestors.pop() ?? null
|
|
60
|
-
path.pop()
|
|
61
|
-
const leaveResult = visitor.leave?.(node, parent, path, ancestors)
|
|
62
|
-
if(leaveResult === BREAK) {
|
|
63
|
-
break;
|
|
64
|
-
}
|
|
65
|
-
if(isAstNode(leaveResult)) {
|
|
66
|
-
node = leaveResult
|
|
67
|
-
}
|
|
68
|
-
jsonPath[i] = node
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
function makeVisitor(visitor: NodeVisitor): IVisitor {
|
|
75
|
-
const visitorCall = function (type:'enter'|'leave', ...[node, ...rest]: Parameters<TypedVisitorFn>): ReturnType<TypedVisitorFn> {
|
|
76
|
-
let result: ReturnType<TypedVisitorFn>
|
|
77
|
-
//call leave node method
|
|
78
|
-
if (type === 'leave') {
|
|
79
|
-
const methodName = `${type}${snakeToPascalCase(node.expression.type) as SnakeToPascalCase<ExpressionType>}` as keyof NodeVisitor
|
|
80
|
-
result = visitor[methodName]?.(node as any,...rest)
|
|
81
|
-
if(result === BREAK) {
|
|
82
|
-
return result
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
//call original visitor
|
|
86
|
-
if (type in visitor) {
|
|
87
|
-
result = (visitor as IVisitor)[type as keyof IVisitor]?.(node, ...rest)
|
|
88
|
-
if(result === BREAK) {
|
|
89
|
-
return result
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
//call enter enter node method
|
|
93
|
-
if (type === 'enter') {
|
|
94
|
-
const methodName = `${snakeToPascalCase(node.expression.type) as SnakeToPascalCase<ExpressionType>}` as keyof NodeVisitor
|
|
95
|
-
result = visitor[methodName]?.(node as any,...rest)
|
|
96
|
-
}
|
|
97
|
-
return result
|
|
98
|
-
}
|
|
99
|
-
const result: IVisitor = {
|
|
100
|
-
enter: visitorCall.bind(null, 'enter') as VisitorFn,
|
|
101
|
-
leave: visitorCall.bind(null, 'leave') as VisitorFn,
|
|
102
|
-
}
|
|
103
|
-
return result
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
export function visit(nodes: ASTNode[]|Array<ASTNode[]>, visitor: NodeVisitor):void{
|
|
107
|
-
doVisit(nodes, makeVisitor(visitor))
|
|
108
|
-
}
|
package/src/utils/ts/helpers.ts
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
export type SnakeToCamelCase<S extends string> = S extends `${infer T}_${infer U}` ?
|
|
2
|
-
`${T}${Capitalize<SnakeToCamelCase<U>>}` : S
|
|
3
|
-
export type SnakeToPascalCase<S extends string> = Capitalize<SnakeToCamelCase<S>>
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
export function snakeToCamelCase(str: string): SnakeToCamelCase<string>
|
|
7
|
-
{
|
|
8
|
-
return str.split('_').map(s => s.charAt(0).toUpperCase() + s.slice(1)).join('') as SnakeToCamelCase<string>
|
|
9
|
-
}
|
|
10
|
-
export function snakeToPascalCase(str: string): SnakeToPascalCase<string>
|
|
11
|
-
{
|
|
12
|
-
return snakeToCamelCase(str).charAt(0).toUpperCase() + snakeToCamelCase(str).slice(1) as SnakeToPascalCase<string>
|
|
13
|
-
}
|