Skip to content

Commit

Permalink
Update error message for invalid paths in config
Browse files Browse the repository at this point in the history
Summary:
Fixes #3663

But, instead of creating a directory from the config, it shows the detailed error message:

```
[ERROR] Config `/Users/alunyov/relay-examples/todo/package.json` is invalid:
 - The `artifactDirectory` does not exist at `./__generated__/relay2`.

```

Reviewed By: kassens

Differential Revision: D33341894

fbshipit-source-id: 10dd2d89208de62c0256fb95cdbd27577a7404de
  • Loading branch information
alunyov authored and facebook-github-bot committed Dec 29, 2021
1 parent 14550c8 commit 95f1ed8
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 52 deletions.
124 changes: 72 additions & 52 deletions compiler/crates/relay-compiler/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,9 @@ impl Config {

let config_file = match config_file {
ConfigFile::MultiProject(config) => *config,
ConfigFile::SingleProject(config) => MultiProjectConfigFile::from(config),
ConfigFile::SingleProject(config) => {
config.create_multi_project_config(&config_path)?
}
};

let MultiProjectConfigFile {
Expand Down Expand Up @@ -545,6 +547,9 @@ struct MultiProjectConfigFile {
#[derive(Debug, Serialize, Deserialize)]
#[serde(deny_unknown_fields, rename_all = "camelCase", default)]
pub struct SingleProjectConfigFile {
#[serde(skip)]
pub project_name: StringKey,

/// Path to schema.graphql
pub schema: PathBuf,

Expand Down Expand Up @@ -597,6 +602,7 @@ pub struct SingleProjectConfigFile {
impl Default for SingleProjectConfigFile {
fn default() -> Self {
Self {
project_name: "default".intern(),
schema: Default::default(),
src: Default::default(),
artifact_directory: Default::default(),
Expand All @@ -615,55 +621,73 @@ impl Default for SingleProjectConfigFile {
}

impl SingleProjectConfigFile {
fn get_common_root(&self, root_dir: PathBuf) -> Option<PathBuf> {
fn get_common_root(
&self,
root_dir: PathBuf,
) -> std::result::Result<PathBuf, ConfigValidationError> {
let mut paths = vec![];
if let Some(artifact_directory_path) = self.artifact_directory.clone() {
paths.push(join_and_canonicalize(
root_dir.clone(),
artifact_directory_path,
));
paths.push(
root_dir
.join(artifact_directory_path.clone())
.canonicalize()
.map_err(|_| ConfigValidationError::ArtifactDirectoryNotExistent {
path: artifact_directory_path,
})?,
);
}
paths.push(join_and_canonicalize(root_dir.clone(), self.src.clone()));
paths.push(join_and_canonicalize(root_dir.clone(), self.schema.clone()));
paths.extend(
self.schema_extensions
.iter()
.map(|dir| join_and_canonicalize(root_dir.clone(), dir.clone())),
paths.push(
root_dir
.join(self.src.clone())
.canonicalize()
.map_err(|_| ConfigValidationError::SourceNotExistent {
source_dir: self.src.clone(),
})?,
);
paths.push(
root_dir
.join(self.schema.clone())
.canonicalize()
.map_err(|_| ConfigValidationError::SchemaFileNotExistent {
project_name: self.project_name,
schema_file: self.schema.clone(),
})?,
);
for extension_dir in self.schema_extensions.iter() {
paths.push(
root_dir
.clone()
.join(extension_dir.clone())
.canonicalize()
.map_err(|_| ConfigValidationError::ExtensionDirNotExistent {
project_name: self.project_name,
extension_dir: extension_dir.clone(),
})?,
);
}
common_path::common_path_all(paths.iter().map(|path| path.as_path()))
.ok_or(ConfigValidationError::CommonPathNotFound)
}
}

fn join_and_canonicalize(root: PathBuf, path: PathBuf) -> PathBuf {
root.clone()
.join(path.clone())
.canonicalize()
.unwrap_or_else(|err| {
panic!(
"Expected to create a valid path from {:?} and {:?}. Error {:?}",
root, path, err
);
})
}

impl From<SingleProjectConfigFile> for MultiProjectConfigFile {
fn from(oss_config: SingleProjectConfigFile) -> MultiProjectConfigFile {
fn create_multi_project_config(self, config_path: &Path) -> Result<MultiProjectConfigFile> {
let current_dir = std::env::current_dir().unwrap();
let common_root_dir = oss_config.get_common_root(current_dir.clone()).expect(
"Could not find a common root directory for project sources, schemas, and extensions.",
);
let common_root_dir = self.get_common_root(current_dir.clone()).map_err(|err| {
Error::ConfigFileValidation {
config_path: config_path.to_path_buf(),
validation_errors: vec![err],
}
})?;

let default_project_name = "default".intern();
let project_config = ConfigFileProject {
output: oss_config.artifact_directory.map(|dir| {
output: self.artifact_directory.map(|dir| {
normalize_path_from_config(current_dir.clone(), common_root_dir.clone(), dir)
}),
schema: Some(normalize_path_from_config(
current_dir.clone(),
common_root_dir.clone(),
oss_config.schema,
self.schema,
)),
schema_extensions: oss_config
schema_extensions: self
.schema_extensions
.iter()
.map(|dir| {
Expand All @@ -674,42 +698,38 @@ impl From<SingleProjectConfigFile> for MultiProjectConfigFile {
)
})
.collect(),
persist: oss_config.persist_config,
persist: self.persist_config,
typegen_config: TypegenConfig {
language: oss_config.language.unwrap_or(TypegenLanguage::TypeScript),
custom_scalar_types: oss_config.custom_scalars,
eager_es_modules: oss_config.eager_es_modules,
language: self.language.unwrap_or(TypegenLanguage::TypeScript),
custom_scalar_types: self.custom_scalars.clone(),
eager_es_modules: self.eager_es_modules,
flow_typegen: FlowTypegenConfig {
no_future_proof_enums: oss_config.no_future_proof_enums,
no_future_proof_enums: self.no_future_proof_enums,
..Default::default()
},
..Default::default()
},
js_module_format: oss_config.js_module_format,
js_module_format: self.js_module_format,
..Default::default()
};

let mut projects = FnvIndexMap::default();
projects.insert(default_project_name, project_config);
projects.insert(self.project_name, project_config);

let mut sources = FnvIndexMap::default();
let src = normalize_path_from_config(
current_dir.clone(),
common_root_dir.clone(),
oss_config.src,
);
let src = normalize_path_from_config(current_dir, common_root_dir.clone(), self.src);

sources.insert(src, SourceSet::SourceSetName(default_project_name));
sources.insert(src, SourceSet::SourceSetName(self.project_name));

MultiProjectConfigFile {
Ok(MultiProjectConfigFile {
root: Some(common_root_dir),
projects,
sources,
excludes: oss_config.excludes,
is_dev_variable_name: oss_config.is_dev_variable_name,
codegen_command: oss_config.codegen_command,
excludes: self.excludes,
is_dev_variable_name: self.is_dev_variable_name,
codegen_command: self.codegen_command,
..Default::default()
}
})
}
}

Expand Down
14 changes: 14 additions & 0 deletions compiler/crates/relay-compiler/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,14 @@ pub enum ConfigValidationError {
schema_dir: PathBuf,
},

#[error(
"The `schemaExtensions` configured for project `{project_name}` does not exist at `{extension_dir}`."
)]
ExtensionDirNotExistent {
project_name: ProjectName,
extension_dir: PathBuf,
},

#[error(
"The `schema_dir` configured for project `{project_name}` to be `{schema_dir}` is not a directory."
)]
Expand All @@ -209,6 +217,12 @@ pub enum ConfigValidationError {
project_name: ProjectName,
error: regex::Error,
},

#[error("The `artifactDirectory` does not exist at `{path}`.")]
ArtifactDirectoryNotExistent { path: PathBuf },

#[error("Unable to find common path for directories in the config file.")]
CommonPathNotFound,
}

#[derive(Debug, Error)]
Expand Down

0 comments on commit 95f1ed8

Please sign in to comment.