@@ -4,17 +4,58 @@ import * as fs from 'fs';
44import * as path from 'path' ;
55import { findMatchingFiles } from './utils' ;
66
7+ const replaceValuesForKey = ( content : any , key : string , oldValue : string , newValue : string ) : { result : any , count : number } => {
8+ if ( typeof content !== 'object' || content === null ) {
9+ return { result : content , count : 0 }
10+ }
11+
12+ if ( Array . isArray ( content ) ) {
13+ const { result, count } = content . reduce ( ( acc , item ) => {
14+ const { result : itemResult , count : itemCount } = replaceValuesForKey ( item , key , oldValue , newValue )
15+ return {
16+ result : [ ...acc . result , itemResult ] ,
17+ count : acc . count + itemCount
18+ }
19+ } , { result : [ ] as any [ ] , count : 0 } )
20+ return { result, count }
21+ }
22+
23+ let regex = new RegExp ( oldValue , 'g' ) ;
24+ let totalCount = 0 ;
25+
26+ const result = Object . keys ( content ) . reduce ( ( acc , objKey ) => {
27+ const value = content [ objKey ]
28+ if ( ( objKey === key ) && typeof value === 'string' ) {
29+ const matches = value . match ( regex ) ;
30+ const count = matches ? matches . length : 0 ;
31+ totalCount += count ;
32+ acc [ objKey ] = value . replace ( regex , newValue )
33+ } else if ( typeof value === 'object' && value !== null ) {
34+ const { result : nestedResult , count } = replaceValuesForKey ( value , key , oldValue , newValue )
35+ totalCount += count ;
36+ acc [ objKey ] = nestedResult
37+ } else {
38+ acc [ objKey ] = value
39+ }
40+ return acc
41+ } , { } as Record < string , any > )
42+
43+ return { result, count : totalCount }
44+ }
45+
746/**
847 * Replace all occurrences of provided links with localised version in the given content
948 */
10- function replaceLinks ( content : string , oldUrl : string , newUrl : string ) : string {
11- return content . replace ( new RegExp ( oldUrl , 'g' ) , newUrl ) ;
49+ function replaceLinks ( content : string , oldUrl : string , newUrl : string ) : { content : string , count : number } {
50+ let parsed = JSON . parse ( content ) ;
51+ let { result : updated , count } = replaceValuesForKey ( parsed , 'description' , oldUrl , newUrl )
52+ return { content : JSON . stringify ( updated , null , 2 ) , count }
1253}
1354
1455/**
1556 * Process a single OpenAPI file
1657 */
17- function processFile ( filePath : string , oldUrl : string , newUrl : string ) : void {
58+ function processFile ( filePath : string , oldUrl : string , newUrl : string , outputPath ?: string ) : void {
1859 try {
1960 console . log ( `Processing: ${ filePath } ` ) ;
2061
@@ -36,22 +77,35 @@ function processFile(filePath: string, oldUrl: string, newUrl: string): void {
3677 process . exit ( 1 ) ;
3778 }
3879
39- // Count occurrences before replacement
40- const matches = content . match ( new RegExp ( oldUrl , 'g' ) ) ;
41- const count = matches ? matches . length : 0 ;
80+ // Replace links and get the actual count
81+ const { content : updatedContent , count } = replaceLinks ( content , oldUrl , newUrl ) ;
4282
4383 if ( count === 0 ) {
4484 console . log ( ` ℹ️ No links to replace in ${ filePath } ` ) ;
4585 return ;
4686 }
4787
48- // Replace links
49- const updatedContent = replaceLinks ( content , oldUrl , newUrl ) ;
50-
51- // Write back to file
52- fs . writeFileSync ( filePath , updatedContent , 'utf-8' ) ;
88+ // Determine output file path
89+ let outputFilePath : string ;
90+ if ( outputPath ) {
91+ // If output path is provided, save files there
92+ const fileName = path . basename ( filePath ) ;
93+
94+ // Ensure output directory exists
95+ if ( ! fs . existsSync ( outputPath ) ) {
96+ fs . mkdirSync ( outputPath , { recursive : true } ) ;
97+ }
98+
99+ outputFilePath = path . join ( outputPath , fileName ) ;
100+ console . log ( ` ✅ Replaced ${ count } occurrence(s), saved to ${ outputFilePath } ` ) ;
101+ } else {
102+ // Otherwise, save in place
103+ outputFilePath = filePath ;
104+ console . log ( ` ✅ Replaced ${ count } occurrence(s) in ${ filePath } ` ) ;
105+ }
53106
54- console . log ( ` ✅ Replaced ${ count } occurrence(s) in ${ filePath } ` ) ;
107+ // Write to file
108+ fs . writeFileSync ( outputFilePath , updatedContent , 'utf-8' ) ;
55109 } catch ( e ) {
56110 console . error ( ` ❌ Error processing ${ filePath } :` , ( e as Error ) . message ) ;
57111 process . exit ( 1 ) ;
@@ -68,27 +122,34 @@ export function main(): number {
68122 if ( args . length < 4 ) {
69123 console . error ( '❌ Error: Missing required arguments' ) ;
70124 console . error ( '' ) ;
71- console . error ( 'Usage: cd .github/scripts && npm run replace-links -- <directory> <pattern> <old_url> <new_url>' ) ;
125+ console . error ( 'Usage: cd .github/scripts && npm run replace-links -- <directory> <pattern> <old_url> <new_url> [output] ' ) ;
72126 console . error ( '' ) ;
73127 console . error ( 'Arguments:' ) ;
74128 console . error ( ' <directory> - Path to directory containing JSON files' ) ;
75129 console . error ( ' <pattern> - Regex pattern to match filenames' ) ;
76130 console . error ( ' <old_url> - URL to replace' ) ;
77131 console . error ( ' <new_url> - Replacement URL' ) ;
132+ console . error ( ' [output] - Optional output directory (if not provided, files are saved in place)' ) ;
78133 console . error ( '' ) ;
79134 console . error ( 'Examples:' ) ;
80- console . error ( ' cd .github/scripts && npm run replace-links -- "openapi" "openapi.*\\.json" "https://developer.box.com" "https://ja.developer.box.com"' ) ;
135+ console . error ( ' cd .github/scripts && npm run replace-links -- "../../openapi" "openapi.*\\.json" "https://developer.box.com" "https://ja.developer.box.com"' ) ;
136+ console . error ( ' cd .github/scripts && npm run replace-links -- "../../openapi" "openapi.*\\.json" "https://developer.box.com" "https://ja.developer.box.com" "output"' ) ;
81137 return 1 ;
82138 }
83139
84140 const directoryPath = args [ 0 ] ;
85141 const pattern = args [ 1 ] ;
86142 const oldUrl = args [ 2 ] ;
87143 const newUrl = args [ 3 ] ;
144+ const outputPath = args [ 4 ] ; // Optional fifth argument
88145
89146 console . log ( `Replacing "${ oldUrl } " with "${ newUrl } "` ) ;
90147 console . log ( `Searching in directory: ${ directoryPath } ` ) ;
91- console . log ( `Pattern: ${ pattern } \n` ) ;
148+ console . log ( `Pattern: ${ pattern } ` ) ;
149+ if ( outputPath ) {
150+ console . log ( `Output directory: ${ outputPath } ` ) ;
151+ }
152+ console . log ( ) ;
92153
93154 // Find matching files
94155 const filePaths = findMatchingFiles ( directoryPath , pattern ) ;
@@ -104,7 +165,7 @@ export function main(): number {
104165
105166 // Process each file
106167 for ( const filePath of filePaths ) {
107- processFile ( filePath , oldUrl , newUrl ) ;
168+ processFile ( filePath , oldUrl , newUrl , outputPath ) ;
108169 }
109170
110171 console . log ( '\n✅ All files processed successfully!' ) ;
@@ -116,4 +177,3 @@ if (require.main === module) {
116177 const exitCode = main ( ) ;
117178 process . exit ( exitCode ) ;
118179}
119-
0 commit comments