Merge branch 'master' of https://github.com/boatbomber/rojo into headless-api

This commit is contained in:
boatbomber 2023-10-30 19:13:31 -04:00
commit 15f5a89525
82 changed files with 5314 additions and 942 deletions

2
.git-blame-ignore-revs Normal file
View file

@ -0,0 +1,2 @@
# stylua formatting
0f8e1625d572a5fe0f7b5c08653ff92cc837d346

View file

@ -16,7 +16,7 @@ jobs:
strategy: strategy:
matrix: matrix:
rust_version: [stable, 1.69.0] rust_version: [stable, 1.70.0]
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3

View file

@ -2,37 +2,155 @@
## Unreleased Changes ## Unreleased Changes
* Added headless API for Studio companion plugins. ([#631]) * Added headless API for Studio companion plugins. ([#631])
* Internally for themes, we use `Color3.fromHex` now instead of custom-made solution ([#761]).
* On failing to open a file from Roblox Studio, it is now logged appropriately instead of being ignored ([#745]). [#631]: https://github.com/rojo-rbx/rojo/pull/631
* Significantly improved performance of `rojo sourcemap`. ([#668])
## [7.4.0-rc3] - October 25, 2023
* Changed `sourcemap --watch` to only generate the sourcemap when it's necessary ([#800])
* Switched script source property getter and setter to `ScriptEditorService` methods ([#801])
This ensures that the script editor reflects any changes Rojo makes to a script while it is open in the script editor.
* Fixed issues when handling `SecurityCapabilities` values ([#803], [#807])
* Fixed Rojo plugin erroring out when attempting to sync attributes with invalid names ([#809])
[#800]: https://github.com/rojo-rbx/rojo/pull/800
[#801]: https://github.com/rojo-rbx/rojo/pull/801
[#803]: https://github.com/rojo-rbx/rojo/pull/803
[#807]: https://github.com/rojo-rbx/rojo/pull/807
[#809]: https://github.com/rojo-rbx/rojo/pull/809
## [7.4.0-rc2] - October 3, 2023
* Fixed bug with parsing version for plugin validation ([#797])
[#797]: https://github.com/rojo-rbx/rojo/pull/797
## [7.4.0-rc1] - October 3, 2023
### Additions
#### Project format
* Added support for `.toml` files to `$path` ([#633])
* Added support for `Font` and `CFrame` attributes ([rbx-dom#299], [rbx-dom#296])
* Added the `emitLegacyScripts` field to the project format ([#765]). The behavior is outlined below:
| `emitLegacyScripts` Value | Action Taken by Rojo |
|---------------------------|------------------------------------------------------------------------------------------------------------------|
| false | Rojo emits Scripts with the appropriate `RunContext` for `*.client.lua` and `*.server.lua` files in the project. |
| true (default) | Rojo emits LocalScripts and Scripts with legacy `RunContext` (same behavior as previously). |
It can be used like this:
```json
{
"emitLegacyScripts": false,
"name": "MyCoolRunContextProject",
"tree": {
"$path": "src"
}
}
```
* Added `Terrain` classname inference, similar to services ([#771])
`Terrain` may now be defined in projects without using `$className`:
```json
"Workspace": {
"Terrain": {
"$path": "path/to/terrain.rbxm"
}
}
```
* Added support for `Terrain.MaterialColors` ([#770])
`Terrain.MaterialColors` is now represented in projects in a human readable format:
```json
"Workspace": {
"Terrain": {
"$path": "path/to/terrain.rbxm"
"$properties": {
"MaterialColors": {
"Grass": [10, 20, 30],
"Asphalt": [40, 50, 60],
"LeafyGrass": [255, 155, 55]
}
}
}
}
```
* Added better support for `Font` properties ([#731])
`FontFace` properties may now be defined using implicit property syntax:
```json
"TextBox": {
"$className": "TextBox",
"$properties": {
"FontFace": {
"family": "rbxasset://fonts/families/RobotoMono.json",
"weight": "Thin",
"style": "Normal"
}
}
}
```
#### Patch visualizer and notifications
* Added a setting to control patch confirmation behavior ([#774])
This is a new setting for controlling when the Rojo plugin prompts for confirmation before syncing. It has four options:
* Initial (default): prompts only once for a project in a given Studio session
* Always: always prompts for confirmation
* Large Changes: only prompts when there are more than X changed instances. The number of instances is configurable - an additional setting for the number of instances becomes available when this option is chosen
* Unlisted PlaceId: only prompts if the place ID is not present in servePlaceIds
* Added the ability to select Instances in patch visualizer ([#709])
Double-clicking an instance in the patch visualizer sets Roblox Studio's selection to the instance.
* Added a sync reminder notification. ([#689])
Rojo detects if you have previously synced to a place, and displays a notification reminding you to sync again:
![Rojo reminds you to sync a place that you've synced previously](https://user-images.githubusercontent.com/40185666/242397435-ccdfddf2-a63f-420c-bc18-a6e3d6455bba.png)
* Added rich Source diffs in patch visualizer ([#748])
A "View Diff" button for script sources is now present in the patch visualizer. Clicking it displays a side-by-side diff of the script changes:
![The patch visualizer contains a "view diff" button](https://user-images.githubusercontent.com/40185666/256065992-3f03558f-84b0-45a1-80eb-901f348cf067.png)
![The "View Diff" button opens a widget that displays a diff](https://user-images.githubusercontent.com/40185666/256066084-1d9d8fe8-7dad-4ee7-a542-b4aee35a5644.png)
* Patch visualizer now indicates what changes failed to apply. ([#717])
A clickable warning label is displayed when the Rojo plugin is unable to apply changes. Clicking the label displays precise information about which changes failed:
![Patch visualizer displays a clickable warning label when changes fail to apply](https://user-images.githubusercontent.com/40185666/252063660-f08399ef-1e16-4f1c-bed8-552821f98cef.png)
#### Miscellaneous
* Added `plugin` flag to the `build` command that outputs to the local plugins folder ([#735])
This is a flag that builds a Rojo project into Roblox Studio's plugins directory. This allows you to build a Rojo project and load it into Studio as a plugin without having to type the full path to the plugins directory. It can be used like this: `rojo build <PATH-TO-PROJECT> --plugin <FILE-NAME>`
* Added new plugin template to the `init` command ([#738])
This is a new template geared towards plugins. It is similar to the model template, but creates a `Script` instead of a `ModuleScript` in the `src` directory. It can be used like this: `rojo init --kind plugin`
* Added protection against syncing non-place projects as a place. ([#691])
* Add buttons for navigation on the Connected page ([#722])
### Fixes
* Significantly improved performance of `rojo serve` and `rojo build` on macOS. [#783]
* Significantly improved performance of `rojo sourcemap` ([#668])
* Fixed the diff visualizer of connected sessions. ([#674]) * Fixed the diff visualizer of connected sessions. ([#674])
* Fixed disconnected session activity. ([#675]) * Fixed disconnected session activity. ([#675])
* Skip confirming patches that contain only a datamodel name change. ([#688]) * Skip confirming patches that contain only a datamodel name change. ([#688])
* Added sync reminder notification. ([#689])
* Added protection against syncing a model to a place. ([#691])
* Select Instances from diff tree view ([#709])
* Fix Rojo breaking when users undo/redo in Studio ([#708]) * Fix Rojo breaking when users undo/redo in Studio ([#708])
* Improved sync info text on Connected page. ([#692])
* Fix patch visualizer breaking when instances are removed during sync ([#713])
* Patch visualizer now indicates what changes failed to apply. ([#717])
* Add buttons for navigation on the Connected page ([#722])
* Improve tooltip behavior ([#723]) * Improve tooltip behavior ([#723])
* Better settings controls ([#725]) * Better settings controls ([#725])
* Rework patch visualizer with many fixes and improvements ([#726]) * Rework patch visualizer with many fixes and improvements ([#713], [#726], [#755])
* Added support for syncing in `.toml` files ([#633])
* Add `plugin` flag to the `build` command that outputs to the local plugins folder ([#735])
* Added better support for `Font` properties ([#731])
* Add new plugin template to the `init` command ([#738])
* Added rich Source diffs in patch visualizer ([#748])
* Fix PatchTree performance issues ([#755])
* Don't override the initial enabled state for source diffing ([#760])
* Added support for `Terrain.MaterialColors` ([#770])
* Allow `Terrain` to be specified without a classname ([#771])
* Add Confirmation Behavior setting ([#774])
[#631]: https://github.com/rojo-rbx/rojo/pull/631
[#761]: https://github.com/rojo-rbx/rojo/pull/761
[#745]: https://github.com/rojo-rbx/rojo/pull/745
[#668]: https://github.com/rojo-rbx/rojo/pull/668 [#668]: https://github.com/rojo-rbx/rojo/pull/668
[#674]: https://github.com/rojo-rbx/rojo/pull/674 [#674]: https://github.com/rojo-rbx/rojo/pull/674
[#675]: https://github.com/rojo-rbx/rojo/pull/675 [#675]: https://github.com/rojo-rbx/rojo/pull/675
@ -41,7 +159,6 @@
[#691]: https://github.com/rojo-rbx/rojo/pull/691 [#691]: https://github.com/rojo-rbx/rojo/pull/691
[#709]: https://github.com/rojo-rbx/rojo/pull/709 [#709]: https://github.com/rojo-rbx/rojo/pull/709
[#708]: https://github.com/rojo-rbx/rojo/pull/708 [#708]: https://github.com/rojo-rbx/rojo/pull/708
[#692]: https://github.com/rojo-rbx/rojo/pull/692
[#713]: https://github.com/rojo-rbx/rojo/pull/713 [#713]: https://github.com/rojo-rbx/rojo/pull/713
[#717]: https://github.com/rojo-rbx/rojo/pull/717 [#717]: https://github.com/rojo-rbx/rojo/pull/717
[#722]: https://github.com/rojo-rbx/rojo/pull/722 [#722]: https://github.com/rojo-rbx/rojo/pull/722
@ -54,10 +171,13 @@
[#738]: https://github.com/rojo-rbx/rojo/pull/738 [#738]: https://github.com/rojo-rbx/rojo/pull/738
[#748]: https://github.com/rojo-rbx/rojo/pull/748 [#748]: https://github.com/rojo-rbx/rojo/pull/748
[#755]: https://github.com/rojo-rbx/rojo/pull/755 [#755]: https://github.com/rojo-rbx/rojo/pull/755
[#760]: https://github.com/rojo-rbx/rojo/pull/760 [#765]: https://github.com/rojo-rbx/rojo/pull/765
[#770]: https://github.com/rojo-rbx/rojo/pull/770 [#770]: https://github.com/rojo-rbx/rojo/pull/770
[#771]: https://github.com/rojo-rbx/rojo/pull/771 [#771]: https://github.com/rojo-rbx/rojo/pull/771
[#774]: https://github.com/rojo-rbx/rojo/pull/774 [#774]: https://github.com/rojo-rbx/rojo/pull/774
[#783]: https://github.com/rojo-rbx/rojo/pull/783
[rbx-dom#299]: https://github.com/rojo-rbx/rbx-dom/pull/299
[rbx-dom#296]: https://github.com/rojo-rbx/rbx-dom/pull/296
## [7.3.0] - April 22, 2023 ## [7.3.0] - April 22, 2023
* Added `$attributes` to project format. ([#574]) * Added `$attributes` to project format. ([#574])

865
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -1,7 +1,7 @@
[package] [package]
name = "rojo" name = "rojo"
version = "7.3.0" version = "7.4.0-rc3"
rust-version = "1.68.2" rust-version = "1.70.0"
authors = ["Lucien Greathouse <me@lpghatguy.com>"] authors = ["Lucien Greathouse <me@lpghatguy.com>"]
description = "Enables professional-grade development tools for Roblox developers" description = "Enables professional-grade development tools for Roblox developers"
license = "MPL-2.0" license = "MPL-2.0"
@ -49,11 +49,11 @@ memofs = { version = "0.2.0", path = "crates/memofs" }
# rbx_reflection_database = { path = "../rbx-dom/rbx_reflection_database" } # rbx_reflection_database = { path = "../rbx-dom/rbx_reflection_database" }
# rbx_xml = { path = "../rbx-dom/rbx_xml" } # rbx_xml = { path = "../rbx-dom/rbx_xml" }
rbx_binary = "0.7.1" rbx_binary = "0.7.3"
rbx_dom_weak = "2.5.0" rbx_dom_weak = "2.6.0"
rbx_reflection = "4.3.0" rbx_reflection = "4.4.0"
rbx_reflection_database = "0.2.7" rbx_reflection_database = "0.2.8"
rbx_xml = "0.13.1" rbx_xml = "0.13.2"
anyhow = "1.0.44" anyhow = "1.0.44"
backtrace = "0.3.61" backtrace = "0.3.61"
@ -102,6 +102,7 @@ anyhow = "1.0.44"
bincode = "1.3.3" bincode = "1.3.3"
fs-err = "2.6.0" fs-err = "2.6.0"
maplit = "1.0.2" maplit = "1.0.2"
semver = "1.0.19"
[dev-dependencies] [dev-dependencies]
rojo-insta-ext = { path = "crates/rojo-insta-ext" } rojo-insta-ext = { path = "crates/rojo-insta-ext" }

View file

@ -40,7 +40,7 @@ Check out our [contribution guide](CONTRIBUTING.md) for detailed instructions fo
Pull requests are welcome! Pull requests are welcome!
Rojo supports Rust 1.58.1 and newer. The minimum supported version of Rust is based on the latest versions of the dependencies that Rojo has. Rojo supports Rust 1.70.0 and newer. The minimum supported version of Rust is based on the latest versions of the dependencies that Rojo has.
## License ## License
Rojo is available under the terms of the Mozilla Public License, Version 2.0. See [LICENSE.txt](LICENSE.txt) for details. Rojo is available under the terms of the Mozilla Public License, Version 2.0. See [LICENSE.txt](LICENSE.txt) for details.

View file

@ -7,6 +7,7 @@ use fs_err as fs;
use fs_err::File; use fs_err::File;
use maplit::hashmap; use maplit::hashmap;
use memofs::VfsSnapshot; use memofs::VfsSnapshot;
use semver::Version;
fn snapshot_from_fs_path(path: &Path) -> io::Result<VfsSnapshot> { fn snapshot_from_fs_path(path: &Path) -> io::Result<VfsSnapshot> {
println!("cargo:rerun-if-changed={}", path.display()); println!("cargo:rerun-if-changed={}", path.display());
@ -43,6 +44,19 @@ fn main() -> Result<(), anyhow::Error> {
let root_dir = env::var_os("CARGO_MANIFEST_DIR").unwrap(); let root_dir = env::var_os("CARGO_MANIFEST_DIR").unwrap();
let plugin_root = PathBuf::from(root_dir).join("plugin"); let plugin_root = PathBuf::from(root_dir).join("plugin");
let our_version = Version::parse(env::var_os("CARGO_PKG_VERSION").unwrap().to_str().unwrap())?;
let plugin_version =
Version::parse(fs::read_to_string(plugin_root.join("Version.txt"))?.trim())?;
assert!(
our_version.major == plugin_version.major,
"plugin version does not match Cargo version"
);
assert!(
our_version.minor == plugin_version.minor,
"plugin version does not match Cargo version"
);
let snapshot = VfsSnapshot::dir(hashmap! { let snapshot = VfsSnapshot::dir(hashmap! {
"default.project.json" => snapshot_from_fs_path(&plugin_root.join("default.project.json"))?, "default.project.json" => snapshot_from_fs_path(&plugin_root.join("default.project.json"))?,
"fmt" => snapshot_from_fs_path(&plugin_root.join("fmt"))?, "fmt" => snapshot_from_fs_path(&plugin_root.join("fmt"))?,
@ -51,6 +65,7 @@ fn main() -> Result<(), anyhow::Error> {
"rbx_dom_lua" => snapshot_from_fs_path(&plugin_root.join("rbx_dom_lua"))?, "rbx_dom_lua" => snapshot_from_fs_path(&plugin_root.join("rbx_dom_lua"))?,
"src" => snapshot_from_fs_path(&plugin_root.join("src"))?, "src" => snapshot_from_fs_path(&plugin_root.join("src"))?,
"Packages" => snapshot_from_fs_path(&plugin_root.join("Packages"))?, "Packages" => snapshot_from_fs_path(&plugin_root.join("Packages"))?,
"Version.txt" => snapshot_from_fs_path(&plugin_root.join("Version.txt"))?,
}); });
let out_path = Path::new(&out_dir).join("plugin.bincode"); let out_path = Path::new(&out_dir).join("plugin.bincode");

View file

@ -1,6 +1,7 @@
# memofs Changelog # memofs Changelog
## Unreleased Changes ## Unreleased Changes
* Changed the `StdBackend` file watcher to use `PollWatcher` on macOS.
## 0.2.0 (2021-08-23) ## 0.2.0 (2021-08-23)
* Updated to `crossbeam-channel` 0.5.1. * Updated to `crossbeam-channel` 0.5.1.

View file

@ -5,19 +5,34 @@ use std::thread;
use std::time::Duration; use std::time::Duration;
use crossbeam_channel::Receiver; use crossbeam_channel::Receiver;
use notify::{watcher, DebouncedEvent, RecommendedWatcher, RecursiveMode, Watcher}; use notify::{DebouncedEvent, RecursiveMode, Watcher};
#[cfg(target_os = "macos")]
use notify::PollWatcher;
#[cfg(not(target_os = "macos"))]
use notify::{watcher, RecommendedWatcher};
use crate::{DirEntry, Metadata, ReadDir, VfsBackend, VfsEvent}; use crate::{DirEntry, Metadata, ReadDir, VfsBackend, VfsEvent};
/// `VfsBackend` that uses `std::fs` and the `notify` crate. /// `VfsBackend` that uses `std::fs` and the `notify` crate.
pub struct StdBackend { pub struct StdBackend {
// We use PollWatcher on macos because using the KQueue watcher
// can cause some gnarly performance problems.
#[cfg(target_os = "macos")]
watcher: PollWatcher,
#[cfg(not(target_os = "macos"))]
watcher: RecommendedWatcher, watcher: RecommendedWatcher,
watcher_receiver: Receiver<VfsEvent>, watcher_receiver: Receiver<VfsEvent>,
} }
impl StdBackend { impl StdBackend {
pub fn new() -> StdBackend { pub fn new() -> StdBackend {
let (notify_tx, notify_rx) = mpsc::channel(); let (notify_tx, notify_rx) = mpsc::channel();
#[cfg(target_os = "macos")]
let watcher = PollWatcher::new(notify_tx, Duration::from_millis(50)).unwrap();
#[cfg(not(target_os = "macos"))]
let watcher = watcher(notify_tx, Duration::from_millis(50)).unwrap(); let watcher = watcher(notify_tx, Duration::from_millis(50)).unwrap();
let (tx, rx) = crossbeam_channel::unbounded(); let (tx, rx) = crossbeam_channel::unbounded();

1
plugin/Version.txt Normal file
View file

@ -0,0 +1 @@
7.4.0-rc3

View file

@ -7,7 +7,6 @@
}, },
"Packages": { "Packages": {
"$path": "Packages", "$path": "Packages",
"Log": { "Log": {
"$path": "log" "$path": "log"
}, },
@ -20,6 +19,9 @@
"RbxDom": { "RbxDom": {
"$path": "rbx_dom_lua" "$path": "rbx_dom_lua"
} }
},
"Version": {
"$path": "Version.txt"
} }
} }
} }

View file

@ -20,8 +20,8 @@ local function serializeFloat(value)
return value return value
end end
local ALL_AXES = {"X", "Y", "Z"} local ALL_AXES = { "X", "Y", "Z" }
local ALL_FACES = {"Right", "Top", "Back", "Left", "Bottom", "Front"} local ALL_FACES = { "Right", "Top", "Back", "Left", "Bottom", "Front" }
local EncodedValue = {} local EncodedValue = {}
@ -37,7 +37,10 @@ types = {
if ok then if ok then
output[key] = result output[key] = result
else else
local warning = ("Could not decode attribute value of type %q: %s"):format(typeof(value), tostring(result)) local warning = ("Could not decode attribute value of type %q: %s"):format(
typeof(value),
tostring(result)
)
warn(warning) warn(warning)
end end
end end
@ -53,7 +56,10 @@ types = {
if ok then if ok then
output[key] = result output[key] = result
else else
local warning = ("Could not encode attribute value of type %q: %s"):format(typeof(value), tostring(result)) local warning = ("Could not encode attribute value of type %q: %s"):format(
typeof(value),
tostring(result)
)
warn(warning) warn(warning)
end end
end end
@ -111,6 +117,7 @@ types = {
local pos = pod.position local pos = pod.position
local orient = pod.orientation local orient = pod.orientation
--stylua: ignore
return CFrame.new( return CFrame.new(
pos[1], pos[2], pos[3], pos[1], pos[2], pos[3],
orient[1][1], orient[1][2], orient[1][3], orient[1][1], orient[1][2], orient[1][3],
@ -120,17 +127,14 @@ types = {
end, end,
toPod = function(roblox) toPod = function(roblox)
local x, y, z, local x, y, z, r00, r01, r02, r10, r11, r12, r20, r21, r22 = roblox:GetComponents()
r00, r01, r02,
r10, r11, r12,
r20, r21, r22 = roblox:GetComponents()
return { return {
position = {x, y, z}, position = { x, y, z },
orientation = { orientation = {
{r00, r01, r02}, { r00, r01, r02 },
{r10, r11, r12}, { r10, r11, r12 },
{r20, r21, r22}, { r20, r21, r22 },
}, },
} }
end, end,
@ -140,7 +144,7 @@ types = {
fromPod = unpackDecoder(Color3.new), fromPod = unpackDecoder(Color3.new),
toPod = function(roblox) toPod = function(roblox)
return {roblox.r, roblox.g, roblox.b} return { roblox.r, roblox.g, roblox.b }
end, end,
}, },
@ -161,10 +165,7 @@ types = {
local keypoints = {} local keypoints = {}
for index, keypoint in ipairs(pod.keypoints) do for index, keypoint in ipairs(pod.keypoints) do
keypoints[index] = ColorSequenceKeypoint.new( keypoints[index] = ColorSequenceKeypoint.new(keypoint.time, types.Color3.fromPod(keypoint.color))
keypoint.time,
types.Color3.fromPod(keypoint.color)
)
end end
return ColorSequence.new(keypoints) return ColorSequence.new(keypoints)
@ -279,7 +280,7 @@ types = {
pod[material.Name] = { pod[material.Name] = {
math.round(math.clamp(color.R, 0, 1) * 255), math.round(math.clamp(color.R, 0, 1) * 255),
math.round(math.clamp(color.G, 0, 1) * 255), math.round(math.clamp(color.G, 0, 1) * 255),
math.round(math.clamp(color.B, 0, 1) * 255) math.round(math.clamp(color.B, 0, 1) * 255),
} }
end end
return pod return pod
@ -290,7 +291,7 @@ types = {
fromPod = unpackDecoder(NumberRange.new), fromPod = unpackDecoder(NumberRange.new),
toPod = function(roblox) toPod = function(roblox)
return {roblox.Min, roblox.Max} return { roblox.Min, roblox.Max }
end, end,
}, },
@ -299,11 +300,7 @@ types = {
local keypoints = {} local keypoints = {}
for index, keypoint in ipairs(pod.keypoints) do for index, keypoint in ipairs(pod.keypoints) do
keypoints[index] = NumberSequenceKeypoint.new( keypoints[index] = NumberSequenceKeypoint.new(keypoint.time, keypoint.value, keypoint.envelope)
keypoint.time,
keypoint.value,
keypoint.envelope
)
end end
return NumberSequence.new(keypoints) return NumberSequence.new(keypoints)
@ -358,10 +355,7 @@ types = {
Ray = { Ray = {
fromPod = function(pod) fromPod = function(pod)
return Ray.new( return Ray.new(types.Vector3.fromPod(pod.origin), types.Vector3.fromPod(pod.direction))
types.Vector3.fromPod(pod.origin),
types.Vector3.fromPod(pod.direction)
)
end, end,
toPod = function(roblox) toPod = function(roblox)
@ -374,10 +368,7 @@ types = {
Rect = { Rect = {
fromPod = function(pod) fromPod = function(pod)
return Rect.new( return Rect.new(types.Vector2.fromPod(pod[1]), types.Vector2.fromPod(pod[2]))
types.Vector2.fromPod(pod[1]),
types.Vector2.fromPod(pod[2])
)
end, end,
toPod = function(roblox) toPod = function(roblox)
@ -389,31 +380,28 @@ types = {
}, },
Ref = { Ref = {
fromPod = function(_pod) fromPod = function(_)
error("Ref cannot be decoded on its own") error("Ref cannot be decoded on its own")
end, end,
toPod = function(_roblox) toPod = function(_)
error("Ref can not be encoded on its own") error("Ref can not be encoded on its own")
end, end,
}, },
Region3 = { Region3 = {
fromPod = function(pod) fromPod = function(_)
error("Region3 is not implemented") error("Region3 is not implemented")
end, end,
toPod = function(roblox) toPod = function(_)
error("Region3 is not implemented") error("Region3 is not implemented")
end, end,
}, },
Region3int16 = { Region3int16 = {
fromPod = function(pod) fromPod = function(pod)
return Region3int16.new( return Region3int16.new(types.Vector3int16.fromPod(pod[1]), types.Vector3int16.fromPod(pod[2]))
types.Vector3int16.fromPod(pod[1]),
types.Vector3int16.fromPod(pod[2])
)
end, end,
toPod = function(roblox) toPod = function(roblox)
@ -425,11 +413,11 @@ types = {
}, },
SharedString = { SharedString = {
fromPod = function(pod) fromPod = function(_pod)
error("SharedString is not supported") error("SharedString is not supported")
end, end,
toPod = function(roblox) toPod = function(_roblox)
error("SharedString is not supported") error("SharedString is not supported")
end, end,
}, },
@ -443,16 +431,13 @@ types = {
fromPod = unpackDecoder(UDim.new), fromPod = unpackDecoder(UDim.new),
toPod = function(roblox) toPod = function(roblox)
return {roblox.Scale, roblox.Offset} return { roblox.Scale, roblox.Offset }
end, end,
}, },
UDim2 = { UDim2 = {
fromPod = function(pod) fromPod = function(pod)
return UDim2.new( return UDim2.new(types.UDim.fromPod(pod[1]), types.UDim.fromPod(pod[2]))
types.UDim.fromPod(pod[1]),
types.UDim.fromPod(pod[2])
)
end, end,
toPod = function(roblox) toPod = function(roblox)
@ -483,7 +468,7 @@ types = {
fromPod = unpackDecoder(Vector2int16.new), fromPod = unpackDecoder(Vector2int16.new),
toPod = function(roblox) toPod = function(roblox)
return {roblox.X, roblox.Y} return { roblox.X, roblox.Y }
end, end,
}, },
@ -503,7 +488,7 @@ types = {
fromPod = unpackDecoder(Vector3int16.new), fromPod = unpackDecoder(Vector3int16.new),
toPod = function(roblox) toPod = function(roblox)
return {roblox.X, roblox.Y, roblox.Z} return { roblox.X, roblox.Y, roblox.Z }
end, end,
}, },
} }

View file

@ -1,67 +0,0 @@
return function()
local HttpService = game:GetService("HttpService")
local EncodedValue = require(script.Parent.EncodedValue)
local allValues = require(script.Parent.allValues)
local function deepEq(a, b)
if typeof(a) ~= typeof(b) then
return false
end
local ty = typeof(a)
if ty == "table" then
local visited = {}
for key, valueA in a do
visited[key] = true
if not deepEq(valueA, b[key]) then
return false
end
end
for key, valueB in b do
if visited[key] then
continue
end
if not deepEq(valueB, a[key]) then
return false
end
end
return true
else
return a == b
end
end
local extraAssertions = {
CFrame = function(value)
expect(value).to.equal(CFrame.new(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12))
end,
}
for testName, testEntry in allValues do
it("round trip " .. testName, function()
local decodeOk, decoded = EncodedValue.decode(testEntry.value)
assert(decodeOk, decoded)
if extraAssertions[testName] ~= nil then
extraAssertions[testName](decoded)
end
local encodeOk, encoded = EncodedValue.encode(decoded, testEntry.ty)
assert(encodeOk, encoded)
if not deepEq(encoded, testEntry.value) then
local expected = HttpService:JSONEncode(testEntry.value)
local actual = HttpService:JSONEncode(encoded)
error(`Round-trip results did not match.\nExpected:\n{expected}\nActual:\n{actual}`)
end
end)
end
end

View file

@ -1,29 +0,0 @@
return function()
local base64 = require(script.Parent.base64)
it("should encode and decode", function()
local function try(str, expected)
local encoded = base64.encode(str)
expect(encoded).to.equal(expected)
expect(base64.decode(encoded)).to.equal(str)
end
try("Man", "TWFu")
try("Ma", "TWE=")
try("M", "TQ==")
try("ManM", "TWFuTQ==")
try(
[[Man is distinguished, not only by his reason, but by this ]]..
[[singular passion from other animals, which is a lust of the ]]..
[[mind, that by a perseverance of delight in the continued and ]]..
[[indefatigable generation of knowledge, exceeds the short ]]..
[[vehemence of any carnal pleasure.]],
[[TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sI]]..
[[GJ1dCBieSB0aGlzIHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYW]]..
[[xzLCB3aGljaCBpcyBhIGx1c3Qgb2YgdGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJ]]..
[[zZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGludWVkIGFuZCBpbmRl]]..
[[ZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRoZ]]..
[[SBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4=]]
)
end)
end

View file

@ -1,4 +1,5 @@
local CollectionService = game:GetService("CollectionService") local CollectionService = game:GetService("CollectionService")
local ScriptEditorService = game:GetService("ScriptEditorService")
--- A list of `Enum.Material` values that are used for Terrain.MaterialColors --- A list of `Enum.Material` values that are used for Terrain.MaterialColors
local TERRAIN_MATERIAL_COLORS = { local TERRAIN_MATERIAL_COLORS = {
@ -36,9 +37,24 @@ return {
end, end,
write = function(instance, _, value) write = function(instance, _, value)
local existing = instance:GetAttributes() local existing = instance:GetAttributes()
local didAllWritesSucceed = true
for key, attr in pairs(value) do for attributeName, attributeValue in pairs(value) do
instance:SetAttribute(key, attr) local isNameValid =
-- For our SetAttribute to succeed, the attribute name must be
-- less than or equal to 100 characters...
#attributeName <= 100
-- ...must only contain alphanumeric characters, periods, hyphens,
-- underscores, or forward slashes...
and attributeName:match("[^%w%.%-_/]") == nil
-- ... and must not use the RBX prefix, which is reserved by Roblox.
and attributeName:sub(1, 3) ~= "RBX"
if isNameValid then
instance:SetAttribute(attributeName, attributeValue)
else
didAllWritesSucceed = false
end
end end
for key in pairs(existing) do for key in pairs(existing) do
@ -47,7 +63,7 @@ return {
end end
end end
return true return didAllWritesSucceed
end, end,
}, },
Tags = { Tags = {
@ -77,10 +93,10 @@ return {
}, },
LocalizationTable = { LocalizationTable = {
Contents = { Contents = {
read = function(instance, key) read = function(instance, _)
return true, instance:GetContents() return true, instance:GetContents()
end, end,
write = function(instance, key, value) write = function(instance, _, value)
instance:SetContents(value) instance:SetContents(value)
return true return true
end, end,
@ -116,4 +132,34 @@ return {
end, end,
}, },
}, },
Script = {
Source = {
read = function(instance: Script)
return true, ScriptEditorService:GetEditorSource(instance)
end,
write = function(instance: Script, _, value: string)
task.spawn(function()
ScriptEditorService:UpdateSourceAsync(instance, function()
return value
end)
end)
return true
end,
},
},
ModuleScript = {
Source = {
read = function(instance: ModuleScript)
return true, ScriptEditorService:GetEditorSource(instance)
end,
write = function(instance: ModuleScript, _, value: string)
task.spawn(function()
ScriptEditorService:UpdateSourceAsync(instance, function()
return value
end)
end)
return true
end,
},
},
} }

File diff suppressed because it is too large Load diff

View file

@ -24,7 +24,8 @@ local function findCanonicalPropertyDescriptor(className, propertyName)
return PropertyDescriptor.fromRaw( return PropertyDescriptor.fromRaw(
currentClass.Properties[aliasData.AliasFor], currentClass.Properties[aliasData.AliasFor],
currentClassName, currentClassName,
aliasData.AliasFor) aliasData.AliasFor
)
end end
return nil return nil

View file

@ -1,7 +0,0 @@
return function()
local RbxDom = require(script.Parent)
it("should load", function()
expect(RbxDom).to.be.ok()
end)
end

View file

@ -2,11 +2,24 @@ local strict = require(script.Parent.strict)
local isDevBuild = script.Parent.Parent:FindFirstChild("ROJO_DEV_BUILD") ~= nil local isDevBuild = script.Parent.Parent:FindFirstChild("ROJO_DEV_BUILD") ~= nil
local Version = script.Parent.Parent.Version
local major, minor, patch, metadata = Version.Value:match("^(%d+)%.(%d+)%.(%d+)(.*)$")
local realVersion = { major, minor, patch, metadata }
for i = 1, 3 do
local num = tonumber(realVersion[i])
if num then
realVersion[i] = num
else
error(("invalid version `%s` (field %d)"):format(realVersion[i], i))
end
end
return strict("Config", { return strict("Config", {
isDevBuild = isDevBuild, isDevBuild = isDevBuild,
codename = "Epiphany", codename = "Epiphany",
version = { 7, 3, 0 }, version = realVersion,
expectedServerVersionString = "7.2 or newer", expectedServerVersionString = ("%d.%d or newer"):format(realVersion[1], realVersion[2]),
protocolVersion = 4, protocolVersion = 4,
defaultHost = "localhost", defaultHost = "localhost",
defaultPort = "34872", defaultPort = "34872",

View file

@ -233,7 +233,7 @@ function PatchTree.build(patch, instanceMap, changeListHeaders)
addProp( addProp(
prop, prop,
if currentSuccess then currentValue else "[Error]", if currentSuccess then currentValue else "[Error]",
if incomingSuccess then incomingValue else next(incoming) if incomingSuccess then incomingValue else select(2, next(incoming))
) )
end end
@ -359,7 +359,7 @@ function PatchTree.build(patch, instanceMap, changeListHeaders)
if success then if success then
table.insert(changeList, { prop, "N/A", incomingValue }) table.insert(changeList, { prop, "N/A", incomingValue })
else else
table.insert(changeList, { prop, "N/A", next(incoming) }) table.insert(changeList, { prop, "N/A", select(2, next(incoming)) })
end end
end end

View file

@ -282,7 +282,7 @@ function ServeSession:__initialSync(serverInfo)
local unappliedPatch = self.__reconciler:applyPatch(catchUpPatch) local unappliedPatch = self.__reconciler:applyPatch(catchUpPatch)
if not PatchSet.isEmpty(unappliedPatch) then if not PatchSet.isEmpty(unappliedPatch) then
Log.warn( Log.debug(
"Could not apply all changes requested by the Rojo server:\n{}", "Could not apply all changes requested by the Rojo server:\n{}",
PatchSet.humanSummary(self.__instanceMap, unappliedPatch) PatchSet.humanSummary(self.__instanceMap, unappliedPatch)
) )
@ -309,7 +309,7 @@ function ServeSession:__mainSyncLoop()
local unappliedPatch = self.__reconciler:applyPatch(message) local unappliedPatch = self.__reconciler:applyPatch(message)
if not PatchSet.isEmpty(unappliedPatch) then if not PatchSet.isEmpty(unappliedPatch) then
Log.warn( Log.debug(
"Could not apply all changes requested by the Rojo server:\n{}", "Could not apply all changes requested by the Rojo server:\n{}",
PatchSet.humanSummary(self.__instanceMap, unappliedPatch) PatchSet.humanSummary(self.__instanceMap, unappliedPatch)
) )

View file

@ -0,0 +1,35 @@
---
source: tests/tests/build.rs
expression: contents
---
<roblox version="4">
<Item class="Folder" referent="0">
<Properties>
<string name="Name">nested_runcontext</string>
</Properties>
<Item class="Folder" referent="1">
<Properties>
<string name="Name">folder1</string>
</Properties>
<Item class="Script" referent="2">
<Properties>
<string name="Name">test</string>
<token name="RunContext">1</token>
<string name="Source"></string>
</Properties>
</Item>
</Item>
<Item class="Folder" referent="3">
<Properties>
<string name="Name">folder2</string>
</Properties>
<Item class="Script" referent="4">
<Properties>
<string name="Name">test</string>
<token name="RunContext">0</token>
<string name="Source"></string>
</Properties>
</Item>
</Item>
</Item>
</roblox>

View file

@ -11,6 +11,7 @@ expression: contents
<Properties> <Properties>
<string name="Name">hello</string> <string name="Name">hello</string>
<bool name="Disabled">true</bool> <bool name="Disabled">true</bool>
<token name="RunContext">0</token>
<string name="Source">-- This script should be marked 'Disabled'</string> <string name="Source">-- This script should be marked 'Disabled'</string>
</Properties> </Properties>
</Item> </Item>

View file

@ -10,6 +10,7 @@ expression: contents
<Item class="Script" referent="1"> <Item class="Script" referent="1">
<Properties> <Properties>
<string name="Name">serverScript</string> <string name="Name">serverScript</string>
<token name="RunContext">0</token>
<string name="Source">-- This is a Lua server script</string> <string name="Source">-- This is a Lua server script</string>
</Properties> </Properties>
</Item> </Item>

View file

@ -6,6 +6,7 @@ expression: contents
<Item class="Script" referent="0"> <Item class="Script" referent="0">
<Properties> <Properties>
<string name="Name">server_init</string> <string name="Name">server_init</string>
<token name="RunContext">0</token>
<string name="Source">return "From folder/init.server.lua"</string> <string name="Source">return "From folder/init.server.lua"</string>
</Properties> </Properties>
</Item> </Item>

View file

@ -0,0 +1,13 @@
{
"name": "nested_runcontext",
"emitLegacyScripts": false,
"tree": {
"$className": "Folder",
"folder1": {
"$path": "folder1"
},
"folder2": {
"$path": "folder2"
}
}
}

View file

@ -0,0 +1,7 @@
{
"name": "nested_runcontext",
"emitLegacyScripts": true,
"tree": {
"$path": "folder3"
}
}

View file

@ -24,6 +24,8 @@ instances:
Name: bar Name: bar
Parent: id-2 Parent: id-2
Properties: Properties:
RunContext:
Enum: 0
Source: Source:
String: "-- Hello, from bar!" String: "-- Hello, from bar!"
id-4: id-4:

View file

@ -24,6 +24,8 @@ instances:
Name: bar Name: bar
Parent: id-2 Parent: id-2
Properties: Properties:
RunContext:
Enum: 0
Source: Source:
String: "-- Hello, from bar!" String: "-- Hello, from bar!"
id-4: id-4:

View file

@ -25,7 +25,7 @@ pub use self::upload::UploadCommand;
/// Command line options that Rojo accepts, defined using the clap crate. /// Command line options that Rojo accepts, defined using the clap crate.
#[derive(Debug, Parser)] #[derive(Debug, Parser)]
#[clap(name = "Rojo", version, about, author)] #[clap(name = "Rojo", version, about)]
pub struct Options { pub struct Options {
#[clap(flatten)] #[clap(flatten)]
pub global: GlobalOptions, pub global: GlobalOptions,

View file

@ -14,7 +14,7 @@ use tokio::runtime::Runtime;
use crate::{ use crate::{
serve_session::ServeSession, serve_session::ServeSession,
snapshot::{InstanceWithMeta, RojoTree}, snapshot::{AppliedPatchSet, InstanceWithMeta, RojoTree},
}; };
use super::resolve_path; use super::resolve_path;
@ -90,12 +90,14 @@ impl SourcemapCommand {
loop { loop {
let receiver = session.message_queue().subscribe(cursor); let receiver = session.message_queue().subscribe(cursor);
let (new_cursor, _patch_set) = rt.block_on(receiver).unwrap(); let (new_cursor, patch_set) = rt.block_on(receiver).unwrap();
cursor = new_cursor; cursor = new_cursor;
if patch_set_affects_sourcemap(&session, &patch_set, filter) {
write_sourcemap(&session, self.output.as_deref(), filter)?; write_sourcemap(&session, self.output.as_deref(), filter)?;
} }
} }
}
// Avoid dropping ServeSession: it's potentially VERY expensive to drop // Avoid dropping ServeSession: it's potentially VERY expensive to drop
// and we're about to exit anyways. // and we're about to exit anyways.
@ -116,6 +118,43 @@ fn filter_non_scripts(instance: &InstanceWithMeta) -> bool {
) )
} }
fn patch_set_affects_sourcemap(
session: &ServeSession,
patch_set: &[AppliedPatchSet],
filter: fn(&InstanceWithMeta) -> bool,
) -> bool {
let tree = session.tree();
// A sourcemap has probably changed when:
patch_set.par_iter().any(|set| {
// 1. An instance was removed, in which case it will no
// longer exist in the tree and we cant check the filter
!set.removed.is_empty()
// 2. A newly added instance passes the filter
|| set.added.iter().any(|referent| {
let instance = tree
.get_instance(*referent)
.expect("instance did not exist when updating sourcemap");
filter(&instance)
})
// 3. An existing instance has its class name, name,
// or file paths changed, and passes the filter
|| set.updated.iter().any(|updated| {
let changed = updated.changed_class_name.is_some()
|| updated.changed_name.is_some()
|| updated.changed_metadata.is_some();
if changed {
let instance = tree
.get_instance(updated.id)
.expect("instance did not exist when updating sourcemap");
filter(&instance)
} else {
false
}
})
})
}
fn recurse_create_node<'a>( fn recurse_create_node<'a>(
tree: &'a RojoTree, tree: &'a RojoTree,
referent: Ref, referent: Ref,

View file

@ -24,7 +24,7 @@ impl<K: Hash + Eq, V: Eq> MultiMap<K, V> {
K: Borrow<Q>, K: Borrow<Q>,
Q: Hash + Eq, Q: Hash + Eq,
{ {
self.inner.get(k.borrow()).map(Vec::as_slice).unwrap_or(&[]) self.inner.get(k).map(Vec::as_slice).unwrap_or(&[])
} }
pub fn insert(&mut self, k: K, v: V) { pub fn insert(&mut self, k: K, v: V) {

View file

@ -8,7 +8,9 @@ use std::{
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use thiserror::Error; use thiserror::Error;
use crate::{glob::Glob, resolution::UnresolvedValue}; use crate::{
glob::Glob, resolution::UnresolvedValue, snapshot_middleware::emit_legacy_scripts_default,
};
static PROJECT_FILENAME: &str = "default.project.json"; static PROJECT_FILENAME: &str = "default.project.json";
@ -73,6 +75,14 @@ pub struct Project {
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub serve_address: Option<IpAddr>, pub serve_address: Option<IpAddr>,
/// Determines if rojo should emit scripts with the appropriate `RunContext` for `*.client.lua` and `*.server.lua` files in the project.
/// Or, if rojo should keep the legacy behavior of emitting LocalScripts and Scripts with legacy Runcontext
#[serde(
default = "emit_legacy_scripts_default",
skip_serializing_if = "Option::is_none"
)]
pub emit_legacy_scripts: Option<bool>,
/// A list of globs, relative to the folder the project file is in, that /// A list of globs, relative to the folder the project file is in, that
/// match files that should be excluded if Rojo encounters them. /// match files that should be excluded if Rojo encounters them.
#[serde(default, skip_serializing_if = "Vec::is_empty")] #[serde(default, skip_serializing_if = "Vec::is_empty")]

View file

@ -123,7 +123,8 @@ impl ServeSession {
let root_id = tree.get_root_id(); let root_id = tree.get_root_id();
let instance_context = InstanceContext::default(); let instance_context =
InstanceContext::with_emit_legacy_scripts(root_project.emit_legacy_scripts);
log::trace!("Generating snapshot of instances from VFS"); log::trace!("Generating snapshot of instances from VFS");
let snapshot = snapshot_from_vfs(&instance_context, &vfs, start_path)?; let snapshot = snapshot_from_vfs(&instance_context, &vfs, start_path)?;

View file

@ -6,7 +6,10 @@ use std::{
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{glob::Glob, path_serializer, project::ProjectNode}; use crate::{
glob::Glob, path_serializer, project::ProjectNode,
snapshot_middleware::emit_legacy_scripts_default,
};
/// Rojo-specific metadata that can be associated with an instance or a snapshot /// Rojo-specific metadata that can be associated with an instance or a snapshot
/// of an instance. /// of an instance.
@ -103,9 +106,26 @@ impl Default for InstanceMetadata {
pub struct InstanceContext { pub struct InstanceContext {
#[serde(skip_serializing_if = "Vec::is_empty")] #[serde(skip_serializing_if = "Vec::is_empty")]
pub path_ignore_rules: Arc<Vec<PathIgnoreRule>>, pub path_ignore_rules: Arc<Vec<PathIgnoreRule>>,
pub emit_legacy_scripts: bool,
} }
impl InstanceContext { impl InstanceContext {
pub fn new() -> Self {
Self {
path_ignore_rules: Arc::new(Vec::new()),
emit_legacy_scripts: emit_legacy_scripts_default().unwrap(),
}
}
pub fn with_emit_legacy_scripts(emit_legacy_scripts: Option<bool>) -> Self {
Self {
emit_legacy_scripts: emit_legacy_scripts
.or_else(emit_legacy_scripts_default)
.unwrap(),
..Self::new()
}
}
/// Extend the list of ignore rules in the context with the given new rules. /// Extend the list of ignore rules in the context with the given new rules.
pub fn add_path_ignore_rules<I>(&mut self, new_rules: I) pub fn add_path_ignore_rules<I>(&mut self, new_rules: I)
where where
@ -123,13 +143,15 @@ impl InstanceContext {
let rules = Arc::make_mut(&mut self.path_ignore_rules); let rules = Arc::make_mut(&mut self.path_ignore_rules);
rules.extend(new_rules); rules.extend(new_rules);
} }
pub fn set_emit_legacy_scripts(&mut self, emit_legacy_scripts: bool) {
self.emit_legacy_scripts = emit_legacy_scripts;
}
} }
impl Default for InstanceContext { impl Default for InstanceContext {
fn default() -> Self { fn default() -> Self {
InstanceContext { Self::new()
path_ignore_rules: Arc::new(Vec::new()),
}
} }
} }

View file

@ -1,7 +1,6 @@
--- ---
source: src/snapshot/tests/apply.rs source: src/snapshot/tests/apply.rs
expression: tree_view expression: tree_view
--- ---
id: id-1 id: id-1
name: ROOT name: ROOT
@ -12,6 +11,7 @@ properties:
metadata: metadata:
ignore_unknown_instances: false ignore_unknown_instances: false
relevant_paths: [] relevant_paths: []
context: {} context:
emit_legacy_scripts: true
children: [] children: []

View file

@ -9,5 +9,7 @@ properties: {}
metadata: metadata:
ignore_unknown_instances: false ignore_unknown_instances: false
relevant_paths: [] relevant_paths: []
context: {} context:
emit_legacy_scripts: true
children: [] children: []

View file

@ -1,7 +1,6 @@
--- ---
source: src/snapshot/tests/apply.rs source: src/snapshot/tests/apply.rs
expression: tree_view expression: tree_view
--- ---
id: id-1 id: id-1
name: ROOT name: ROOT
@ -12,6 +11,7 @@ properties:
metadata: metadata:
ignore_unknown_instances: false ignore_unknown_instances: false
relevant_paths: [] relevant_paths: []
context: {} context:
emit_legacy_scripts: true
children: [] children: []

View file

@ -9,5 +9,7 @@ properties: {}
metadata: metadata:
ignore_unknown_instances: false ignore_unknown_instances: false
relevant_paths: [] relevant_paths: []
context: {} context:
emit_legacy_scripts: true
children: [] children: []

View file

@ -10,7 +10,8 @@ added_instances:
metadata: metadata:
ignore_unknown_instances: false ignore_unknown_instances: false
relevant_paths: [] relevant_paths: []
context: {} context:
emit_legacy_scripts: true
name: New name: New
class_name: Folder class_name: Folder
properties: {} properties: {}

View file

@ -1,8 +1,8 @@
use std::{path::Path, str}; use std::{collections::HashMap, path::Path, str};
use anyhow::Context; use anyhow::Context;
use maplit::hashmap;
use memofs::{IoResultExt, Vfs}; use memofs::{IoResultExt, Vfs};
use rbx_dom_weak::types::Enum;
use crate::snapshot::{InstanceContext, InstanceMetadata, InstanceSnapshot}; use crate::snapshot::{InstanceContext, InstanceMetadata, InstanceSnapshot};
@ -12,6 +12,13 @@ use super::{
util::match_trailing, util::match_trailing,
}; };
#[derive(Debug)]
enum ScriptType {
Server,
Client,
Module,
}
/// Core routine for turning Lua files into snapshots. /// Core routine for turning Lua files into snapshots.
pub fn snapshot_lua( pub fn snapshot_lua(
context: &InstanceContext, context: &InstanceContext,
@ -20,36 +27,58 @@ pub fn snapshot_lua(
) -> anyhow::Result<Option<InstanceSnapshot>> { ) -> anyhow::Result<Option<InstanceSnapshot>> {
let file_name = path.file_name().unwrap().to_string_lossy(); let file_name = path.file_name().unwrap().to_string_lossy();
let (class_name, instance_name) = if let Some(name) = match_trailing(&file_name, ".server.lua") let run_context_enums = &rbx_reflection_database::get()
.enums
.get("RunContext")
.expect("Unable to get RunContext enums!")
.items;
let (script_type, instance_name) = if let Some(name) = match_trailing(&file_name, ".server.lua")
{ {
("Script", name) (ScriptType::Server, name)
} else if let Some(name) = match_trailing(&file_name, ".client.lua") { } else if let Some(name) = match_trailing(&file_name, ".client.lua") {
("LocalScript", name) (ScriptType::Client, name)
} else if let Some(name) = match_trailing(&file_name, ".lua") { } else if let Some(name) = match_trailing(&file_name, ".lua") {
("ModuleScript", name) (ScriptType::Module, name)
} else if let Some(name) = match_trailing(&file_name, ".server.luau") { } else if let Some(name) = match_trailing(&file_name, ".server.luau") {
("Script", name) (ScriptType::Server, name)
} else if let Some(name) = match_trailing(&file_name, ".client.luau") { } else if let Some(name) = match_trailing(&file_name, ".client.luau") {
("LocalScript", name) (ScriptType::Client, name)
} else if let Some(name) = match_trailing(&file_name, ".luau") { } else if let Some(name) = match_trailing(&file_name, ".luau") {
("ModuleScript", name) (ScriptType::Module, name)
} else { } else {
return Ok(None); return Ok(None);
}; };
let (class_name, run_context) = match (context.emit_legacy_scripts, script_type) {
(false, ScriptType::Server) => ("Script", run_context_enums.get("Server")),
(false, ScriptType::Client) => ("Script", run_context_enums.get("Client")),
(true, ScriptType::Server) => ("Script", run_context_enums.get("Legacy")),
(true, ScriptType::Client) => ("LocalScript", None),
(_, ScriptType::Module) => ("ModuleScript", None),
};
let contents = vfs.read(path)?; let contents = vfs.read(path)?;
let contents_str = str::from_utf8(&contents) let contents_str = str::from_utf8(&contents)
.with_context(|| format!("File was not valid UTF-8: {}", path.display()))? .with_context(|| format!("File was not valid UTF-8: {}", path.display()))?
.to_owned(); .to_owned();
let mut properties = HashMap::with_capacity(2);
properties.insert("Source".to_owned(), contents_str.into());
if let Some(run_context) = run_context {
properties.insert(
"RunContext".to_owned(),
Enum::from_u32(run_context.to_owned()).into(),
);
}
let meta_path = path.with_file_name(format!("{}.meta.json", instance_name)); let meta_path = path.with_file_name(format!("{}.meta.json", instance_name));
let mut snapshot = InstanceSnapshot::new() let mut snapshot = InstanceSnapshot::new()
.name(instance_name) .name(instance_name)
.class_name(class_name) .class_name(class_name)
.properties(hashmap! { .properties(properties)
"Source".to_owned() => contents_str.into(),
})
.metadata( .metadata(
InstanceMetadata::new() InstanceMetadata::new()
.instigating_source(path) .instigating_source(path)
@ -107,26 +136,53 @@ pub fn snapshot_lua_init(
mod test { mod test {
use super::*; use super::*;
use maplit::hashmap;
use memofs::{InMemoryFs, VfsSnapshot}; use memofs::{InMemoryFs, VfsSnapshot};
#[test] #[test]
fn module_from_vfs() { fn class_module_from_vfs() {
let mut imfs = InMemoryFs::new(); let mut imfs = InMemoryFs::new();
imfs.load_snapshot("/foo.lua", VfsSnapshot::file("Hello there!")) imfs.load_snapshot("/foo.lua", VfsSnapshot::file("Hello there!"))
.unwrap(); .unwrap();
let mut vfs = Vfs::new(imfs); let mut vfs = Vfs::new(imfs);
let instance_snapshot = let instance_snapshot = snapshot_lua(
snapshot_lua(&InstanceContext::default(), &mut vfs, Path::new("/foo.lua")) &InstanceContext::with_emit_legacy_scripts(Some(true)),
&mut vfs,
Path::new("/foo.lua"),
)
.unwrap() .unwrap()
.unwrap(); .unwrap();
insta::with_settings!({ sort_maps => true }, {
insta::assert_yaml_snapshot!(instance_snapshot); insta::assert_yaml_snapshot!(instance_snapshot);
});
} }
#[test] #[test]
fn server_from_vfs() { fn runcontext_module_from_vfs() {
let mut imfs = InMemoryFs::new();
imfs.load_snapshot("/foo.lua", VfsSnapshot::file("Hello there!"))
.unwrap();
let mut vfs = Vfs::new(imfs);
let instance_snapshot = snapshot_lua(
&InstanceContext::with_emit_legacy_scripts(Some(false)),
&mut vfs,
Path::new("/foo.lua"),
)
.unwrap()
.unwrap();
insta::with_settings!({ sort_maps => true }, {
insta::assert_yaml_snapshot!(instance_snapshot);
});
}
#[test]
fn class_server_from_vfs() {
let mut imfs = InMemoryFs::new(); let mut imfs = InMemoryFs::new();
imfs.load_snapshot("/foo.server.lua", VfsSnapshot::file("Hello there!")) imfs.load_snapshot("/foo.server.lua", VfsSnapshot::file("Hello there!"))
.unwrap(); .unwrap();
@ -134,18 +190,41 @@ mod test {
let mut vfs = Vfs::new(imfs); let mut vfs = Vfs::new(imfs);
let instance_snapshot = snapshot_lua( let instance_snapshot = snapshot_lua(
&InstanceContext::default(), &InstanceContext::with_emit_legacy_scripts(Some(true)),
&mut vfs, &mut vfs,
Path::new("/foo.server.lua"), Path::new("/foo.server.lua"),
) )
.unwrap() .unwrap()
.unwrap(); .unwrap();
insta::with_settings!({ sort_maps => true }, {
insta::assert_yaml_snapshot!(instance_snapshot); insta::assert_yaml_snapshot!(instance_snapshot);
});
} }
#[test] #[test]
fn client_from_vfs() { fn runcontext_server_from_vfs() {
let mut imfs = InMemoryFs::new();
imfs.load_snapshot("/foo.server.lua", VfsSnapshot::file("Hello there!"))
.unwrap();
let mut vfs = Vfs::new(imfs);
let instance_snapshot = snapshot_lua(
&InstanceContext::with_emit_legacy_scripts(Some(false)),
&mut vfs,
Path::new("/foo.server.lua"),
)
.unwrap()
.unwrap();
insta::with_settings!({ sort_maps => true }, {
insta::assert_yaml_snapshot!(instance_snapshot);
});
}
#[test]
fn class_client_from_vfs() {
let mut imfs = InMemoryFs::new(); let mut imfs = InMemoryFs::new();
imfs.load_snapshot("/foo.client.lua", VfsSnapshot::file("Hello there!")) imfs.load_snapshot("/foo.client.lua", VfsSnapshot::file("Hello there!"))
.unwrap(); .unwrap();
@ -153,14 +232,37 @@ mod test {
let mut vfs = Vfs::new(imfs); let mut vfs = Vfs::new(imfs);
let instance_snapshot = snapshot_lua( let instance_snapshot = snapshot_lua(
&InstanceContext::default(), &InstanceContext::with_emit_legacy_scripts(Some(true)),
&mut vfs, &mut vfs,
Path::new("/foo.client.lua"), Path::new("/foo.client.lua"),
) )
.unwrap() .unwrap()
.unwrap(); .unwrap();
insta::with_settings!({ sort_maps => true }, {
insta::assert_yaml_snapshot!(instance_snapshot); insta::assert_yaml_snapshot!(instance_snapshot);
});
}
#[test]
fn runcontext_client_from_vfs() {
let mut imfs = InMemoryFs::new();
imfs.load_snapshot("/foo.client.lua", VfsSnapshot::file("Hello there!"))
.unwrap();
let mut vfs = Vfs::new(imfs);
let instance_snapshot = snapshot_lua(
&InstanceContext::with_emit_legacy_scripts(Some(false)),
&mut vfs,
Path::new("/foo.client.lua"),
)
.unwrap()
.unwrap();
insta::with_settings!({ sort_maps => true }, {
insta::assert_yaml_snapshot!(instance_snapshot);
});
} }
#[ignore = "init.lua functionality has moved to the root snapshot function"] #[ignore = "init.lua functionality has moved to the root snapshot function"]
@ -177,16 +279,21 @@ mod test {
let mut vfs = Vfs::new(imfs); let mut vfs = Vfs::new(imfs);
let instance_snapshot = let instance_snapshot = snapshot_lua(
snapshot_lua(&InstanceContext::default(), &mut vfs, Path::new("/root")) &InstanceContext::with_emit_legacy_scripts(Some(true)),
&mut vfs,
Path::new("/root"),
)
.unwrap() .unwrap()
.unwrap(); .unwrap();
insta::with_settings!({ sort_maps => true }, {
insta::assert_yaml_snapshot!(instance_snapshot); insta::assert_yaml_snapshot!(instance_snapshot);
});
} }
#[test] #[test]
fn module_with_meta() { fn class_module_with_meta() {
let mut imfs = InMemoryFs::new(); let mut imfs = InMemoryFs::new();
imfs.load_snapshot("/foo.lua", VfsSnapshot::file("Hello there!")) imfs.load_snapshot("/foo.lua", VfsSnapshot::file("Hello there!"))
.unwrap(); .unwrap();
@ -204,16 +311,53 @@ mod test {
let mut vfs = Vfs::new(imfs); let mut vfs = Vfs::new(imfs);
let instance_snapshot = let instance_snapshot = snapshot_lua(
snapshot_lua(&InstanceContext::default(), &mut vfs, Path::new("/foo.lua")) &InstanceContext::with_emit_legacy_scripts(Some(true)),
&mut vfs,
Path::new("/foo.lua"),
)
.unwrap() .unwrap()
.unwrap(); .unwrap();
insta::with_settings!({ sort_maps => true }, {
insta::assert_yaml_snapshot!(instance_snapshot); insta::assert_yaml_snapshot!(instance_snapshot);
});
} }
#[test] #[test]
fn script_with_meta() { fn runcontext_module_with_meta() {
let mut imfs = InMemoryFs::new();
imfs.load_snapshot("/foo.lua", VfsSnapshot::file("Hello there!"))
.unwrap();
imfs.load_snapshot(
"/foo.meta.json",
VfsSnapshot::file(
r#"
{
"ignoreUnknownInstances": true
}
"#,
),
)
.unwrap();
let mut vfs = Vfs::new(imfs);
let instance_snapshot = snapshot_lua(
&InstanceContext::with_emit_legacy_scripts(Some(false)),
&mut vfs,
Path::new("/foo.lua"),
)
.unwrap()
.unwrap();
insta::with_settings!({ sort_maps => true }, {
insta::assert_yaml_snapshot!(instance_snapshot);
});
}
#[test]
fn class_script_with_meta() {
let mut imfs = InMemoryFs::new(); let mut imfs = InMemoryFs::new();
imfs.load_snapshot("/foo.server.lua", VfsSnapshot::file("Hello there!")) imfs.load_snapshot("/foo.server.lua", VfsSnapshot::file("Hello there!"))
.unwrap(); .unwrap();
@ -232,18 +376,52 @@ mod test {
let mut vfs = Vfs::new(imfs); let mut vfs = Vfs::new(imfs);
let instance_snapshot = snapshot_lua( let instance_snapshot = snapshot_lua(
&InstanceContext::default(), &InstanceContext::with_emit_legacy_scripts(Some(true)),
&mut vfs, &mut vfs,
Path::new("/foo.server.lua"), Path::new("/foo.server.lua"),
) )
.unwrap() .unwrap()
.unwrap(); .unwrap();
insta::with_settings!({ sort_maps => true }, {
insta::assert_yaml_snapshot!(instance_snapshot); insta::assert_yaml_snapshot!(instance_snapshot);
});
} }
#[test] #[test]
fn script_disabled() { fn runcontext_script_with_meta() {
let mut imfs = InMemoryFs::new();
imfs.load_snapshot("/foo.server.lua", VfsSnapshot::file("Hello there!"))
.unwrap();
imfs.load_snapshot(
"/foo.meta.json",
VfsSnapshot::file(
r#"
{
"ignoreUnknownInstances": true
}
"#,
),
)
.unwrap();
let mut vfs = Vfs::new(imfs);
let instance_snapshot = snapshot_lua(
&InstanceContext::with_emit_legacy_scripts(Some(false)),
&mut vfs,
Path::new("/foo.server.lua"),
)
.unwrap()
.unwrap();
insta::with_settings!({ sort_maps => true }, {
insta::assert_yaml_snapshot!(instance_snapshot);
});
}
#[test]
fn class_script_disabled() {
let mut imfs = InMemoryFs::new(); let mut imfs = InMemoryFs::new();
imfs.load_snapshot("/bar.server.lua", VfsSnapshot::file("Hello there!")) imfs.load_snapshot("/bar.server.lua", VfsSnapshot::file("Hello there!"))
.unwrap(); .unwrap();
@ -264,7 +442,41 @@ mod test {
let mut vfs = Vfs::new(imfs); let mut vfs = Vfs::new(imfs);
let instance_snapshot = snapshot_lua( let instance_snapshot = snapshot_lua(
&InstanceContext::default(), &InstanceContext::with_emit_legacy_scripts(Some(true)),
&mut vfs,
Path::new("/bar.server.lua"),
)
.unwrap()
.unwrap();
insta::with_settings!({ sort_maps => true }, {
insta::assert_yaml_snapshot!(instance_snapshot);
});
}
#[test]
fn runcontext_script_disabled() {
let mut imfs = InMemoryFs::new();
imfs.load_snapshot("/bar.server.lua", VfsSnapshot::file("Hello there!"))
.unwrap();
imfs.load_snapshot(
"/bar.meta.json",
VfsSnapshot::file(
r#"
{
"properties": {
"Disabled": true
}
}
"#,
),
)
.unwrap();
let mut vfs = Vfs::new(imfs);
let instance_snapshot = snapshot_lua(
&InstanceContext::with_emit_legacy_scripts(Some(false)),
&mut vfs, &mut vfs,
Path::new("/bar.server.lua"), Path::new("/bar.server.lua"),
) )

View file

@ -38,7 +38,7 @@ use self::{
util::PathExt, util::PathExt,
}; };
pub use self::project::snapshot_project_node; pub use self::{project::snapshot_project_node, util::emit_legacy_scripts_default};
/// The main entrypoint to the snapshot function. This function can be pointed /// The main entrypoint to the snapshot function. This function can be pointed
/// at any path and will return something if Rojo knows how to deal with it. /// at any path and will return something if Rojo knows how to deal with it.

View file

@ -12,7 +12,7 @@ use crate::{
}, },
}; };
use super::snapshot_from_vfs; use super::{emit_legacy_scripts_default, snapshot_from_vfs};
pub fn snapshot_project( pub fn snapshot_project(
context: &InstanceContext, context: &InstanceContext,
@ -30,6 +30,12 @@ pub fn snapshot_project(
}); });
context.add_path_ignore_rules(rules); context.add_path_ignore_rules(rules);
context.set_emit_legacy_scripts(
project
.emit_legacy_scripts
.or_else(emit_legacy_scripts_default)
.unwrap(),
);
match snapshot_project_node(&context, path, &project.name, &project.tree, vfs, None)? { match snapshot_project_node(&context, path, &project.name, &project.tree, vfs, None)? {
Some(found_snapshot) => { Some(found_snapshot) => {
@ -77,7 +83,7 @@ pub fn snapshot_project_node(
let name = Cow::Owned(instance_name.to_owned()); let name = Cow::Owned(instance_name.to_owned());
let mut properties = HashMap::new(); let mut properties = HashMap::new();
let mut children = Vec::new(); let mut children = Vec::new();
let mut metadata = InstanceMetadata::default(); let mut metadata = InstanceMetadata::new().context(context);
if let Some(path_node) = &node.path { if let Some(path_node) = &node.path {
let path = path_node.path(); let path = path_node.path();

View file

@ -10,7 +10,8 @@ metadata:
relevant_paths: relevant_paths:
- /foo.csv - /foo.csv
- /foo.meta.json - /foo.meta.json
context: {} context:
emit_legacy_scripts: true
name: foo name: foo
class_name: LocalizationTable class_name: LocalizationTable
properties: properties:

View file

@ -10,7 +10,8 @@ metadata:
relevant_paths: relevant_paths:
- /foo.csv - /foo.csv
- /foo.meta.json - /foo.meta.json
context: {} context:
emit_legacy_scripts: true
name: foo name: foo
class_name: LocalizationTable class_name: LocalizationTable
properties: properties:

View file

@ -17,7 +17,8 @@ metadata:
- /foo/init.client.lua - /foo/init.client.lua
- /foo/init.client.luau - /foo/init.client.luau
- /foo/init.csv - /foo/init.csv
context: {} context:
emit_legacy_scripts: true
name: foo name: foo
class_name: Folder class_name: Folder
properties: {} properties: {}

View file

@ -17,7 +17,8 @@ metadata:
- /foo/init.client.lua - /foo/init.client.lua
- /foo/init.client.luau - /foo/init.client.luau
- /foo/init.csv - /foo/init.csv
context: {} context:
emit_legacy_scripts: true
name: foo name: foo
class_name: Folder class_name: Folder
properties: {} properties: {}
@ -37,7 +38,8 @@ children:
- /foo/Child/init.client.lua - /foo/Child/init.client.lua
- /foo/Child/init.client.luau - /foo/Child/init.client.luau
- /foo/Child/init.csv - /foo/Child/init.csv
context: {} context:
emit_legacy_scripts: true
name: Child name: Child
class_name: Folder class_name: Folder
properties: {} properties: {}

View file

@ -10,7 +10,8 @@ metadata:
relevant_paths: relevant_paths:
- /foo.json - /foo.json
- /foo.meta.json - /foo.meta.json
context: {} context:
emit_legacy_scripts: true
name: foo name: foo
class_name: ModuleScript class_name: ModuleScript
properties: properties:

View file

@ -9,7 +9,8 @@ metadata:
Path: /foo.model.json Path: /foo.model.json
relevant_paths: relevant_paths:
- /foo.model.json - /foo.model.json
context: {} context:
emit_legacy_scripts: true
name: foo name: foo
class_name: IntValue class_name: IntValue
properties: properties:
@ -20,7 +21,8 @@ children:
metadata: metadata:
ignore_unknown_instances: false ignore_unknown_instances: false
relevant_paths: [] relevant_paths: []
context: {} context:
emit_legacy_scripts: true
name: The Child name: The Child
class_name: StringValue class_name: StringValue
properties: {} properties: {}

View file

@ -9,7 +9,8 @@ metadata:
Path: /foo.model.json Path: /foo.model.json
relevant_paths: relevant_paths:
- /foo.model.json - /foo.model.json
context: {} context:
emit_legacy_scripts: true
name: foo name: foo
class_name: IntValue class_name: IntValue
properties: properties:
@ -20,7 +21,8 @@ children:
metadata: metadata:
ignore_unknown_instances: false ignore_unknown_instances: false
relevant_paths: [] relevant_paths: []
context: {} context:
emit_legacy_scripts: true
name: The Child name: The Child
class_name: StringValue class_name: StringValue
properties: {} properties: {}

View file

@ -10,7 +10,8 @@ metadata:
relevant_paths: relevant_paths:
- /foo.client.lua - /foo.client.lua
- /foo.meta.json - /foo.meta.json
context: {} context:
emit_legacy_scripts: true
name: foo name: foo
class_name: LocalScript class_name: LocalScript
properties: properties:

View file

@ -10,7 +10,8 @@ metadata:
relevant_paths: relevant_paths:
- /foo.lua - /foo.lua
- /foo.meta.json - /foo.meta.json
context: {} context:
emit_legacy_scripts: true
name: foo name: foo
class_name: ModuleScript class_name: ModuleScript
properties: properties:

View file

@ -10,7 +10,8 @@ metadata:
relevant_paths: relevant_paths:
- /foo.lua - /foo.lua
- /foo.meta.json - /foo.meta.json
context: {} context:
emit_legacy_scripts: true
name: foo name: foo
class_name: ModuleScript class_name: ModuleScript
properties: properties:

View file

@ -10,12 +10,15 @@ metadata:
relevant_paths: relevant_paths:
- /bar.server.lua - /bar.server.lua
- /bar.meta.json - /bar.meta.json
context: {} context:
emit_legacy_scripts: true
name: bar name: bar
class_name: Script class_name: Script
properties: properties:
Disabled: Disabled:
Bool: true Bool: true
RunContext:
Enum: 0
Source: Source:
String: Hello there! String: Hello there!
children: [] children: []

View file

@ -10,10 +10,13 @@ metadata:
relevant_paths: relevant_paths:
- /foo.server.lua - /foo.server.lua
- /foo.meta.json - /foo.meta.json
context: {} context:
emit_legacy_scripts: true
name: foo name: foo
class_name: Script class_name: Script
properties: properties:
RunContext:
Enum: 0
Source: Source:
String: Hello there! String: Hello there!
children: [] children: []

View file

@ -10,10 +10,13 @@ metadata:
relevant_paths: relevant_paths:
- /foo.server.lua - /foo.server.lua
- /foo.meta.json - /foo.meta.json
context: {} context:
emit_legacy_scripts: true
name: foo name: foo
class_name: Script class_name: Script
properties: properties:
RunContext:
Enum: 0
Source: Source:
String: Hello there! String: Hello there!
children: [] children: []

View file

@ -13,7 +13,8 @@ metadata:
- /root/init.lua - /root/init.lua
- /root/init.server.lua - /root/init.server.lua
- /root/init.client.lua - /root/init.client.lua
context: {} context:
script_type: Class
name: root name: root
class_name: ModuleScript class_name: ModuleScript
properties: properties:

View file

@ -0,0 +1,23 @@
---
source: src/snapshot_middleware/lua.rs
expression: instance_snapshot
---
snapshot_id: "00000000000000000000000000000000"
metadata:
ignore_unknown_instances: false
instigating_source:
Path: /foo.client.lua
relevant_paths:
- /foo.client.lua
- /foo.meta.json
context:
emit_legacy_scripts: false
name: foo
class_name: Script
properties:
RunContext:
Enum: 2
Source:
String: Hello there!
children: []

View file

@ -0,0 +1,21 @@
---
source: src/snapshot_middleware/lua.rs
expression: instance_snapshot
---
snapshot_id: "00000000000000000000000000000000"
metadata:
ignore_unknown_instances: false
instigating_source:
Path: /foo.lua
relevant_paths:
- /foo.lua
- /foo.meta.json
context:
emit_legacy_scripts: false
name: foo
class_name: ModuleScript
properties:
Source:
String: Hello there!
children: []

View file

@ -0,0 +1,21 @@
---
source: src/snapshot_middleware/lua.rs
expression: instance_snapshot
---
snapshot_id: "00000000000000000000000000000000"
metadata:
ignore_unknown_instances: true
instigating_source:
Path: /foo.lua
relevant_paths:
- /foo.lua
- /foo.meta.json
context:
emit_legacy_scripts: false
name: foo
class_name: ModuleScript
properties:
Source:
String: Hello there!
children: []

View file

@ -0,0 +1,25 @@
---
source: src/snapshot_middleware/lua.rs
expression: instance_snapshot
---
snapshot_id: "00000000000000000000000000000000"
metadata:
ignore_unknown_instances: false
instigating_source:
Path: /bar.server.lua
relevant_paths:
- /bar.server.lua
- /bar.meta.json
context:
emit_legacy_scripts: false
name: bar
class_name: Script
properties:
Disabled:
Bool: true
RunContext:
Enum: 1
Source:
String: Hello there!
children: []

View file

@ -0,0 +1,23 @@
---
source: src/snapshot_middleware/lua.rs
expression: instance_snapshot
---
snapshot_id: "00000000000000000000000000000000"
metadata:
ignore_unknown_instances: true
instigating_source:
Path: /foo.server.lua
relevant_paths:
- /foo.server.lua
- /foo.meta.json
context:
emit_legacy_scripts: false
name: foo
class_name: Script
properties:
RunContext:
Enum: 1
Source:
String: Hello there!
children: []

View file

@ -0,0 +1,23 @@
---
source: src/snapshot_middleware/lua.rs
expression: instance_snapshot
---
snapshot_id: "00000000000000000000000000000000"
metadata:
ignore_unknown_instances: false
instigating_source:
Path: /foo.server.lua
relevant_paths:
- /foo.server.lua
- /foo.meta.json
context:
emit_legacy_scripts: false
name: foo
class_name: Script
properties:
RunContext:
Enum: 1
Source:
String: Hello there!
children: []

View file

@ -9,7 +9,8 @@ metadata:
Path: /foo/hello.project.json Path: /foo/hello.project.json
relevant_paths: relevant_paths:
- /foo/hello.project.json - /foo/hello.project.json
context: {} context:
emit_legacy_scripts: true
name: direct-project name: direct-project
class_name: Model class_name: Model
properties: {} properties: {}

View file

@ -9,7 +9,8 @@ metadata:
Path: /foo/default.project.json Path: /foo/default.project.json
relevant_paths: relevant_paths:
- /foo/default.project.json - /foo/default.project.json
context: {} context:
script_type: Class
name: indirect-project name: indirect-project
class_name: Folder class_name: Folder
properties: {} properties: {}

View file

@ -10,7 +10,8 @@ metadata:
relevant_paths: relevant_paths:
- /foo/other.project.json - /foo/other.project.json
- /foo/default.project.json - /foo/default.project.json
context: {} context:
emit_legacy_scripts: true
name: path-property-override name: path-property-override
class_name: StringValue class_name: StringValue
properties: properties:

View file

@ -9,7 +9,8 @@ metadata:
Path: /foo.project.json Path: /foo.project.json
relevant_paths: relevant_paths:
- /foo.project.json - /foo.project.json
context: {} context:
emit_legacy_scripts: true
name: children name: children
class_name: Folder class_name: Folder
properties: {} properties: {}
@ -24,7 +25,8 @@ children:
- $className: Model - $className: Model
- Folder - Folder
relevant_paths: [] relevant_paths: []
context: {} context:
emit_legacy_scripts: true
name: Child name: Child
class_name: Model class_name: Model
properties: {} properties: {}

View file

@ -10,7 +10,8 @@ metadata:
relevant_paths: relevant_paths:
- /foo/other.project.json - /foo/other.project.json
- /foo/default.project.json - /foo/default.project.json
context: {} context:
emit_legacy_scripts: true
name: path-project name: path-project
class_name: Model class_name: Model
properties: {} properties: {}

View file

@ -10,7 +10,8 @@ metadata:
relevant_paths: relevant_paths:
- /foo/other.project.json - /foo/other.project.json
- /foo/default.project.json - /foo/default.project.json
context: {} context:
emit_legacy_scripts: true
name: path-child-project name: path-child-project
class_name: Folder class_name: Folder
properties: {} properties: {}
@ -25,7 +26,8 @@ children:
- $className: Model - $className: Model
- Folder - Folder
relevant_paths: [] relevant_paths: []
context: {} context:
emit_legacy_scripts: true
name: SomeChild name: SomeChild
class_name: Model class_name: Model
properties: {} properties: {}

View file

@ -11,7 +11,8 @@ metadata:
- /foo/other.txt - /foo/other.txt
- /foo/other.meta.json - /foo/other.meta.json
- /foo/default.project.json - /foo/default.project.json
context: {} context:
emit_legacy_scripts: true
name: path-project name: path-project
class_name: StringValue class_name: StringValue
properties: properties:

View file

@ -9,7 +9,8 @@ metadata:
Path: /foo.project.json Path: /foo.project.json
relevant_paths: relevant_paths:
- /foo.project.json - /foo.project.json
context: {} context:
emit_legacy_scripts: true
name: resolved-properties name: resolved-properties
class_name: StringValue class_name: StringValue
properties: properties:

View file

@ -9,7 +9,8 @@ metadata:
Path: /foo.project.json Path: /foo.project.json
relevant_paths: relevant_paths:
- /foo.project.json - /foo.project.json
context: {} context:
emit_legacy_scripts: true
name: unresolved-properties name: unresolved-properties
class_name: StringValue class_name: StringValue
properties: properties:

View file

@ -10,7 +10,8 @@ metadata:
relevant_paths: relevant_paths:
- /foo.toml - /foo.toml
- /foo.meta.json - /foo.meta.json
context: {} context:
emit_legacy_scripts: true
name: foo name: foo
class_name: ModuleScript class_name: ModuleScript
properties: properties:

View file

@ -10,7 +10,8 @@ metadata:
relevant_paths: relevant_paths:
- /foo.txt - /foo.txt
- /foo.meta.json - /foo.meta.json
context: {} context:
emit_legacy_scripts: true
name: foo name: foo
class_name: StringValue class_name: StringValue
properties: properties:

View file

@ -41,3 +41,8 @@ where
.with_context(|| format!("Path did not end in {}: {}", suffix, path.display())) .with_context(|| format!("Path did not end in {}: {}", suffix, path.display()))
} }
} }
// TEMP function until rojo 8.0, when it can be replaced with bool::default (aka false)
pub fn emit_legacy_scripts_default() -> Option<bool> {
Some(true)
}

View file

@ -159,7 +159,7 @@ impl ApiService {
}) })
.unwrap(); .unwrap();
json_ok(&WriteResponse { session_id }) json_ok(WriteResponse { session_id })
} }
async fn handle_api_read(&self, request: Request<Body>) -> Response<Body> { async fn handle_api_read(&self, request: Request<Body>) -> Response<Body> {
@ -271,7 +271,7 @@ impl ApiService {
}, },
}; };
json_ok(&OpenResponse { json_ok(OpenResponse {
session_id: self.serve_session.session_id(), session_id: self.serve_session.session_id(),
}) })
} }

View file

@ -44,6 +44,7 @@ gen_build_tests! {
json_model_legacy_name, json_model_legacy_name,
module_in_folder, module_in_folder,
module_init, module_init,
nested_runcontext,
optional, optional,
project_composed_default, project_composed_default,
project_composed_file, project_composed_file,

View file

@ -1,6 +1,6 @@
use std::fs; use std::fs;
use insta::assert_yaml_snapshot; use insta::{assert_yaml_snapshot, with_settings};
use tempfile::tempdir; use tempfile::tempdir;
use crate::rojo_test::{internable::InternAndRedact, serve_util::run_serve_test}; use crate::rojo_test::{internable::InternAndRedact, serve_util::run_serve_test};
@ -30,10 +30,12 @@ fn scripts() {
assert_yaml_snapshot!("scripts_info", redactions.redacted_yaml(info)); assert_yaml_snapshot!("scripts_info", redactions.redacted_yaml(info));
let read_response = session.get_api_read(root_id).unwrap(); let read_response = session.get_api_read(root_id).unwrap();
with_settings!({ sort_maps => true }, {
assert_yaml_snapshot!( assert_yaml_snapshot!(
"scripts_all", "scripts_all",
read_response.intern_and_redact(&mut redactions, root_id) read_response.intern_and_redact(&mut redactions, root_id)
); );
});
fs::write(session.path().join("src/foo.lua"), "Updated foo!").unwrap(); fs::write(session.path().join("src/foo.lua"), "Updated foo!").unwrap();
@ -44,11 +46,13 @@ fn scripts() {
); );
let read_response = session.get_api_read(root_id).unwrap(); let read_response = session.get_api_read(root_id).unwrap();
with_settings!({ sort_maps => true }, {
assert_yaml_snapshot!( assert_yaml_snapshot!(
"scripts_all-2", "scripts_all-2",
read_response.intern_and_redact(&mut redactions, root_id) read_response.intern_and_redact(&mut redactions, root_id)
); );
}); });
});
} }
#[test] #[test]