|
| 1 | +import { NodeGraph } from '@daxserver/validation-schema-codegen/traverse/node-graph' |
| 2 | +import type { TraversedNode } from '@daxserver/validation-schema-codegen/traverse/types' |
| 3 | +import { resolverStore } from '@daxserver/validation-schema-codegen/utils/resolver-store' |
| 4 | +import { Node, SourceFile } from 'ts-morph' |
| 5 | + |
| 6 | +const CHUNK_SIZE = 20 |
| 7 | + |
| 8 | +export const shouldChunkUnion = (node: Node): boolean => { |
| 9 | + if (Node.isUnionTypeNode(node)) { |
| 10 | + return node.getTypeNodes().length >= CHUNK_SIZE |
| 11 | + } |
| 12 | + |
| 13 | + // Handle IndexedAccessType that resolves to large unions (e.g., typeof sites[number]) |
| 14 | + if (Node.isIndexedAccessTypeNode(node)) { |
| 15 | + const typeChecker = node.getProject().getTypeChecker() |
| 16 | + const type = typeChecker.getTypeAtLocation(node) |
| 17 | + if (type.isUnion()) { |
| 18 | + return type.getUnionTypes().length >= CHUNK_SIZE |
| 19 | + } |
| 20 | + } |
| 21 | + |
| 22 | + return false |
| 23 | +} |
| 24 | + |
| 25 | +export const createChunkNodes = ( |
| 26 | + node: Node, |
| 27 | + parentTypeName: string, |
| 28 | + nodeGraph: NodeGraph, |
| 29 | + maincodeNodeIds: Set<string>, |
| 30 | + requiredNodeIds: Set<string>, |
| 31 | + sourceFile: SourceFile, |
| 32 | + addToRequired: boolean = true, |
| 33 | +): string[] => { |
| 34 | + let typeTexts: string[] = [] |
| 35 | + |
| 36 | + if (Node.isUnionTypeNode(node)) { |
| 37 | + typeTexts = node.getTypeNodes().map((n) => n.getText()) |
| 38 | + } else if (Node.isIndexedAccessTypeNode(node)) { |
| 39 | + // Handle IndexedAccessType by resolving to union types |
| 40 | + const typeChecker = node.getProject().getTypeChecker() |
| 41 | + const type = typeChecker.getTypeAtLocation(node) |
| 42 | + if (type.isUnion()) { |
| 43 | + typeTexts = type.getUnionTypes().map((t) => t.getText()) |
| 44 | + } |
| 45 | + } |
| 46 | + |
| 47 | + if (typeTexts.length === 0) { |
| 48 | + return [] |
| 49 | + } |
| 50 | + const chunks: string[][] = [] |
| 51 | + |
| 52 | + // Create chunks of 20 items each |
| 53 | + for (let i = 0; i < typeTexts.length; i += CHUNK_SIZE) { |
| 54 | + chunks.push(typeTexts.slice(i, i + CHUNK_SIZE)) |
| 55 | + } |
| 56 | + |
| 57 | + const chunkReferences: string[] = [] |
| 58 | + |
| 59 | + // scratch file for chunk nodes (once per parent) |
| 60 | + const scratchSourceFile = node.getProject().createSourceFile( |
| 61 | + `__chunks_${parentTypeName}_${Date.now()}.ts`, |
| 62 | + '', |
| 63 | + { overwrite: true }, |
| 64 | + ) |
| 65 | + |
| 66 | + // Create chunk nodes |
| 67 | + for (let i = 0; i < chunks.length; i++) { |
| 68 | + const chunkName = `${parentTypeName}_Chunk${i + 1}` |
| 69 | + const chunkQualifiedName = resolverStore.generateQualifiedName(chunkName, sourceFile) |
| 70 | + |
| 71 | + chunkReferences.push(chunkQualifiedName) |
| 72 | + maincodeNodeIds.add(chunkQualifiedName) |
| 73 | + if (addToRequired) { |
| 74 | + requiredNodeIds.add(chunkQualifiedName) |
| 75 | + } |
| 76 | + |
| 77 | + // Create a new union node with only the chunk's type texts |
| 78 | + const chunkTypeTexts = chunks[i]! |
| 79 | + |
| 80 | + // Create a synthetic union node for this chunk |
| 81 | + const chunkAlias = scratchSourceFile.addTypeAlias({ |
| 82 | + name: chunkName, |
| 83 | + type: chunkTypeTexts.join(' | '), |
| 84 | + }) |
| 85 | + const chunkTypeNode = chunkAlias.getTypeNode()! |
| 86 | + |
| 87 | + const chunkTraversedNode: TraversedNode = { |
| 88 | + node: chunkTypeNode, // Use the chunk-specific union node |
| 89 | + type: 'chunk', |
| 90 | + originalName: chunkName, |
| 91 | + qualifiedName: chunkQualifiedName, |
| 92 | + isImported: false, |
| 93 | + isMainCode: true, |
| 94 | + isChunk: true, |
| 95 | + chunkReferences: [], // Chunk nodes don't need references to other chunks |
| 96 | + } |
| 97 | + |
| 98 | + nodeGraph.addTypeNode(chunkQualifiedName, chunkTraversedNode) |
| 99 | + |
| 100 | + // Add to ResolverStore |
| 101 | + resolverStore.addTypeMapping({ |
| 102 | + originalName: chunkName, |
| 103 | + sourceFile, |
| 104 | + }) |
| 105 | + } |
| 106 | + |
| 107 | + return chunkReferences |
| 108 | +} |
| 109 | + |
| 110 | +export const markChunksAsRequired = ( |
| 111 | + parentQualifiedName: string, |
| 112 | + nodeGraph: NodeGraph, |
| 113 | + requiredNodeIds: Set<string>, |
| 114 | +): void => { |
| 115 | + const parentNode = nodeGraph.getNode(parentQualifiedName) |
| 116 | + if (parentNode?.chunkReferences) { |
| 117 | + for (const chunkRef of parentNode.chunkReferences) { |
| 118 | + requiredNodeIds.add(chunkRef) |
| 119 | + } |
| 120 | + } |
| 121 | +} |
0 commit comments