-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Make codemod available in oss compiler
Reviewed By: captbaritone Differential Revision: D64006449 fbshipit-source-id: 078ba9dc814cfda39356ea9e1142fe41d40c24e9
- Loading branch information
1 parent
6e4c120
commit bddf8a4
Showing
8 changed files
with
267 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
# @generated by autocargo from //relay/oss/crates/relay-codemod:relay-codemod | ||
|
||
[package] | ||
name = "relay-codemod" | ||
version = "0.0.0" | ||
authors = ["Facebook"] | ||
edition = "2021" | ||
repository = "https://github.com/facebook/relay" | ||
license = "MIT" | ||
|
||
[dependencies] | ||
clap = { version = "3.2.25", features = ["derive", "env", "regex", "unicode", "wrap_help"] } | ||
graphql-ir = { path = "../graphql-ir" } | ||
log = { version = "0.4.22", features = ["kv_unstable"] } | ||
lsp-types = "0.94.1" | ||
relay-compiler = { path = "../relay-compiler" } | ||
relay-lsp = { path = "../relay-lsp" } | ||
relay-transforms = { path = "../relay-transforms" } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
/* | ||
* Copyright (c) Meta Platforms, Inc. and affiliates. | ||
* | ||
* This source code is licensed under the MIT license found in the | ||
* LICENSE file in the root directory of this source tree. | ||
*/ | ||
|
||
use std::fs; | ||
use std::sync::Arc; | ||
|
||
use clap::ValueEnum; | ||
use graphql_ir::Program; | ||
use log::info; | ||
use lsp_types::CodeActionOrCommand; | ||
use lsp_types::TextEdit; | ||
use lsp_types::Url; | ||
use relay_compiler::config::Config; | ||
use relay_transforms::fragment_alias_directive; | ||
use relay_transforms::validate_unused_variables; | ||
|
||
#[derive(ValueEnum, Debug, Clone)] | ||
pub enum AvailableCodemod { | ||
/// Removes fields that are unused in the GraphQL response | ||
RemoveUnusedVariables, | ||
|
||
/// Marks unaliased conditional fragment spreads as @dangerously_unaliased_fixme | ||
MarkDangerousConditionalFragmentSpreads, | ||
} | ||
|
||
pub async fn run_codemod( | ||
program: Arc<Program>, | ||
config: Arc<Config>, | ||
codemod: AvailableCodemod, | ||
) -> Result<(), std::io::Error> { | ||
let diagnostics = match &codemod { | ||
AvailableCodemod::RemoveUnusedVariables => match validate_unused_variables(&program) { | ||
Ok(_) => vec![], | ||
Err(e) => e, | ||
}, | ||
AvailableCodemod::MarkDangerousConditionalFragmentSpreads => { | ||
match fragment_alias_directive(&program, true) { | ||
Ok(_) => vec![], | ||
Err(e) => e, | ||
} | ||
} | ||
}; | ||
let actions = relay_lsp::diagnostics_to_code_actions(config, &diagnostics); | ||
|
||
info!( | ||
"Codemod {:?} ran and found {} changes to make.", | ||
codemod, | ||
actions.len() | ||
); | ||
apply_actions(actions)?; | ||
Ok(()) | ||
} | ||
|
||
fn apply_actions(actions: Vec<CodeActionOrCommand>) -> Result<(), std::io::Error> { | ||
let mut collected_changes = std::collections::HashMap::new(); | ||
|
||
// Collect all the changes into a map of file-to-list-of-changes | ||
for action in actions { | ||
if let CodeActionOrCommand::CodeAction(code_action) = action { | ||
if let Some(changes) = code_action.edit.unwrap().changes { | ||
for (file, changes) in changes { | ||
collected_changes | ||
.entry(file) | ||
.or_insert_with(Vec::new) | ||
.extend(changes); | ||
} | ||
} | ||
} | ||
} | ||
|
||
for (file, mut changes) in collected_changes { | ||
sort_changes(&file, &mut changes)?; | ||
|
||
// Read file into memory and apply changes | ||
let file_contents: String = fs::read_to_string(file.path())?; | ||
let mut lines: Vec<String> = file_contents.lines().map(|s| s.to_string()).collect(); | ||
for change in &changes { | ||
let line = change.range.start.line as usize; | ||
let mut new_line = String::new(); | ||
new_line.push_str(&lines[line][..change.range.start.character as usize]); | ||
new_line.push_str(&change.new_text); | ||
new_line.push_str(&lines[line][change.range.end.character as usize..]); | ||
lines[line] = new_line; | ||
} | ||
|
||
// Write file back out | ||
let new_file_contents = lines.join("\n"); | ||
fs::write(file.path(), new_file_contents)?; | ||
|
||
info!("Applied {} changes to {}", changes.len(), file.path()); | ||
} | ||
Ok(()) | ||
} | ||
|
||
fn sort_changes(url: &Url, changes: &mut Vec<TextEdit>) -> Result<(), std::io::Error> { | ||
// Now we have all the changes for this file. Sort them by position within the file, end of file first | ||
// This way the changes are applied in reverse order, so we don't have to worry about altering the positions of the remaining changes | ||
changes.sort_by(|a, b| b.range.start.cmp(&a.range.start)); | ||
|
||
// Verify none of the changes overlap | ||
let mut prev_change: Option<&TextEdit> = None; | ||
for change in changes { | ||
if let Some(prev_change) = prev_change { | ||
if change.range.end.line > prev_change.range.start.line | ||
|| (change.range.end.line == prev_change.range.start.line | ||
&& change.range.end.character > prev_change.range.start.character) | ||
{ | ||
return Err(std::io::Error::new( | ||
std::io::ErrorKind::Other, | ||
format!( | ||
"Codemod produced changes that overlap: File {}, changes: {:?} vs {:?}", | ||
url.path(), | ||
change, | ||
prev_change | ||
), | ||
)); | ||
} | ||
} | ||
prev_change = Some(change); | ||
} | ||
Ok(()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
/* | ||
* Copyright (c) Meta Platforms, Inc. and affiliates. | ||
* | ||
* This source code is licensed under the MIT license found in the | ||
* LICENSE file in the root directory of this source tree. | ||
*/ | ||
|
||
#![deny(warnings)] | ||
#![deny(rust_2018_idioms)] | ||
#![deny(clippy::all)] | ||
|
||
mod codemod; | ||
|
||
pub use crate::codemod::run_codemod; | ||
pub use crate::codemod::AvailableCodemod; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
/* | ||
* Copyright (c) Meta Platforms, Inc. and affiliates. | ||
* | ||
* This source code is licensed under the MIT license found in the | ||
* LICENSE file in the root directory of this source tree. | ||
*/ | ||
|
||
use std::sync::Arc; | ||
use std::sync::Mutex; | ||
|
||
use common::PerfLogger; | ||
use relay_transforms::Programs; | ||
|
||
use crate::compiler::Compiler; | ||
use crate::compiler_state::CompilerState; | ||
use crate::config::Config; | ||
use crate::NoopArtifactWriter; | ||
|
||
pub async fn get_programs<TPerfLogger: PerfLogger + 'static>( | ||
mut config: Config, | ||
perf_logger: Arc<TPerfLogger>, | ||
) -> (Arc<Programs>, CompilerState, Arc<Config>) { | ||
let raw_programs: Arc<Mutex<Vec<Arc<Programs>>>> = Arc::new(Mutex::new(vec![])); | ||
let raw_programs_cloned = raw_programs.clone(); | ||
|
||
config.compile_everything = true; | ||
config.generate_virtual_id_file_name = None; | ||
config.artifact_writer = Box::new(NoopArtifactWriter); | ||
config.generate_extra_artifacts = Some(Box::new( | ||
move |_config, _project_config, _schema, programs, _artifacts| { | ||
raw_programs_cloned | ||
.lock() | ||
.unwrap() | ||
.push(Arc::new(programs.clone())); | ||
vec![] | ||
}, | ||
)); | ||
let config = Arc::new(config); | ||
|
||
let compiler = Compiler::new(Arc::clone(&config), Arc::clone(&perf_logger)); | ||
let compiler_state = match compiler.compile().await { | ||
Ok(compiler_state) => compiler_state, | ||
Err(e) => { | ||
eprintln!("{}", e); | ||
std::process::exit(1); | ||
} | ||
}; | ||
let programs = { | ||
let guard = raw_programs.lock().unwrap(); | ||
if guard.len() < 1 { | ||
eprintln!("Failed to extract program from compiler state"); | ||
std::process::exit(1); | ||
} | ||
guard[0].clone() | ||
}; | ||
(Arc::clone(&programs), compiler_state, Arc::clone(&config)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters