From e63ccb5cbb479df0f0e5010065ff57bbf4ca238f Mon Sep 17 00:00:00 2001 From: nsfisis Date: Sat, 27 Jun 2026 19:44:01 +0900 Subject: fix(json): encode empty stdClass fields as {} not [] Empty PhpMixed::Array now serializes as [], so the several places that build a PHP `new \stdClass` via an empty Array were emitting [] where Composer writes {}. Use PhpMixed::Object for those empty-object cases: - InitCommand: require / require-dev - Locker::fixupJsonDataType: stability-flags / platform / platform-dev - JsonConfigSource fallback: require/config keys that must stay objects Align the affected test expectations with Composer: JsonFile::read() decodes with assoc=true, collapsing the on-disk {} back to [], so reads expect []. Co-Authored-By: Claude Opus 4.8 (1M context) --- crates/shirabe/src/command/init_command.rs | 8 ++++---- crates/shirabe/src/config/json_config_source.rs | 9 +++++---- crates/shirabe/src/package/locker.rs | 3 ++- 3 files changed, 11 insertions(+), 9 deletions(-) (limited to 'crates/shirabe/src') diff --git a/crates/shirabe/src/command/init_command.rs b/crates/shirabe/src/command/init_command.rs index ab0e52c..88a2934 100644 --- a/crates/shirabe/src/command/init_command.rs +++ b/crates/shirabe/src/command/init_command.rs @@ -226,8 +226,8 @@ impl Command for InitCommand { .unwrap_or_default(); let formatted = self.format_requirements(req_list)?; if formatted.is_empty() { - // PHP: new \stdClass — represented as an empty IndexMap (JSON object) - PhpMixed::Array(IndexMap::new()) + // PHP: new \stdClass — empty JSON object + PhpMixed::Object(IndexMap::new()) } else { PhpMixed::Array( formatted @@ -238,7 +238,7 @@ impl Command for InitCommand { } } else { // PHP: new \stdClass - PhpMixed::Array(IndexMap::new()) + PhpMixed::Object(IndexMap::new()) }; options.insert("require".to_string(), require_value); @@ -254,7 +254,7 @@ impl Command for InitCommand { .unwrap_or_default(); let formatted = self.format_requirements(req_list)?; let value = if formatted.is_empty() { - PhpMixed::Array(IndexMap::new()) + PhpMixed::Object(IndexMap::new()) } else { PhpMixed::Array( formatted diff --git a/crates/shirabe/src/config/json_config_source.rs b/crates/shirabe/src/config/json_config_source.rs index 2509239..1ce51ad 100644 --- a/crates/shirabe/src/config/json_config_source.rs +++ b/crates/shirabe/src/config/json_config_source.rs @@ -98,7 +98,7 @@ impl JsonConfigSource { && inner.is_empty() { // PHP: $config[$prop] = new \stdClass; - map.insert(prop.to_string(), PhpMixed::Array(IndexMap::new())); + map.insert(prop.to_string(), PhpMixed::Object(IndexMap::new())); } } for prop in ["psr-0", "psr-4"] { @@ -108,14 +108,15 @@ impl JsonConfigSource { && let Some(PhpMixed::Array(inner_map)) = autoload_map.get(prop) && inner_map.is_empty() { - autoload_map.insert(prop.to_string(), PhpMixed::Array(IndexMap::new())); + autoload_map.insert(prop.to_string(), PhpMixed::Object(IndexMap::new())); } if let Some(autoload_dev) = map.get_mut("autoload-dev") && let PhpMixed::Array(autoload_dev_map) = autoload_dev && let Some(PhpMixed::Array(inner_map)) = autoload_dev_map.get(prop) && inner_map.is_empty() { - autoload_dev_map.insert(prop.to_string(), PhpMixed::Array(IndexMap::new())); + autoload_dev_map + .insert(prop.to_string(), PhpMixed::Object(IndexMap::new())); } } } @@ -136,7 +137,7 @@ impl JsonConfigSource { && let Some(PhpMixed::Array(inner_map)) = cfg_map.get(prop) && inner_map.is_empty() { - cfg_map.insert(prop.to_string(), PhpMixed::Array(IndexMap::new())); + cfg_map.insert(prop.to_string(), PhpMixed::Object(IndexMap::new())); } } self.file.borrow().write(config)?; diff --git a/crates/shirabe/src/package/locker.rs b/crates/shirabe/src/package/locker.rs index df5e1de..1f123f1 100644 --- a/crates/shirabe/src/package/locker.rs +++ b/crates/shirabe/src/package/locker.rs @@ -705,7 +705,8 @@ impl Locker { }) .unwrap_or(false); if should_replace { - lock_data.insert(key.to_string(), PhpMixed::Array(IndexMap::new())); + // PHP: new \stdClass() — empty JSON object + lock_data.insert(key.to_string(), PhpMixed::Object(IndexMap::new())); } } -- cgit v1.3.1