2020-12-07 11:10:46 +00:00
/ * *
2021-12-30 23:08:43 +00:00
* Copyright ( c ) Meta Platforms , Inc . and affiliates .
2020-12-07 11:10:46 +00:00
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree .
*
* @ format
* /
'use strict' ;
const fs = require ( 'fs' ) ;
2023-11-06 20:59:38 +00:00
const https = require ( 'https' ) ;
2020-12-07 11:10:46 +00:00
const os = require ( 'os' ) ;
2023-11-06 20:59:38 +00:00
const path = require ( 'path' ) ;
const { exec } = require ( 'shelljs' ) ;
2020-12-07 11:10:46 +00:00
const yargs = require ( 'yargs' ) ;
const googleJavaFormatUrl =
'https://github.com/google/google-java-format/releases/download/google-java-format-1.7/google-java-format-1.7-all-deps.jar' ;
const googleJavaFormatPath = path . join (
os . tmpdir ( ) ,
'google-java-format-all-deps.jar' ,
) ;
const javaFilesCommand = 'find ./ReactAndroid -name "*.java"' ;
function _download ( url , downloadPath , resolve , reject , redirectCount ) {
https . get ( url , response => {
switch ( response . statusCode ) {
case 302 : //Permanent Redirect
if ( redirectCount === 0 ) {
throw new Error (
` Unhandled response code (HTTP ${ response . statusCode } ) while retrieving google-java-format binary from ${ url } ` ,
) ;
}
_download (
response . headers . location ,
downloadPath ,
resolve ,
reject ,
redirectCount - 1 ,
) ;
break ;
case 200 : //OK
const file = fs . createWriteStream ( downloadPath ) ;
response . pipe ( file ) ;
file . on ( 'finish' , ( ) => file . close ( ( ) => resolve ( ) ) ) ;
break ;
default :
reject (
` Unhandled response code (HTTP ${ response . statusCode } ) while retrieving google-java-format binary from ${ url } ` ,
) ;
}
} ) ;
}
function download ( url , downloadPath ) {
return new Promise ( ( resolve , reject ) => {
_download ( url , downloadPath , resolve , reject , 1 ) ;
} ) ;
}
function filesWithLintingIssues ( ) {
const proc = exec (
` java -jar ${ googleJavaFormatPath } --dry-run $ ( ${ javaFilesCommand } ) ` ,
{ silent : true } ,
) ;
if ( proc . code !== 0 ) {
throw new Error ( proc . stderr ) ;
}
return proc . stdout . split ( '\n' ) . filter ( x => x ) ;
}
function unifiedDiff ( file ) {
const lintedProc = exec (
` java -jar ${ googleJavaFormatPath } --set-exit-if-changed ${ file } ` ,
{ silent : true } ,
) ;
//Exit code 1 indicates lint violations, which is what we're expecting
if ( lintedProc . code !== 1 ) {
throw new Error ( lintedProc . stderr ) ;
}
const diffProc = lintedProc . exec ( ` diff -U 0 ${ file } - ` , { silent : true } ) ;
//Exit code 0 if inputs are the same, 1 if different, 2 if trouble.
if ( diffProc . code !== 0 && diffProc . code !== 1 ) {
throw new Error ( diffProc . stderr ) ;
}
return {
file ,
diff : diffProc . stdout ,
} ;
}
function extractRangeInformation ( range ) {
//eg;
// @@ -54 +54,2 @@
// @@ -1,3 +1,9 @@
const regex = /^@@ [-+](\d+,?\d+) [-+](\d+,?\d+) @@$/ ;
const match = regex . exec ( range ) ;
if ( match ) {
const original = match [ 1 ] . split ( ',' ) ;
const updated = match [ 2 ] . split ( ',' ) ;
return {
original : {
line : parseInt ( original [ 0 ] , 10 ) ,
lineCount : parseInt ( original [ 1 ] , 10 ) || 1 ,
} ,
updated : {
line : parseInt ( updated [ 0 ] , 10 ) ,
lineCount : parseInt ( updated [ 1 ] , 10 ) || 1 ,
} ,
} ;
}
}
function parseChanges ( file , diff ) {
let group = null ;
const groups = [ ] ;
diff . split ( '\n' ) . forEach ( line => {
const range = extractRangeInformation ( line ) ;
if ( range ) {
group = {
range ,
description : [ line ] ,
} ;
groups . push ( group ) ;
} else if ( group ) {
group . description . push ( line ) ;
}
} ) ;
return groups . map ( x => ( {
file ,
line : x . range . original . line ,
lineCount : x . range . original . lineCount ,
description : x . description . join ( '\n' ) ,
} ) ) ;
}
async function main ( ) {
const { argv } = yargs
. scriptName ( 'lint-java' )
. usage ( 'Usage: $0 [options]' )
. command (
'$0' ,
'Downloads the google-java-format package and reformats Java source code to comply with Google Java Style.\n\nSee https://github.com/google/google-java-format' ,
)
. option ( 'check' , {
type : 'boolean' ,
description :
'Outputs a list of files with lint violations.\nExit code is set to 1 if there are violations, otherwise 0.\nDoes not reformat lint issues.' ,
} )
. option ( 'diff' , {
type : 'boolean' ,
description :
'Outputs a diff of the lint fix changes in json format.\nDoes not reformat lint issues.' ,
} ) ;
await download ( googleJavaFormatUrl , googleJavaFormatPath ) ;
if ( argv . check ) {
const files = filesWithLintingIssues ( ) ;
files . forEach ( x => console . log ( x ) ) ;
process . exit ( files . length === 0 ? 0 : 1 ) ;
return ;
}
if ( argv . diff ) {
const suggestions = filesWithLintingIssues ( )
. map ( unifiedDiff )
. filter ( x => x )
. map ( x => parseChanges ( x . file , x . diff ) )
. reduce ( ( accumulator , current ) => accumulator . concat ( current ) , [ ] ) ;
console . log ( JSON . stringify ( suggestions ) ) ;
return ;
}
const proc = exec (
` java -jar ${ googleJavaFormatPath } --set-exit-if-changed --replace $ ( ${ javaFilesCommand } ) ` ,
) ;
process . exit ( proc . code ) ;
}
( async ( ) => {
await main ( ) ;
} ) ( ) ;