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 direct transformer files and replace them with 'any'
7+ * Also removes overload signatures and adjusts imports to use generated versions
8+ */
9+
10+ const DIRECT_TRANSFORMER_FILES = [
11+ 'src/transformers-direct/v13-to-v17/index.ts' ,
12+ 'src/transformers-direct/v14-to-v17/index.ts' ,
13+ 'src/transformers-direct/v15-to-v17/index.ts' ,
14+ 'src/transformers-direct/v16-to-v17/index.ts'
15+ ] ;
16+
17+ const OUTPUT_DIR = 'versions/direct' ;
18+
19+ // Types to strip and replace with 'any'
20+ const TYPES_TO_STRIP = [
21+ 'PG13' , 'PG14' , 'PG15' , 'PG16' , 'PG17' ,
22+ 'V13Types' , 'V14Types' , 'V15Types' , 'V16Types' , 'V17Types' ,
23+ 'TransformerContext'
24+ ] ;
25+
26+ function stripTypes ( sourceFile : ts . SourceFile , fileName : string ) : string {
27+ const printer = ts . createPrinter ( { newLine : ts . NewLineKind . LineFeed } ) ;
28+
29+ const transformer : ts . TransformerFactory < ts . SourceFile > = ( context ) => {
30+ let hasTransformMethod = false ;
31+
32+ const visit : ts . Visitor = ( node ) => {
33+ // Update import paths to use generated versions
34+ if ( ts . isImportDeclaration ( node ) ) {
35+ const moduleSpecifier = node . moduleSpecifier ;
36+ if ( ts . isStringLiteral ( moduleSpecifier ) ) {
37+ const importPath = moduleSpecifier . text ;
38+
39+ // Remove imports for type modules
40+ if ( importPath . includes ( '/types' ) ) {
41+ return undefined ;
42+ }
43+
44+ // Update transformer imports to use versions directory
45+ if ( importPath . includes ( 'transformers/v' ) ) {
46+ const transformerMatch = importPath . match ( / t r a n s f o r m e r s \/ ( v \d + - t o - v \d + ) / ) ;
47+ if ( transformerMatch ) {
48+ const newPath = `../${ transformerMatch [ 1 ] } ` ;
49+ return ts . factory . updateImportDeclaration (
50+ node ,
51+ node . modifiers ,
52+ node . importClause ,
53+ ts . factory . createStringLiteral ( newPath ) ,
54+ node . assertClause
55+ ) ;
56+ }
57+ }
58+ }
59+ }
60+
61+ // Handle method declarations - remove overloads, keep implementation
62+ if ( ts . isMethodDeclaration ( node ) && node . name && node . name . getText ( ) === 'transform' ) {
63+ // If it's just a signature (no body), skip it
64+ if ( ! node . body ) {
65+ return undefined ;
66+ }
67+
68+ hasTransformMethod = true ;
69+
70+ // Replace the implementation with 'any' types
71+ return ts . factory . updateMethodDeclaration (
72+ node ,
73+ node . modifiers ,
74+ node . asteriskToken ,
75+ node . name ,
76+ node . questionToken ,
77+ node . typeParameters ,
78+ node . parameters . map ( param =>
79+ ts . factory . updateParameterDeclaration (
80+ param ,
81+ param . modifiers ,
82+ param . dotDotDotToken ,
83+ param . name ,
84+ param . questionToken ,
85+ ts . factory . createKeywordTypeNode ( ts . SyntaxKind . AnyKeyword ) ,
86+ param . initializer
87+ )
88+ ) ,
89+ ts . factory . createKeywordTypeNode ( ts . SyntaxKind . AnyKeyword ) ,
90+ node . body
91+ ) ;
92+ }
93+
94+ // Replace type references with 'any'
95+ if ( ts . isTypeReferenceNode ( node ) ) {
96+ const typeName = node . typeName . getText ( ) ;
97+
98+ // Check if it's a qualified name (e.g., PG13.Node)
99+ if ( typeName . includes ( '.' ) ) {
100+ const [ namespace ] = typeName . split ( '.' ) ;
101+ if ( TYPES_TO_STRIP . includes ( namespace ) ) {
102+ return ts . factory . createKeywordTypeNode ( ts . SyntaxKind . AnyKeyword ) ;
103+ }
104+ }
105+ }
106+
107+ // Replace parameter type annotations
108+ if ( ts . isParameter ( node ) && node . type ) {
109+ const typeText = node . type . getText ( ) ;
110+ if ( TYPES_TO_STRIP . some ( type => typeText . includes ( type ) ) ) {
111+ return ts . factory . updateParameterDeclaration (
112+ node ,
113+ node . modifiers ,
114+ node . dotDotDotToken ,
115+ node . name ,
116+ node . questionToken ,
117+ ts . factory . createKeywordTypeNode ( ts . SyntaxKind . AnyKeyword ) ,
118+ node . initializer
119+ ) ;
120+ }
121+ }
122+
123+ // Replace variable type annotations
124+ if ( ts . isVariableDeclaration ( node ) && node . type ) {
125+ const typeText = node . type . getText ( ) ;
126+ if ( TYPES_TO_STRIP . some ( type => typeText . includes ( type ) ) ) {
127+ return ts . factory . updateVariableDeclaration (
128+ node ,
129+ node . name ,
130+ node . exclamationToken ,
131+ ts . factory . createKeywordTypeNode ( ts . SyntaxKind . AnyKeyword ) ,
132+ node . initializer
133+ ) ;
134+ }
135+ }
136+
137+ // Replace return type annotations
138+ if ( ( ts . isMethodDeclaration ( node ) || ts . isFunctionDeclaration ( node ) || ts . isArrowFunction ( node ) ) && node . type ) {
139+ const typeText = node . type . getText ( ) ;
140+ if ( TYPES_TO_STRIP . some ( type => typeText . includes ( type ) ) ) {
141+ if ( ts . isMethodDeclaration ( node ) ) {
142+ return ts . factory . updateMethodDeclaration (
143+ node ,
144+ node . modifiers ,
145+ node . asteriskToken ,
146+ node . name ,
147+ node . questionToken ,
148+ node . typeParameters ,
149+ node . parameters ,
150+ ts . factory . createKeywordTypeNode ( ts . SyntaxKind . AnyKeyword ) ,
151+ node . body
152+ ) ;
153+ } else if ( ts . isFunctionDeclaration ( node ) ) {
154+ return ts . factory . updateFunctionDeclaration (
155+ node ,
156+ node . modifiers ,
157+ node . asteriskToken ,
158+ node . name ,
159+ node . typeParameters ,
160+ node . parameters ,
161+ ts . factory . createKeywordTypeNode ( ts . SyntaxKind . AnyKeyword ) ,
162+ node . body
163+ ) ;
164+ } else if ( ts . isArrowFunction ( node ) ) {
165+ return ts . factory . updateArrowFunction (
166+ node ,
167+ node . modifiers ,
168+ node . typeParameters ,
169+ node . parameters ,
170+ ts . factory . createKeywordTypeNode ( ts . SyntaxKind . AnyKeyword ) ,
171+ node . equalsGreaterThanToken ,
172+ node . body
173+ ) ;
174+ }
175+ }
176+ }
177+
178+ // Replace type assertions
179+ if ( ts . isAsExpression ( node ) ) {
180+ const typeText = node . type . getText ( ) ;
181+ if ( TYPES_TO_STRIP . some ( type => typeText . includes ( type ) ) ) {
182+ return node . expression ; // Remove the assertion entirely
183+ }
184+ // Also check for any 'as' expression that ends with .ParseResult or .Node
185+ if ( typeText . match ( / \. ( P a r s e R e s u l t | N o d e ) $ / ) ) {
186+ return node . expression ;
187+ }
188+ }
189+
190+ // Handle property declarations with types
191+ if ( ts . isPropertyDeclaration ( node ) && node . type ) {
192+ const typeText = node . type . getText ( ) ;
193+ if ( TYPES_TO_STRIP . some ( type => typeText . includes ( type ) ) ) {
194+ return ts . factory . updatePropertyDeclaration (
195+ node ,
196+ node . modifiers ,
197+ node . name ,
198+ node . questionToken || node . exclamationToken ,
199+ ts . factory . createKeywordTypeNode ( ts . SyntaxKind . AnyKeyword ) ,
200+ node . initializer
201+ ) ;
202+ }
203+ }
204+
205+ return ts . visitEachChild ( node , visit , context ) ;
206+ } ;
207+
208+ return ( node ) => ts . visitNode ( node , visit ) as ts . SourceFile ;
209+ } ;
210+
211+ const result = ts . transform ( sourceFile , [ transformer ] ) ;
212+ const transformedSourceFile = result . transformed [ 0 ] ;
213+
214+ // Add header comment
215+ const headerComment = `/**
216+ * Auto-generated file with types stripped for better tree-shaking
217+ * DO NOT EDIT - Generated by strip-direct-transformer-types.ts
218+ */
219+
220+ ` ;
221+
222+ let code = printer . printFile ( transformedSourceFile ) ;
223+
224+ // Post-process to remove any remaining type references
225+ // Remove 'as PGxx.ParseResult' and 'as PGxx.Node' patterns
226+ code = code . replace ( / \s + a s \s + P G \d + \. ( P a r s e R e s u l t | N o d e ) / g, '' ) ;
227+
228+ // Remove any remaining type casts with version types
229+ code = code . replace ( / \s + a s \s + ( V \d + T y p e s | P G \d + ) \. [ A - Z a - z ] + / g, '' ) ;
230+
231+ return headerComment + code ;
232+ }
233+
234+ function processFile ( filePath : string ) : void {
235+ console . log ( `Processing ${ filePath } ...` ) ;
236+
237+ const fullPath = path . join ( process . cwd ( ) , filePath ) ;
238+ const sourceCode = fs . readFileSync ( fullPath , 'utf-8' ) ;
239+
240+ const sourceFile = ts . createSourceFile (
241+ filePath ,
242+ sourceCode ,
243+ ts . ScriptTarget . Latest ,
244+ true
245+ ) ;
246+
247+ const strippedCode = stripTypes ( sourceFile , filePath ) ;
248+
249+ // Create output directory if it doesn't exist
250+ const outputPath = path . join ( process . cwd ( ) , OUTPUT_DIR ) ;
251+ if ( ! fs . existsSync ( outputPath ) ) {
252+ fs . mkdirSync ( outputPath , { recursive : true } ) ;
253+ }
254+
255+ // Extract version info from path (e.g., v13-to-v17)
256+ const versionMatch = filePath . match ( / v ( \d + ) - t o - v ( \d + ) / ) ;
257+ if ( ! versionMatch ) {
258+ throw new Error ( `Could not extract version info from ${ filePath } ` ) ;
259+ }
260+
261+ const outputFileName = `${ versionMatch [ 0 ] } .ts` ;
262+ const outputFilePath = path . join ( outputPath , outputFileName ) ;
263+ fs . writeFileSync ( outputFilePath , strippedCode ) ;
264+
265+ console . log ( `✓ Written to ${ path . join ( OUTPUT_DIR , outputFileName ) } ` ) ;
266+ }
267+
268+ function main ( ) {
269+ console . log ( 'Stripping types from direct transformer files...\n' ) ;
270+
271+ for ( const file of DIRECT_TRANSFORMER_FILES ) {
272+ try {
273+ processFile ( file ) ;
274+ } catch ( error ) {
275+ console . error ( `Error processing ${ file } :` , error ) ;
276+ }
277+ }
278+
279+ console . log ( '\nDone! Stripped direct transformer files are in the versions/direct/ directory.' ) ;
280+ }
281+
282+ // Run the script
283+ main ( ) ;
0 commit comments