1+ import * as ts from 'typescript' ;
2+ import * as fs from 'fs' ;
3+ import * as path from 'path' ;
4+
5+ /**
6+ * Script to strip types from deparser files and copy them to version directories
7+ * Replaces @pgsql/types imports and t.* type references with 'any'
8+ */
9+
10+ const DEPARSER_SRC_DIR = '../deparser/src' ;
11+ const VERSIONS_DIR = 'versions' ;
12+ const VERSIONS = [ 13 , 14 , 15 , 16 ] ;
13+
14+ // Types to strip
15+ const TYPES_TO_STRIP = [ 'Node' , '@pgsql/types' ] ;
16+
17+ function stripTypes ( sourceFile : ts . SourceFile , fileName : string ) : string {
18+ const printer = ts . createPrinter ( { newLine : ts . NewLineKind . LineFeed } ) ;
19+
20+ const transformer : ts . TransformerFactory < ts . SourceFile > = ( context ) => {
21+ const visit : ts . Visitor = ( node ) => {
22+ // Remove or modify import declarations
23+ if ( ts . isImportDeclaration ( node ) ) {
24+ const moduleSpecifier = node . moduleSpecifier ;
25+ if ( ts . isStringLiteral ( moduleSpecifier ) ) {
26+ const importPath = moduleSpecifier . text ;
27+
28+ // Remove @pgsql /types imports entirely
29+ if ( importPath === '@pgsql/types' ) {
30+ return undefined ;
31+ }
32+ }
33+ }
34+
35+ // Replace type references with 'any'
36+ if ( ts . isTypeReferenceNode ( node ) ) {
37+ const typeName = node . typeName . getText ( ) ;
38+
39+ // Replace Node type
40+ if ( typeName === 'Node' ) {
41+ return ts . factory . createKeywordTypeNode ( ts . SyntaxKind . AnyKeyword ) ;
42+ }
43+
44+ // Replace t.* types (e.g., t.SelectStmt, t.A_Const)
45+ if ( typeName . startsWith ( 't.' ) ) {
46+ return ts . factory . createKeywordTypeNode ( ts . SyntaxKind . AnyKeyword ) ;
47+ }
48+ }
49+
50+ // Replace qualified names in property access (e.g., node as t.SelectStmt)
51+ if ( ts . isPropertyAccessExpression ( node ) ) {
52+ const expression = node . expression ;
53+ if ( ts . isIdentifier ( expression ) && expression . text === 't' ) {
54+ // This is a t.* reference, but in an expression context
55+ // We'll handle this in type assertions
56+ }
57+ }
58+
59+ // Replace type assertions
60+ if ( ts . isAsExpression ( node ) ) {
61+ const typeText = node . type . getText ( ) ;
62+
63+ // Remove assertions with Node type
64+ if ( typeText === 'Node' ) {
65+ return node . expression ;
66+ }
67+
68+ // Remove assertions with t.* types
69+ if ( typeText . startsWith ( 't.' ) ) {
70+ return node . expression ;
71+ }
72+ }
73+
74+ // Replace parameter type annotations
75+ if ( ts . isParameter ( node ) && node . type ) {
76+ const typeText = node . type . getText ( ) ;
77+ if ( typeText === 'Node' || typeText . startsWith ( 't.' ) ) {
78+ return ts . factory . updateParameterDeclaration (
79+ node ,
80+ node . modifiers ,
81+ node . dotDotDotToken ,
82+ node . name ,
83+ node . questionToken ,
84+ ts . factory . createKeywordTypeNode ( ts . SyntaxKind . AnyKeyword ) ,
85+ node . initializer
86+ ) ;
87+ }
88+ }
89+
90+ // Replace variable type annotations
91+ if ( ts . isVariableDeclaration ( node ) && node . type ) {
92+ const typeText = node . type . getText ( ) ;
93+ if ( typeText === 'Node' || typeText . startsWith ( 't.' ) ) {
94+ return ts . factory . updateVariableDeclaration (
95+ node ,
96+ node . name ,
97+ node . exclamationToken ,
98+ ts . factory . createKeywordTypeNode ( ts . SyntaxKind . AnyKeyword ) ,
99+ node . initializer
100+ ) ;
101+ }
102+ }
103+
104+ // Replace return type annotations
105+ if ( ( ts . isMethodDeclaration ( node ) || ts . isFunctionDeclaration ( node ) || ts . isArrowFunction ( node ) ) && node . type ) {
106+ const typeText = node . type . getText ( ) ;
107+ if ( typeText === 'Node' || typeText . startsWith ( 't.' ) ) {
108+ if ( ts . isMethodDeclaration ( node ) ) {
109+ return ts . factory . updateMethodDeclaration (
110+ node ,
111+ node . modifiers ,
112+ node . asteriskToken ,
113+ node . name ,
114+ node . questionToken ,
115+ node . typeParameters ,
116+ node . parameters ,
117+ ts . factory . createKeywordTypeNode ( ts . SyntaxKind . AnyKeyword ) ,
118+ node . body
119+ ) ;
120+ } else if ( ts . isFunctionDeclaration ( node ) ) {
121+ return ts . factory . updateFunctionDeclaration (
122+ node ,
123+ node . modifiers ,
124+ node . asteriskToken ,
125+ node . name ,
126+ node . typeParameters ,
127+ node . parameters ,
128+ ts . factory . createKeywordTypeNode ( ts . SyntaxKind . AnyKeyword ) ,
129+ node . body
130+ ) ;
131+ } else if ( ts . isArrowFunction ( node ) ) {
132+ return ts . factory . updateArrowFunction (
133+ node ,
134+ node . modifiers ,
135+ node . typeParameters ,
136+ node . parameters ,
137+ ts . factory . createKeywordTypeNode ( ts . SyntaxKind . AnyKeyword ) ,
138+ node . equalsGreaterThanToken ,
139+ node . body
140+ ) ;
141+ }
142+ }
143+ }
144+
145+ // Handle property declarations with types
146+ if ( ts . isPropertyDeclaration ( node ) && node . type ) {
147+ const typeText = node . type . getText ( ) ;
148+ if ( typeText === 'Node' || typeText . startsWith ( 't.' ) ) {
149+ return ts . factory . updatePropertyDeclaration (
150+ node ,
151+ node . modifiers ,
152+ node . name ,
153+ node . questionToken || node . exclamationToken ,
154+ ts . factory . createKeywordTypeNode ( ts . SyntaxKind . AnyKeyword ) ,
155+ node . initializer
156+ ) ;
157+ }
158+ }
159+
160+ // Handle interface declarations - convert to type alias with any
161+ if ( ts . isInterfaceDeclaration ( node ) ) {
162+ // Skip interfaces that extend from @pgsql /types
163+ const hasTypesExtension = node . heritageClauses ?. some ( clause =>
164+ clause . types . some ( type => {
165+ const typeText = type . expression . getText ( ) ;
166+ return typeText . startsWith ( 't.' ) || typeText === 'Node' ;
167+ } )
168+ ) ;
169+
170+ if ( hasTypesExtension ) {
171+ // Convert interface to type alias = any
172+ return ts . factory . createTypeAliasDeclaration (
173+ node . modifiers ,
174+ node . name ,
175+ node . typeParameters ,
176+ ts . factory . createKeywordTypeNode ( ts . SyntaxKind . AnyKeyword )
177+ ) ;
178+ }
179+ }
180+
181+ return ts . visitEachChild ( node , visit , context ) ;
182+ } ;
183+
184+ return ( node ) => ts . visitNode ( node , visit ) as ts . SourceFile ;
185+ } ;
186+
187+ const result = ts . transform ( sourceFile , [ transformer ] ) ;
188+ const transformedSourceFile = result . transformed [ 0 ] ;
189+
190+ let code = printer . printFile ( transformedSourceFile ) ;
191+
192+ // Post-process to clean up any remaining type references
193+ // Remove any remaining 'as t.*' patterns
194+ code = code . replace ( / \s + a s \s + t \. [ A - Z a - z _ ] + / g, '' ) ;
195+
196+ // Remove any remaining 'as Node' patterns
197+ code = code . replace ( / \s + a s \s + N o d e / g, '' ) ;
198+
199+ // Add header comment
200+ const headerComment = `/**
201+ * Auto-generated file with types stripped for better tree-shaking
202+ * DO NOT EDIT - Generated by strip-deparser-types.ts
203+ */
204+
205+ ` ;
206+
207+ return headerComment + code ;
208+ }
209+
210+ function processFile ( filePath : string , relativePath : string ) : string {
211+ console . log ( ` Processing ${ relativePath } ...` ) ;
212+
213+ const sourceCode = fs . readFileSync ( filePath , 'utf-8' ) ;
214+
215+ const sourceFile = ts . createSourceFile (
216+ filePath ,
217+ sourceCode ,
218+ ts . ScriptTarget . Latest ,
219+ true
220+ ) ;
221+
222+ return stripTypes ( sourceFile , filePath ) ;
223+ }
224+
225+ function copyDeparserToVersions ( ) : void {
226+ console . log ( 'Stripping types from deparser files and copying to version directories...\n' ) ;
227+
228+ const deparserSrcPath = path . join ( process . cwd ( ) , DEPARSER_SRC_DIR ) ;
229+
230+ if ( ! fs . existsSync ( deparserSrcPath ) ) {
231+ console . error ( `Deparser source directory not found: ${ deparserSrcPath } ` ) ;
232+ return ;
233+ }
234+
235+ // Get all TypeScript files in deparser src
236+ const files : string [ ] = [ ] ;
237+
238+ function collectFiles ( dir : string , baseDir : string = '' ) {
239+ const entries = fs . readdirSync ( dir , { withFileTypes : true } ) ;
240+
241+ for ( const entry of entries ) {
242+ const fullPath = path . join ( dir , entry . name ) ;
243+ const relativePath = path . join ( baseDir , entry . name ) ;
244+
245+ if ( entry . isDirectory ( ) ) {
246+ collectFiles ( fullPath , relativePath ) ;
247+ } else if ( entry . isFile ( ) && entry . name . endsWith ( '.ts' ) ) {
248+ files . push ( relativePath ) ;
249+ }
250+ }
251+ }
252+
253+ collectFiles ( deparserSrcPath ) ;
254+
255+ console . log ( `Found ${ files . length } TypeScript files in deparser src\n` ) ;
256+
257+ // Process each version
258+ for ( const version of VERSIONS ) {
259+ console . log ( `Processing version ${ version } ...` ) ;
260+
261+ const versionDeparserDir = path . join ( VERSIONS_DIR , version . toString ( ) , 'deparser' ) ;
262+
263+ // Create deparser directory
264+ if ( ! fs . existsSync ( versionDeparserDir ) ) {
265+ fs . mkdirSync ( versionDeparserDir , { recursive : true } ) ;
266+ }
267+
268+ // Process and copy each file
269+ for ( const file of files ) {
270+ const sourcePath = path . join ( deparserSrcPath , file ) ;
271+ const destPath = path . join ( versionDeparserDir , file ) ;
272+
273+ // Create subdirectories if needed
274+ const destDir = path . dirname ( destPath ) ;
275+ if ( ! fs . existsSync ( destDir ) ) {
276+ fs . mkdirSync ( destDir , { recursive : true } ) ;
277+ }
278+
279+ // Process and write file
280+ const strippedCode = processFile ( sourcePath , file ) ;
281+ fs . writeFileSync ( destPath , strippedCode ) ;
282+ }
283+
284+ console . log ( ` ✓ Copied ${ files . length } files to versions/${ version } /deparser/\n` ) ;
285+ }
286+
287+ console . log ( 'Done! Deparser files have been stripped and copied to all version directories.' ) ;
288+ }
289+
290+ // Run the script
291+ copyDeparserToVersions ( ) ;
0 commit comments