mirror of
https://github.com/rojo-rbx/rojo.git
synced 2026-10-23 05:51:39 +00:00
Enhance connected mode test suite and introduce VFS staleness fixtures
- Updated the connected mode test expansion plan, marking all todos as completed, including multi-step sequence tests and various phase implementations. - Added a new plan for VFS staleness test fixtures, outlining the creation of a validation API, test helpers, and multiple staleness tests to improve detection of VFS event issues. - Introduced new rules for the Atlas API and project file format specifications, enhancing the overall structure and documentation for better clarity and maintainability. This update significantly improves testing coverage and addresses potential VFS synchronization issues.
This commit is contained in:
parent
316d7ea70e
commit
f798f3f5e8
107 changed files with 4484 additions and 41 deletions
338
.cursor/rules/atlas-project.mdc
Normal file
338
.cursor/rules/atlas-project.mdc
Normal file
|
|
@ -0,0 +1,338 @@
|
|||
---
|
||||
description: "Project file format specification: .project.json5 schema, file extensions, middleware types, init files, sync rules, and property reference."
|
||||
globs:
|
||||
- "**/*.project.json5"
|
||||
- "**/*.project.json"
|
||||
- "**/*.meta.json5"
|
||||
- "**/*.meta.json"
|
||||
- "**/*.model.json5"
|
||||
- "**/*.model.json"
|
||||
---
|
||||
|
||||
# Atlas Project File Format
|
||||
|
||||
> Atlas is a fork of Rojo. Many internal identifiers, API endpoints, and struct names still reference the original "Rojo" naming.
|
||||
|
||||
## Project File Schema
|
||||
|
||||
**Recognized files:** `*.project.json5` (preferred) | `*.project.json` (legacy)
|
||||
|
||||
**Special:** `default.project.json5` inherits parent folder name.
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
|-------|------|----------|-------------|
|
||||
| `name` | string | Yes* | Instance name (*optional if `default.project.json5`) |
|
||||
| `tree` | ProjectNode | Yes | Root instance tree structure |
|
||||
| `servePort` | number | No | Port for `atlas serve` (default: 34872) |
|
||||
| `serveAddress` | string | No | Address to bind (default: `"127.0.0.1"`) |
|
||||
| `servePlaceIds` | number[] | No | Only allow sync to these place IDs |
|
||||
| `blockedPlaceIds` | number[] | No | Block sync to these place IDs |
|
||||
| `placeId` | number | No | Set place ID on plugin connect |
|
||||
| `gameId` | number | No | Set game/universe ID on plugin connect |
|
||||
| `globIgnorePaths` | string[] | No | Glob patterns to ignore during sync |
|
||||
| `syncRules` | SyncRule[] | No | Custom file type mappings |
|
||||
| `syncbackRules` | SyncbackRules | No | Syncback configuration |
|
||||
| `syncScriptsOnly` | bool | No | Only sync script instances (default: false) |
|
||||
| `ignoreHiddenServices` | bool | No | Ignore internal services (default: true) |
|
||||
|
||||
```json5
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/UserGeneratedLLC/vscode-rojo/master/schemas/project.schema.json",
|
||||
"name": "MyProject",
|
||||
"tree": { /* ProjectNode */ },
|
||||
"servePort": 34872,
|
||||
"serveAddress": "0.0.0.0",
|
||||
"servePlaceIds": [123456],
|
||||
"blockedPlaceIds": [111111],
|
||||
"placeId": 123456,
|
||||
"gameId": 789012,
|
||||
"globIgnorePaths": ["**/*.md", "**/node_modules/**"],
|
||||
"syncRules": [/* SyncRule */],
|
||||
"syncbackRules": { /* SyncbackRules */ },
|
||||
"syncScriptsOnly": false,
|
||||
"ignoreHiddenServices": true
|
||||
}
|
||||
```
|
||||
|
||||
## ProjectNode Schema
|
||||
|
||||
Used in `tree` and recursively for all children.
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `$className` | string | Instance class (required for non-services) |
|
||||
| `$path` | string | Filesystem path to sync from |
|
||||
| `$id` | string | Unique ID for Ref property linking |
|
||||
| `$properties` | object | Instance properties to set |
|
||||
| `$attributes` | object | Instance attributes to set |
|
||||
| `$ignoreUnknownInstances` | bool | Whether to ignore instances not in project during sync |
|
||||
|
||||
**Keys without `$` prefix are child instances.**
|
||||
|
||||
```json5
|
||||
{
|
||||
"$className": "Folder",
|
||||
"$path": "src/MyFolder",
|
||||
"$id": "unique-id",
|
||||
"$properties": { "Anchored": true },
|
||||
"$attributes": { "Health": 100 },
|
||||
"$ignoreUnknownInstances": false,
|
||||
|
||||
// Children
|
||||
"ChildFolder": { "$className": "Folder" },
|
||||
"MyScript": { "$path": "src/MyScript.luau" }
|
||||
}
|
||||
```
|
||||
|
||||
**Services auto-inferred:** Workspace, ReplicatedStorage, ServerScriptService, ServerStorage, StarterGui, StarterPlayer, Lighting, Teams, Chat, SoundService, LocalizationService, TestService, etc.
|
||||
|
||||
## File Extension Mappings
|
||||
|
||||
### Scripts
|
||||
|
||||
| Extension | Result | RunContext | Note |
|
||||
|-----------|--------|------------|------|
|
||||
| `*.luau` | ModuleScript | — | Preferred |
|
||||
| `*.server.luau` | Script | Server | Preferred |
|
||||
| `*.client.luau` | Script | Client | Preferred |
|
||||
| `*.plugin.luau` | Script | Plugin | |
|
||||
| `*.local.luau` | LocalScript | — | |
|
||||
| `*.legacy.luau` | Script | Legacy | |
|
||||
| `*.lua` | ModuleScript | — | Legacy |
|
||||
| `*.server.lua` | Script | Legacy | Legacy |
|
||||
| `*.client.lua` | LocalScript | — | Legacy |
|
||||
|
||||
**Prefer `.luau`** — modern RunContext. `.lua` is legacy (still supported).
|
||||
|
||||
### Models & Data
|
||||
|
||||
| Extension | Result | Note |
|
||||
|-----------|--------|------|
|
||||
| `*.rbxm` | Binary model | |
|
||||
| `*.rbxmx` | XML model | |
|
||||
| `*.model.json5` | JSON model | Preferred |
|
||||
| `*.model.json` | JSON model | Legacy |
|
||||
| `*.txt` | StringValue | |
|
||||
| `*.csv` | LocalizationTable | |
|
||||
| `*.json5` | ModuleScript (Lua table) | Preferred |
|
||||
| `*.json` | ModuleScript (Lua table) | Legacy |
|
||||
| `*.toml` | ModuleScript (Lua table) | |
|
||||
| `*.yaml`, `*.yml` | ModuleScript (Lua table) | |
|
||||
|
||||
### Meta & Project
|
||||
|
||||
| Extension | Purpose | Note |
|
||||
|-----------|---------|------|
|
||||
| `*.meta.json5` | Add properties/attributes to sibling | Preferred |
|
||||
| `*.meta.json` | Add properties/attributes to sibling | Legacy |
|
||||
| `*.project.json5` | Nested project | Preferred |
|
||||
| `*.project.json` | Nested project | Legacy |
|
||||
|
||||
## Init File Priority
|
||||
|
||||
When directory has multiple init files, first match wins:
|
||||
|
||||
1. `default.project.json5` → Nested project (preferred)
|
||||
2. `default.project.json` → Nested project (legacy)
|
||||
3. `init.luau` → ModuleScript (preferred)
|
||||
4. `init.server.luau` → Script (Server)
|
||||
5. `init.client.luau` → Script (Client)
|
||||
6. `init.plugin.luau` → Script (Plugin)
|
||||
7. `init.local.luau` → LocalScript
|
||||
8. `init.legacy.luau` → Script (Legacy)
|
||||
9. `init.csv` → LocalizationTable
|
||||
10. `init.lua` → ModuleScript (legacy)
|
||||
11. `init.server.lua` → Script (Legacy)
|
||||
12. `init.client.lua` → LocalScript (legacy)
|
||||
|
||||
**No init file?** → Directory becomes Folder. **Prefer `.luau` and `.json5` extensions.**
|
||||
|
||||
## Sync Rules Schema
|
||||
|
||||
Custom file type handling in `syncRules` array:
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
|-------|------|----------|-------------|
|
||||
| `pattern` | string | Yes | Glob pattern to match files |
|
||||
| `use` | string | Yes | Middleware type (see table below) |
|
||||
| `suffix` | string | No | Extension to strip from instance name |
|
||||
| `exclude` | string | No | Glob pattern to exclude from matching |
|
||||
|
||||
```json5
|
||||
{
|
||||
"syncRules": [{
|
||||
"pattern": "*.foo",
|
||||
"use": "moduleScript",
|
||||
"suffix": ".foo",
|
||||
"exclude": "*.test.foo"
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
### `use` Values
|
||||
|
||||
| Value | Result |
|
||||
|-------|--------|
|
||||
| `serverScript` | Script (Server) |
|
||||
| `clientScript` | Script (Client) |
|
||||
| `moduleScript` | ModuleScript |
|
||||
| `pluginScript` | Script (Plugin) |
|
||||
| `localScript` | LocalScript |
|
||||
| `legacyScript` | Script (Legacy) |
|
||||
| `json`, `toml`, `yaml` | ModuleScript |
|
||||
| `csv` | LocalizationTable |
|
||||
| `text` | StringValue |
|
||||
| `jsonModel`, `rbxm`, `rbxmx` | Instance tree |
|
||||
| `project` | Nested project |
|
||||
| `ignore` | Skip file |
|
||||
|
||||
## Syncback Rules Schema
|
||||
|
||||
Configure `syncbackRules` object for Roblox → filesystem sync:
|
||||
|
||||
| Field | Type | Default | Description |
|
||||
|-------|------|---------|-------------|
|
||||
| `ignoreTrees` | string[] | `[]` | Roblox tree paths to skip (e.g., `"ServerStorage/Secrets"`) |
|
||||
| `ignorePaths` | string[] | `[]` | Filesystem glob patterns to skip (e.g., `"src/generated/*"`) |
|
||||
| `ignoreProperties` | object | `{}` | Properties to skip per class: `{"ClassName": ["Prop1", "Prop2"]}` |
|
||||
| `ignoreClasses` | string[] | `[]` | Instance classes to skip entirely |
|
||||
| `syncCurrentCamera` | bool | `false` | Whether to sync `Workspace.CurrentCamera` |
|
||||
| `syncUnscriptable` | bool | `true` | Whether to sync unscriptable properties |
|
||||
| `ignoreReferents` | bool | `false` | Whether to skip Ref properties (e.g., `Model.PrimaryPart`) |
|
||||
|
||||
```json5
|
||||
{
|
||||
"syncbackRules": {
|
||||
"ignoreTrees": ["ServerStorage/Secrets", "Workspace/Camera"],
|
||||
"ignorePaths": ["src/generated/*", "src/temp/**"],
|
||||
"ignoreProperties": {
|
||||
"BasePart": ["Color", "BrickColor"],
|
||||
"Script": ["Disabled"]
|
||||
},
|
||||
"ignoreClasses": ["Terrain", "Camera"],
|
||||
"syncCurrentCamera": false,
|
||||
"syncUnscriptable": true,
|
||||
"ignoreReferents": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Meta Files
|
||||
|
||||
Add metadata to file-based instances. Example: `MyScript.server.meta.json5` (for `MyScript.server.luau`)
|
||||
|
||||
```json5
|
||||
{
|
||||
"$className": "Script", // Override (rare)
|
||||
"$properties": { "Disabled": false },
|
||||
"$attributes": { "Priority": 1 },
|
||||
"$ignoreUnknownInstances": true
|
||||
}
|
||||
```
|
||||
|
||||
**Meta files CAN'T:** Override name, set `$path`, have children.
|
||||
|
||||
## Model.json5 Format
|
||||
|
||||
Inline instance definition:
|
||||
|
||||
```json5
|
||||
{
|
||||
"$className": "Tool",
|
||||
"$properties": { "CanBeDropped": false },
|
||||
"$attributes": { "Damage": 50 },
|
||||
"Handle": {
|
||||
"$className": "Part",
|
||||
"$properties": { "Size": [1, 1, 3] }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Property Formats
|
||||
|
||||
### Shorthand (Recommended)
|
||||
|
||||
```json5
|
||||
{
|
||||
"$properties": {
|
||||
"Anchored": true, // Bool
|
||||
"Health": 100, // Number
|
||||
"Position": [0, 5, 0], // Vector3
|
||||
"ImageRectSize": [100, 100], // Vector2
|
||||
"Color": [1, 0, 0], // Color3 (normalized RGB)
|
||||
"BrickColor": "Bright red", // BrickColor name
|
||||
"Shape": "Ball", // Enum name
|
||||
"Tags": ["Collectible", "PowerUp"], // Array
|
||||
"Size": { "X": {"Scale": 1, "Offset": 0}, "Y": {"Scale": 0, "Offset": 100} }, // UDim2
|
||||
"CFrame": { "position": [0, 5, 0], "orientation": [[1,0,0],[0,1,0],[0,0,1]] } // CFrame
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Explicit Type
|
||||
|
||||
Use when ambiguous or specifying enum by number:
|
||||
|
||||
```json5
|
||||
{
|
||||
"$properties": {
|
||||
"Position": { "Vector3": [0, 5, 0] },
|
||||
"Shape": { "Enum": 1 },
|
||||
"BrickColor": { "BrickColor": 21 }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Attributes
|
||||
|
||||
Same format as properties. Limitations: no Ref, no NumberSequence/ColorSequence, enums use string names only.
|
||||
|
||||
```json5
|
||||
{
|
||||
"$attributes": {
|
||||
"Health": 100,
|
||||
"Position": [0, 5, 0],
|
||||
"MyFont": { "family": "rbxasset://fonts/families/SourceSansPro.json", "weight": "Regular", "style": "Normal" }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Ref Property Linking
|
||||
|
||||
Link instances via `$id` and special attributes:
|
||||
|
||||
```json5
|
||||
{
|
||||
"tree": {
|
||||
"Target": { "$className": "Part", "$id": "myTarget" },
|
||||
"Pointer": {
|
||||
"$className": "ObjectValue",
|
||||
"$attributes": { "Rojo_Target_Value": "myTarget" }
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Attribute format:** `Rojo_Target_<PropertyName>`
|
||||
|
||||
## Property Types
|
||||
|
||||
`String`, `Bool`, `Int32`, `Int64`, `Float32`, `Float64`, `Vector2`, `Vector3`, `Vector2int16`, `Vector3int16`, `UDim`, `UDim2`, `CFrame`, `Color3`, `Color3uint8`, `BrickColor`, `NumberRange`, `Rect`, `NumberSequence`, `ColorSequence`, `Enum`, `Ref`, `Content`, `Font`, `PhysicalProperties`, `Axes`, `Faces`, `Tags`, `Attributes`
|
||||
|
||||
See `rbx_reflection_database` for property listings per class.
|
||||
|
||||
## Validation Errors
|
||||
|
||||
1. Unknown fields (except children)
|
||||
2. Reserved names starting with `$` as children
|
||||
3. Missing `$className` or `$path`
|
||||
4. `$path` to non-existent file
|
||||
5. Invalid property types
|
||||
6. Invalid glob syntax
|
||||
7. Duplicate `$id`
|
||||
|
||||
## Source Files
|
||||
|
||||
- `src/project.rs` - Project parsing
|
||||
- `src/snapshot_middleware/mod.rs` - Extension mappings
|
||||
- `src/snapshot/` - Instance tree structure
|
||||
Loading…
Add table
Add a link
Reference in a new issue