diff --git a/CHANGELOG.md b/CHANGELOG.md index 3943ae5f..4c23b7d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,10 +31,6 @@ Making a new release? Simply add the new header with the version and date undern ## Unreleased -* LocalizationTables no longer report as changed when nothing changes. [(#1333)] - -[#1333]: https://github.com/rojo-rbx/rojo/pull/1333 - ## [7.7.1] (October 1st, 2026) * Fixed `$path` values that point outside the project folder failing to match `syncRule`s on Windows, which broke `rojo sourcemap` with a "could not be turned into a Roblox Instance" error. ([#1290]) @@ -42,7 +38,6 @@ Making a new release? Simply add the new header with the version and date undern * Fixed `rojo sourcemap --absolute` emitting verbatim (`\\?\`) paths on Windows, which broke require types in luau-lsp. ([#1290]) * The plugin now disables the `Check for Updates` setting if you block access to `api.github.com`. ([#1297]) -[7.7.1]: https://github.com/rojo-rbx/rojo/releases/tag/v7.7.1 [#1290]: https://github.com/rojo-rbx/rojo/pull/1290 [#1297]: https://github.com/rojo-rbx/rojo/pull/1297 @@ -65,7 +60,6 @@ Making a new release? Simply add the new header with the version and date undern * `rojo serve` now validates the `Host`/`Origin` headers to protect the local/private server against DNS rebinding, gates `/api/open` to local clients, and warns when bound to a network-reachable address. The accepted hosts can be extended with the `--allowed-hosts` option or a project's `serveAllowedHosts` field, for example to reach a network-exposed server by hostname. ([#1270]) * Fixed syncback not removing stale `$properties` entries when Studio resets a property to its engine default. ([#1244]) -[7.7.0]: https://github.com/rojo-rbx/rojo/releases/tag/v7.7.0 [#1176]: https://github.com/rojo-rbx/rojo/pull/1176 [#1179]: https://github.com/rojo-rbx/rojo/pull/1179 [#1192]: https://github.com/rojo-rbx/rojo/pull/1192 diff --git a/rojo-test/build-test-snapshots/end_to_end__tests__build__csv_bug_145.snap b/rojo-test/build-test-snapshots/end_to_end__tests__build__csv_bug_145.snap index 96cdc002..ab7470b3 100644 --- a/rojo-test/build-test-snapshots/end_to_end__tests__build__csv_bug_145.snap +++ b/rojo-test/build-test-snapshots/end_to_end__tests__build__csv_bug_145.snap @@ -10,7 +10,7 @@ expression: contents normal - [{"example":"A number demonstrating issue 145","key":"Count","source":"3","values":{"es":"7"}}] + [{"key":"Count","example":"A number demonstrating issue 145","source":"3","values":{"es":"7"}}] diff --git a/rojo-test/build-test-snapshots/end_to_end__tests__build__csv_bug_147.snap b/rojo-test/build-test-snapshots/end_to_end__tests__build__csv_bug_147.snap index 8a668402..d16de20f 100644 --- a/rojo-test/build-test-snapshots/end_to_end__tests__build__csv_bug_147.snap +++ b/rojo-test/build-test-snapshots/end_to_end__tests__build__csv_bug_147.snap @@ -10,7 +10,7 @@ expression: contents normal - [{"key":"Label.Opacity","source":"Opacity","values":{}},{"key":"Label.Thickness","source":"Thickness","values":{}},{"key":"Language.Name","source":"English","values":{}},{"key":"Language.Region","source":"United States","values":{}},{"key":"Toolbar.Camera","source":"Top-down camera","values":{}},{"key":"Toolbar.Mode.Pixel","source":"Pixel mode","values":{}},{"key":"Toolbar.Mode.Vector","source":"Vector mode","values":{}},{"key":"Toolbar.Preferences","source":"Settings","values":{}},{"key":"Toolbar.Redo","source":"Redo","values":{}},{"key":"Toolbar.Saves","source":"Saved drawings","values":{}},{"key":"Toolbar.Undo","source":"Undo","values":{}}] + [{"key":"Language.Name","source":"English","values":{}},{"key":"Language.Region","source":"United States","values":{}},{"key":"Label.Thickness","source":"Thickness","values":{}},{"key":"Label.Opacity","source":"Opacity","values":{}},{"key":"Toolbar.Undo","source":"Undo","values":{}},{"key":"Toolbar.Redo","source":"Redo","values":{}},{"key":"Toolbar.Camera","source":"Top-down camera","values":{}},{"key":"Toolbar.Saves","source":"Saved drawings","values":{}},{"key":"Toolbar.Preferences","source":"Settings","values":{}},{"key":"Toolbar.Mode.Vector","source":"Vector mode","values":{}},{"key":"Toolbar.Mode.Pixel","source":"Pixel mode","values":{}}] diff --git a/rojo-test/build-test-snapshots/end_to_end__tests__build__csv_in_folder.snap b/rojo-test/build-test-snapshots/end_to_end__tests__build__csv_in_folder.snap index 40bc7ce5..d60fca02 100644 --- a/rojo-test/build-test-snapshots/end_to_end__tests__build__csv_in_folder.snap +++ b/rojo-test/build-test-snapshots/end_to_end__tests__build__csv_in_folder.snap @@ -10,7 +10,7 @@ expression: contents normal - [{"example":"An exclamation of despair","key":"Ack","source":"Ack!","values":{"es":"¡Ay!"}}] + [{"key":"Ack","example":"An exclamation of despair","source":"Ack!","values":{"es":"¡Ay!"}}] diff --git a/src/snapshot_middleware/csv.rs b/src/snapshot_middleware/csv.rs index 8cea9b81..b4a86efc 100644 --- a/src/snapshot_middleware/csv.rs +++ b/src/snapshot_middleware/csv.rs @@ -162,11 +162,13 @@ pub fn syncback_csv_init<'sync>( /// /// We manually deserialize into this table from CSV, but let serde_json handle /// serialization. -#[derive(Debug, Default, Deserialize)] +#[derive(Debug, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] struct LocalizationEntry<'a> { + #[serde(skip_serializing_if = "Option::is_none")] key: Option>, + #[serde(skip_serializing_if = "Option::is_none")] context: Option>, // Roblox writes `examples` for LocalizationTable's Content property, which @@ -174,77 +176,16 @@ struct LocalizationEntry<'a> { // This is reported here: https://devforum.roblox.com/t/2908720. // // To support their mistake, we support an alias named `examples`. - #[serde(alias = "examples")] + #[serde(skip_serializing_if = "Option::is_none", alias = "examples")] example: Option>, + #[serde(skip_serializing_if = "Option::is_none")] source: Option>, // We use a BTreeMap here to get deterministic output order. values: BTreeMap, Cow<'a, str>>, } -// Guarantee a specific order of both fields and entries so that diff always match up -impl<'a> Serialize for LocalizationEntry<'a> { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - let mut btree = BTreeMap::new(); - - if let Some(key) = &self.key { - btree.insert( - "key", - serde_json::to_value(key).map_err(serde::ser::Error::custom)?, - ); - } - - if let Some(context) = &self.context { - btree.insert( - "context", - serde_json::to_value(context).map_err(serde::ser::Error::custom)?, - ); - } - - if let Some(example) = &self.example { - btree.insert( - "example", - serde_json::to_value(example).map_err(serde::ser::Error::custom)?, - ); - } - - if let Some(source) = &self.source { - btree.insert( - "source", - serde_json::to_value(source).map_err(serde::ser::Error::custom)?, - ); - } - - btree.insert( - "values", - serde_json::to_value(&self.values).map_err(serde::ser::Error::custom)?, - ); - - btree.serialize(serializer) - } -} - -fn sort_localization_entries(mut entries: Vec) -> Vec { - entries.sort_by(|a, b| { - a.key - .as_deref() - .unwrap_or_default() - .cmp(&b.key.as_deref().unwrap_or_default()) - .then_with(|| { - a.source - .as_deref() - .unwrap_or_default() - .cmp(&b.source.as_deref().unwrap_or_default()) - }) - }); - - entries -} - /// Normally, we'd be able to let the csv crate construct our struct for us. /// /// However, because of a limitation with Serde's 'flatten' feature, it's not @@ -295,8 +236,8 @@ fn convert_localization_csv(contents: &[u8]) -> anyhow::Result { entries.push(entry); } - let encoded = serde_json::to_string(&sort_localization_entries(entries)) - .context("Could not encode JSON for localization table")?; + let encoded = + serde_json::to_string(&entries).context("Could not encode JSON for localization table")?; Ok(encoded) } @@ -308,9 +249,11 @@ fn localization_to_csv(csv_contents: &str) -> anyhow::Result> { let mut out = Vec::new(); let mut writer = csv::Writer::from_writer(&mut out); - let csv: Vec = sort_localization_entries( - serde_json::from_str(csv_contents).context("cannot decode JSON from localization table")?, - ); + let mut csv: Vec = + serde_json::from_str(csv_contents).context("cannot decode JSON from localization table")?; + + // TODO sort this better + csv.sort_by(|a, b| a.source.partial_cmp(&b.source).unwrap()); let mut headers = vec!["Key", "Source", "Context", "Example"]; // We want both order and a lack of duplicates, so we use a BTreeSet. diff --git a/src/snapshot_middleware/snapshots/librojo__snapshot_middleware__csv__test__csv_from_vfs.snap b/src/snapshot_middleware/snapshots/librojo__snapshot_middleware__csv__test__csv_from_vfs.snap index aa4c18c8..6356256d 100644 --- a/src/snapshot_middleware/snapshots/librojo__snapshot_middleware__csv__test__csv_from_vfs.snap +++ b/src/snapshot_middleware/snapshots/librojo__snapshot_middleware__csv__test__csv_from_vfs.snap @@ -20,5 +20,5 @@ name: foo class_name: LocalizationTable properties: Contents: - String: "[{\"example\":\"An exclamation of despair\",\"key\":\"Ack\",\"source\":\"Ack!\",\"values\":{\"es\":\"¡Ay!\"}}]" + String: "[{\"key\":\"Ack\",\"example\":\"An exclamation of despair\",\"source\":\"Ack!\",\"values\":{\"es\":\"¡Ay!\"}}]" children: [] diff --git a/src/snapshot_middleware/snapshots/librojo__snapshot_middleware__csv__test__csv_init.snap b/src/snapshot_middleware/snapshots/librojo__snapshot_middleware__csv__test__csv_init.snap index 385666a8..813a2933 100644 --- a/src/snapshot_middleware/snapshots/librojo__snapshot_middleware__csv__test__csv_init.snap +++ b/src/snapshot_middleware/snapshots/librojo__snapshot_middleware__csv__test__csv_init.snap @@ -29,5 +29,5 @@ name: root class_name: LocalizationTable properties: Contents: - String: "[{\"example\":\"An exclamation of despair\",\"key\":\"Ack\",\"source\":\"Ack!\",\"values\":{\"es\":\"¡Ay!\"}}]" + String: "[{\"key\":\"Ack\",\"example\":\"An exclamation of despair\",\"source\":\"Ack!\",\"values\":{\"es\":\"¡Ay!\"}}]" children: [] diff --git a/src/snapshot_middleware/snapshots/librojo__snapshot_middleware__csv__test__csv_init_with_meta.snap b/src/snapshot_middleware/snapshots/librojo__snapshot_middleware__csv__test__csv_init_with_meta.snap index 81fe3351..01ce0a0f 100644 --- a/src/snapshot_middleware/snapshots/librojo__snapshot_middleware__csv__test__csv_init_with_meta.snap +++ b/src/snapshot_middleware/snapshots/librojo__snapshot_middleware__csv__test__csv_init_with_meta.snap @@ -29,5 +29,5 @@ name: root class_name: LocalizationTable properties: Contents: - String: "[{\"example\":\"An exclamation of despair\",\"key\":\"Ack\",\"source\":\"Ack!\",\"values\":{\"es\":\"¡Ay!\"}}]" + String: "[{\"key\":\"Ack\",\"example\":\"An exclamation of despair\",\"source\":\"Ack!\",\"values\":{\"es\":\"¡Ay!\"}}]" children: [] diff --git a/src/snapshot_middleware/snapshots/librojo__snapshot_middleware__csv__test__csv_with_meta.snap b/src/snapshot_middleware/snapshots/librojo__snapshot_middleware__csv__test__csv_with_meta.snap index b02dee1c..f90eccc8 100644 --- a/src/snapshot_middleware/snapshots/librojo__snapshot_middleware__csv__test__csv_with_meta.snap +++ b/src/snapshot_middleware/snapshots/librojo__snapshot_middleware__csv__test__csv_with_meta.snap @@ -20,5 +20,5 @@ name: foo class_name: LocalizationTable properties: Contents: - String: "[{\"example\":\"An exclamation of despair\",\"key\":\"Ack\",\"source\":\"Ack!\",\"values\":{\"es\":\"¡Ay!\"}}]" + String: "[{\"key\":\"Ack\",\"example\":\"An exclamation of despair\",\"source\":\"Ack!\",\"values\":{\"es\":\"¡Ay!\"}}]" children: []