@@ -11,6 +11,8 @@ interface FileMapping {
1111
1212interface ObfuscatorConfig {
1313 enabled : boolean ;
14+ preserveExtensions ?: string [ ] ;
15+ directories ?: string [ ] ;
1416}
1517
1618class BuildObfuscator {
@@ -21,7 +23,11 @@ class BuildObfuscator {
2123
2224 constructor ( config : ObfuscatorConfig = { enabled : true } ) {
2325 this . root = process . cwd ( ) ;
24- this . config = config ;
26+ this . config = {
27+ preserveExtensions : [ ".json" , ".css" , ".scss" ] ,
28+ directories : [ "src/components" , "src/layouts" , "src/lib" , "src/pages" ] ,
29+ ...config ,
30+ } ;
2531 }
2632
2733 private generateName ( extension : string , isRoute : boolean = false ) : string {
@@ -50,7 +56,7 @@ class BuildObfuscator {
5056 } while ( this . usedNames . has ( name ) ) ;
5157
5258 this . usedNames . add ( name ) ;
53-
59+
5460 return `${ name } ${ extension } ` ;
5561 }
5662
@@ -187,16 +193,18 @@ class BuildObfuscator {
187193 }
188194
189195 private processSourceFiles ( ) : void {
190- const directories = [ "src/components" , "src/layouts" , "src/lib" , "src/pages" ] ;
196+ const directories = this . config . directories || [ "src/components" , "src/layouts" , "src/lib" , "src/pages" ] ;
191197
192198 for ( const dir of directories ) {
193199 const dirPath = path . join ( this . root , dir ) ;
194200 if ( ! fs . existsSync ( dirPath ) ) continue ;
195201
196- const files = this . findFiles ( dirPath , [ ".astro" , ".ts" ] ) ;
202+ const files = this . findFiles ( dirPath , [ ".astro" , ".ts" , ".tsx" , ".js" , ".jsx" ] ) ;
197203
198204 for ( const file of files ) {
199- if ( path . basename ( file ) === "index.astro" ) continue ;
205+ if ( path . basename ( file ) === "index.astro" || this . config . preserveExtensions ?. includes ( path . extname ( file ) ) ) {
206+ continue ;
207+ }
200208
201209 const isRoute = file . includes ( "/pages/" ) ;
202210 const randomizedName = this . generateName ( path . extname ( file ) , isRoute ) ;
@@ -214,7 +222,7 @@ class BuildObfuscator {
214222 }
215223
216224 private updateFileContents ( ) : void {
217- const filesToUpdate = this . findFiles ( path . join ( this . root , "src" ) , [ ".astro" , ".ts" ] ) ;
225+ const filesToUpdate = this . findFiles ( path . join ( this . root , "src" ) , [ ".astro" , ".ts" , ".tsx" , ".js" , ".jsx" ] ) ;
218226
219227 for ( const file of filesToUpdate ) {
220228 let content = fs . readFileSync ( file , "utf-8" ) ;
@@ -223,6 +231,8 @@ class BuildObfuscator {
223231 for ( const [ originalPath , mapping ] of this . mappings ) {
224232 const originalName = path . basename ( mapping . original ) ;
225233 const newName = path . basename ( mapping . randomized ) ;
234+ const originalNameNoExt = path . basename ( mapping . original , path . extname ( mapping . original ) ) ;
235+ const newNameNoExt = path . basename ( mapping . randomized , path . extname ( mapping . randomized ) ) ;
226236
227237 if ( mapping . type === "favicon" ) {
228238 const faviconName = path . basename ( originalPath ) ;
@@ -235,31 +245,123 @@ class BuildObfuscator {
235245
236246 for ( const { from, to } of patterns ) {
237247 if ( content . includes ( from ) ) {
238- content = content . replace ( new RegExp ( this . escapeRegex ( from ) , "g" ) , to ) ;
248+ content = content . replaceAll ( from , to ) ;
239249 modified = true ;
240250 }
241251 }
242252 } else {
243253 const relDir = path . dirname ( mapping . original ) . replace ( path . join ( this . root , "src" ) , "" ) ;
244- const componentType = relDir . includes ( "/components" ) ? "components" : relDir . includes ( "/layouts" ) ? "layouts" : relDir . includes ( "/lib" ) ? "lib" : null ;
254+ const componentType = relDir . includes ( "/components" ) ? "components" : relDir . includes ( "/layouts" ) ? "layouts" : relDir . includes ( "/lib" ) ? "lib" : relDir . includes ( "/pages" ) ? "pages" : null ;
245255
246256 if ( componentType ) {
247- const aliasPattern = `@/${ componentType } /${ this . escapeRegex ( originalName ) } ` ;
248- const regex = new RegExp ( `(['"\`])${ aliasPattern } (['"\`])` , "g" ) ;
249- const newContent = content . replace ( regex , `$1@/${ componentType } /${ newName } $2` ) ;
257+ const patterns = `@/${ componentType } /${ this . escapeRegex ( originalName ) } ` ;
258+ const regex = new RegExp ( `(['"\`])${ patterns } (['"\`])` , "g" ) ;
259+ const replacement = `$1@/${ componentType } /${ newName } $2` ;
260+ let newContent = content . replace ( regex , replacement ) ;
261+ if ( newContent !== content ) {
262+ content = newContent ;
263+ modified = true ;
264+ }
265+
266+ const noExtPattern = `@/${ componentType } /${ this . escapeRegex ( originalNameNoExt ) } ` ;
267+ const noExtRegex = new RegExp ( `(['"\`])${ noExtPattern } (['"\`])` , "g" ) ;
268+ const noExtReplacement = `$1@/${ componentType } /${ newName } $2` ;
269+ newContent = content . replace ( noExtRegex , noExtReplacement ) ;
270+ if ( newContent !== content ) {
271+ content = newContent ;
272+ modified = true ;
273+ }
274+
275+ const scriptSrcPattern = `src\\s*=\\s*(['"\`])@/${ componentType } /${ this . escapeRegex ( originalName ) } \\1` ;
276+ const scriptSrcRegex = new RegExp ( scriptSrcPattern , "g" ) ;
277+ const scriptSrcReplacement = `src=$1@/${ componentType } /${ newName } $1` ;
278+ newContent = content . replace ( scriptSrcRegex , scriptSrcReplacement ) ;
279+ if ( newContent !== content ) {
280+ content = newContent ;
281+ modified = true ;
282+ }
283+
284+ const scriptSrcNoExtPattern = `src\\s*=\\s*(['"\`])@/${ componentType } /${ this . escapeRegex ( originalNameNoExt ) } \\1` ;
285+ const scriptSrcNoExtRegex = new RegExp ( scriptSrcNoExtPattern , "g" ) ;
286+ const scriptSrcNoExtReplacement = `src=$1@/${ componentType } /${ newName } $1` ;
287+ newContent = content . replace ( scriptSrcNoExtRegex , scriptSrcNoExtReplacement ) ;
288+ if ( newContent !== content ) {
289+ content = newContent ;
290+ modified = true ;
291+ }
292+
293+ const absoluteSrcPattern = `(src\\s*=\\s*['"\`])/src/${ componentType } /${ this . escapeRegex ( originalName ) } (['"\`])` ;
294+ const absoluteSrcRegex = new RegExp ( absoluteSrcPattern , "g" ) ;
295+ const absoluteSrcReplacement = `$1/src/${ componentType } /${ newName } $2` ;
296+ newContent = content . replace ( absoluteSrcRegex , absoluteSrcReplacement ) ;
297+ if ( newContent !== content ) {
298+ content = newContent ;
299+ modified = true ;
300+ }
301+
302+ const absoluteSrcNoExtPattern = `(src\\s*=\\s*['"\`])/src/${ componentType } /${ this . escapeRegex ( originalNameNoExt ) } (['"\`])` ;
303+ const absoluteSrcNoExtRegex = new RegExp ( absoluteSrcNoExtPattern , "g" ) ;
304+ const absoluteSrcNoExtReplacement = `$1/src/${ componentType } /${ newName } $2` ;
305+ newContent = content . replace ( absoluteSrcNoExtRegex , absoluteSrcNoExtReplacement ) ;
250306 if ( newContent !== content ) {
251307 content = newContent ;
252308 modified = true ;
253309 }
254310 }
255311
256- if ( mapping . type === "route" ) {
257- const originalFileName = path . basename ( mapping . original , path . extname ( mapping . original ) ) ;
258- const newFileName = path . basename ( mapping . randomized , path . extname ( mapping . randomized ) ) ;
312+ const currentFileDir = path . dirname ( file ) ;
313+ let relativePath = path . relative ( currentFileDir , mapping . original ) ;
314+ relativePath = relativePath . replace ( / \\ / g , "/" ) ;
259315
316+ if ( ! relativePath . startsWith ( "." ) ) {
317+ relativePath = `./${ relativePath } ` ;
318+ }
319+
320+ const regex = new RegExp ( `(['"\`])${ this . escapeRegex ( relativePath ) } (['"\`])` , "g" ) ;
321+ let newRelativePath = path . relative ( currentFileDir , mapping . randomized ) ;
322+ newRelativePath = newRelativePath . replace ( / \\ / g, "/" ) ;
323+ if ( ! newRelativePath . startsWith ( "." ) ) {
324+ newRelativePath = `./${ newRelativePath } ` ;
325+ }
326+
327+ let newContent = content . replace ( regex , `$1${ newRelativePath } $2` ) ;
328+ if ( newContent !== content ) {
329+ content = newContent ;
330+ modified = true ;
331+ }
332+
333+ const relativePathNoExt = relativePath . replace ( path . extname ( relativePath ) , "" ) ;
334+ const noExtRegex = new RegExp ( `(['"\`])${ this . escapeRegex ( relativePathNoExt ) } (['"\`])` , "g" ) ;
335+ const newRelativePathWithExt = newRelativePath ;
336+
337+ newContent = content . replace ( noExtRegex , `$1${ newRelativePathWithExt } $2` ) ;
338+ if ( newContent !== content ) {
339+ content = newContent ;
340+ modified = true ;
341+ }
342+
343+ const scriptRelativePattern = `src\\s*=\\s*(['"\`])${ this . escapeRegex ( relativePath ) } \\1` ;
344+ const scriptRelativeRegex = new RegExp ( scriptRelativePattern , "g" ) ;
345+ newContent = content . replace ( scriptRelativeRegex , `src=$1${ newRelativePath } $1` ) ;
346+ if ( newContent !== content ) {
347+ content = newContent ;
348+ modified = true ;
349+ }
350+
351+ const scriptRelativeNoExtPattern = `src\\s*=\\s*(['"\`])${ this . escapeRegex ( relativePathNoExt ) } \\1` ;
352+ const scriptRelativeNoExtRegex = new RegExp ( scriptRelativeNoExtPattern , "g" ) ;
353+ newContent = content . replace ( scriptRelativeNoExtRegex , `src=$1${ newRelativePath } $1` ) ;
354+ if ( newContent !== content ) {
355+ content = newContent ;
356+ modified = true ;
357+ }
358+
359+ if ( mapping . type === "route" ) {
260360 const routeReplacements = [
261- { pattern : new RegExp ( `(['"\`])/${ this . escapeRegex ( originalFileName ) } (['"\`])` , "g" ) , replacement : `$1/${ newFileName } $2` } ,
262- { pattern : new RegExp ( `(href\\s*=\\s*['"\`])/${ this . escapeRegex ( originalFileName ) } (['"\`])` , "g" ) , replacement : `$1/${ newFileName } $2` } ,
361+ { pattern : new RegExp ( `(['"\`])/${ this . escapeRegex ( originalNameNoExt ) } (['"\`])` , "g" ) , replacement : `$1/${ newNameNoExt } $2` } ,
362+ { pattern : new RegExp ( `(href\\s*=\\s*['"\`])/${ this . escapeRegex ( originalNameNoExt ) } (['"\`])` , "g" ) , replacement : `$1/${ newNameNoExt } $2` } ,
363+ { pattern : new RegExp ( `===\\s*['"\`]/${ this . escapeRegex ( originalNameNoExt ) } ['"\`]` , "g" ) , replacement : `=== '/${ newNameNoExt } '` } ,
364+ { pattern : new RegExp ( `['"\`]/${ this . escapeRegex ( originalNameNoExt ) } ['"\`]\\s*===` , "g" ) , replacement : `'/${ newNameNoExt } ' ===` } ,
263365 ] ;
264366
265367 for ( const { pattern, replacement } of routeReplacements ) {
@@ -285,6 +387,7 @@ class BuildObfuscator {
285387 for ( const mapping of sortedMappings ) {
286388 if ( fs . existsSync ( mapping . original ) ) {
287389 fs . renameSync ( mapping . original , mapping . randomized ) ;
390+ console . log ( `Renamed: ${ path . relative ( this . root , mapping . original ) } → ${ path . basename ( mapping . randomized ) } ` ) ;
288391 }
289392 }
290393 }
@@ -303,19 +406,29 @@ class BuildObfuscator {
303406
304407 this . processFavicons ( ) ;
305408 this . processSourceFiles ( ) ;
409+
410+ console . log ( `Found ${ this . mappings . size } files to obfuscate` ) ;
411+
306412 this . updateFileContents ( ) ;
307413 this . renameFiles ( ) ;
308414
309415 const routeMappings : Record < string , string > = { } ;
310416 for ( const [ , mapping ] of this . mappings ) {
311417 if ( mapping . type === "route" ) {
312- const originalRoute = "/" + path . basename ( mapping . original , path . extname ( mapping . original ) ) ;
313- const newRoute = "/" + path . basename ( mapping . randomized , path . extname ( mapping . randomized ) ) ;
418+ const originalRoute = `/ ${ path . basename ( mapping . original , path . extname ( mapping . original ) ) } ` ;
419+ const newRoute = `/ ${ path . basename ( mapping . randomized , path . extname ( mapping . randomized ) ) } ` ;
314420 routeMappings [ originalRoute ] = newRoute ;
315421 }
316422 }
317423
318- console . log ( "Routes updated:" , routeMappings ) ;
424+ if ( Object . keys ( routeMappings ) . length > 0 ) {
425+ console . log ( "Routes updated:" ) ;
426+ for ( const [ old , newRoute ] of Object . entries ( routeMappings ) ) {
427+ console . log ( ` ${ old } → ${ newRoute } ` ) ;
428+ }
429+ }
430+
431+ console . log ( "Obfuscation completed!" ) ;
319432 }
320433
321434 public async revert ( ) : Promise < void > {
0 commit comments