@@ -745,19 +745,65 @@ fn validate_package_specs_value(value: &serde_json::Value) -> Result<()> {
745745 None => return Ok ( ( ) ) ,
746746 } ;
747747
748+ let top_level_suffix = value
749+ . get ( "suffix" )
750+ . and_then ( |suffix| suffix. as_str ( ) )
751+ . unwrap_or ( ".js" )
752+ . to_string ( ) ;
753+ let mut seen_suffixes = std:: collections:: HashSet :: new ( ) ;
754+
748755 match specs {
749756 serde_json:: Value :: Array ( specs) => {
750757 for spec in specs {
751758 validate_package_spec_value ( spec) ?;
759+ if let Some ( suffix) = resolve_spec_suffix ( spec, & top_level_suffix) {
760+ let in_source = resolve_spec_in_source ( spec) ;
761+ if !seen_suffixes. insert ( ( suffix. clone ( ) , in_source) ) {
762+ return Err ( anyhow ! (
763+ "Duplicate package-spec suffix \" {suffix}\" is not allowed."
764+ ) ) ;
765+ }
766+ }
767+ }
768+ }
769+ serde_json:: Value :: Object ( _) => {
770+ validate_package_spec_value ( specs) ?;
771+ if let Some ( suffix) = resolve_spec_suffix ( specs, & top_level_suffix) {
772+ let in_source = resolve_spec_in_source ( specs) ;
773+ if !seen_suffixes. insert ( ( suffix. clone ( ) , in_source) ) {
774+ return Err ( anyhow ! (
775+ "Duplicate package-spec suffix \" {suffix}\" is not allowed."
776+ ) ) ;
777+ }
752778 }
753779 }
754- serde_json:: Value :: Object ( _) => validate_package_spec_value ( specs) ?,
755780 _ => { }
756781 }
757782
758783 Ok ( ( ) )
759784}
760785
786+ fn resolve_spec_suffix ( spec : & serde_json:: Value , top_level_suffix : & str ) -> Option < String > {
787+ if !spec. is_object ( ) {
788+ return None ;
789+ }
790+
791+ match spec. get ( "suffix" ) . and_then ( |suffix| suffix. as_str ( ) ) {
792+ Some ( suffix) => Some ( suffix. to_string ( ) ) ,
793+ None => Some ( top_level_suffix. to_string ( ) ) ,
794+ }
795+ }
796+
797+ fn resolve_spec_in_source ( spec : & serde_json:: Value ) -> bool {
798+ if !spec. is_object ( ) {
799+ return true ;
800+ }
801+
802+ spec. get ( "in-source" )
803+ . and_then ( |in_source| in_source. as_bool ( ) )
804+ . unwrap_or ( true )
805+ }
806+
761807fn validate_package_spec_value ( value : & serde_json:: Value ) -> Result < ( ) > {
762808 let module = match value. get ( "module" ) {
763809 Some ( module) => module,
@@ -837,6 +883,57 @@ pub mod tests {
837883 assert_eq ! ( config. get_suffix( spec) , ".mjs" ) ;
838884 }
839885
886+ #[ test]
887+ fn test_package_specs_duplicate_suffix_default ( ) {
888+ let json = r#"
889+ {
890+ "name": "dup-suffix-default",
891+ "sources": ".",
892+ "package-specs": [
893+ { "module": "commonjs", "in-source": true },
894+ { "module": "esmodule", "in-source": true }
895+ ]
896+ }
897+ "# ;
898+
899+ let error = Config :: new_from_json_string ( json) . unwrap_err ( ) . to_string ( ) ;
900+ assert ! ( error. contains( "Duplicate package-spec suffix" ) ) ;
901+ }
902+
903+ #[ test]
904+ fn test_package_specs_duplicate_suffix_explicit ( ) {
905+ let json = r#"
906+ {
907+ "name": "dup-suffix-explicit",
908+ "sources": ".",
909+ "package-specs": [
910+ { "module": "commonjs", "in-source": true, "suffix": ".mjs" },
911+ { "module": "esmodule", "in-source": true, "suffix": ".mjs" }
912+ ]
913+ }
914+ "# ;
915+
916+ let error = Config :: new_from_json_string ( json) . unwrap_err ( ) . to_string ( ) ;
917+ assert ! ( error. contains( "Duplicate package-spec suffix" ) ) ;
918+ }
919+
920+ #[ test]
921+ fn test_package_specs_duplicate_suffix_different_in_source_ok ( ) {
922+ let json = r#"
923+ {
924+ "name": "dup-suffix-different-in-source",
925+ "sources": ".",
926+ "package-specs": [
927+ { "module": "esmodule", "in-source": true, "suffix": ".res.js" },
928+ { "module": "esmodule", "in-source": false, "suffix": ".res.js" }
929+ ]
930+ }
931+ "# ;
932+
933+ let config = Config :: new_from_json_string ( json) . unwrap ( ) ;
934+ assert_eq ! ( config. get_package_specs( ) . len( ) , 2 ) ;
935+ }
936+
840937 #[ test]
841938 fn test_sources ( ) {
842939 let json = r#"
0 commit comments