rojo/.cursor/plans/script_naming_refactor_424e3277.plan.md
2026-01-30 08:12:04 -06:00

10 KiB

name overview todos isProject
Script Naming Refactor Remove the `emitLegacyScripts` option entirely and implement a new, deterministic script naming convention where each script type has exactly one file extension. Add period encoding to the Windows encoding system and default that setting to true.
id content status
path-encoding Add %DOT% to CHAR_ENCODINGS, add %% escape for literal %, default encoding to true completed
id content status
middleware-enum Add LocalScript, LegacyScript, LocalScriptDir, LegacyScriptDir middleware variants completed
id content status
script-type Simplify ScriptType enum and update snapshot_lua logic completed
id content status
sync-rules Add .local.lua/.luau and .legacy.lua/.luau sync rules and init paths completed
id content status
remove-emit-legacy Remove emitLegacyScripts from Project, InstanceContext, and all usages completed
id content status
syncback-runcontext Update get_best_middleware to check RunContext for Script class completed
id content status
file-extensions Update extension_for_middleware with new script types completed
id content status
meta-suffixes Add .local and .legacy to meta file suffix stripping completed
id content status
web-api Update web/api.rs for new script naming conventions completed
id content status
update-tests Update snapshot files and test cases completed
false

Script Naming Convention Refactor

New File Extension Mapping

Script Type File Extension
Script (RunContext.Server) .server.luau
Script (RunContext.Client) .client.luau
Script (RunContext.Plugin) .plugin.luau
Script (RunContext.Legacy) .legacy.luau
LocalScript .local.luau
ModuleScript .luau

Key Changes

1. Add Period Encoding, Percent Escaping, and Default to True

Update [src/path_encoding.rs](src/path_encoding.rs):

  1. Add (".", "%DOT%") to CHAR_ENCODINGS
  2. Add %% as escape sequence for literal % (encode first, decode last)
const CHAR_ENCODINGS: &[(&str, &str)] = &[
    (".", "%DOT%"),  // NEW
    ("<", "%LT%"),
    // ... rest unchanged
];

pub fn encode_path_name(name: &str) -> String {
    // ... handle leading/trailing spaces ...
    
    // FIRST: Escape existing % as %% (before adding new % signs)
    let mut encoded_middle = middle.replace("%", "%%");
    
    // THEN: Encode special characters (which add new % signs)
    for (char, encoded) in CHAR_ENCODINGS {
        encoded_middle = encoded_middle.replace(char, encoded);
    }
    // ...
}

pub fn decode_path_name(name: &str) -> String {
    // ...
    // FIRST: Decode %NAME% patterns
    for (char, encoded) in CHAR_ENCODINGS {
        result = result.replace(encoded, char);
    }
    
    // LAST: Unescape %% back to %
    result = result.replace("%%", "%");
    // ...
}

This ensures round-tripping works correctly:

  • My%DOT%Thing encodes to My%%DOT%%Thing, decodes back to My%DOT%Thing
  • My.Thing encodes to My%DOT%Thing, decodes back to My.Thing

Change the default for encode_windows_invalid_chars to true in [src/syncback/mod.rs](src/syncback/mod.rs) SyncbackRules::encode_windows_invalid_chars():

pub fn encode_windows_invalid_chars(&self) -> bool {
    self.encode_windows_invalid_chars.unwrap_or(true)  // Changed from false to true
}

Also update the decode_windows_invalid_chars logic in [src/serve_session.rs](src/serve_session.rs) to default to true.

2. Add New Middleware Variants

In [src/snapshot_middleware/mod.rs](src/snapshot_middleware/mod.rs), add new variants:

  • LocalScript - for .local.lua/.luau files (LocalScript class)
  • LegacyScript - for .legacy.lua/.luau files (Script with RunContext.Legacy)
  • LocalScriptDir - for init.local.lua/.luau directories
  • LegacyScriptDir - for init.legacy.lua/.luau directories

3. Update ScriptType Enum

In [src/snapshot_middleware/lua.rs](src/snapshot_middleware/lua.rs), simplify to:

pub enum ScriptType {
    Server,   // Script + RunContext.Server
    Client,   // Script + RunContext.Client  
    Plugin,   // Script + RunContext.Plugin
    Legacy,   // Script + RunContext.Legacy
    Local,    // LocalScript
    Module,   // ModuleScript
}

Remove LegacyServer, LegacyClient, RunContextServer, RunContextClient - these were for the dual-mode system.

4. Update Default Sync Rules

In [src/snapshot_middleware/mod.rs](src/snapshot_middleware/mod.rs) default_sync_rules():

vec![
    sync_rule!("*.server.lua", ServerScript, ".server.lua"),
    sync_rule!("*.server.luau", ServerScript, ".server.luau"),
    sync_rule!("*.client.lua", ClientScript, ".client.lua"),
    sync_rule!("*.client.luau", ClientScript, ".client.luau"),
    sync_rule!("*.plugin.lua", PluginScript, ".plugin.lua"),
    sync_rule!("*.plugin.luau", PluginScript, ".plugin.luau"),
    sync_rule!("*.legacy.lua", LegacyScript, ".legacy.lua"),     // NEW
    sync_rule!("*.legacy.luau", LegacyScript, ".legacy.luau"),   // NEW
    sync_rule!("*.local.lua", LocalScript, ".local.lua"),        // NEW
    sync_rule!("*.local.luau", LocalScript, ".local.luau"),      // NEW
    sync_rule!("*.{lua,luau}", ModuleScript),  // Unchanged - catches remaining
    // ... rest unchanged
]

5. Update Init Path Handling

In [src/snapshot_middleware/mod.rs](src/snapshot_middleware/mod.rs) get_dir_middleware():

vec![
    (Middleware::ModuleScriptDir, "init.luau"),
    (Middleware::ModuleScriptDir, "init.lua"),
    (Middleware::ServerScriptDir, "init.server.luau"),
    (Middleware::ServerScriptDir, "init.server.lua"),
    (Middleware::ClientScriptDir, "init.client.luau"),
    (Middleware::ClientScriptDir, "init.client.lua"),
    (Middleware::LocalScriptDir, "init.local.luau"),     // NEW
    (Middleware::LocalScriptDir, "init.local.lua"),       // NEW
    (Middleware::LegacyScriptDir, "init.legacy.luau"),   // NEW
    (Middleware::LegacyScriptDir, "init.legacy.lua"),     // NEW
    (Middleware::CsvDir, "init.csv"),
]

6. Remove emitLegacyScripts Entirely

Files to modify:

  • [src/project.rs](src/project.rs) - Remove emit_legacy_scripts field from Project
  • [src/snapshot/metadata.rs](src/snapshot/metadata.rs) - Remove from InstanceContext, remove with_emit_legacy_scripts()
  • [src/snapshot_middleware/util.rs](src/snapshot_middleware/util.rs) - Remove emit_legacy_scripts_default()
  • [src/snapshot_middleware/project.rs](src/snapshot_middleware/project.rs) - Remove set_emit_legacy_scripts() call
  • [src/serve_session.rs](src/serve_session.rs) - Remove emit_legacy_scripts usage

7. Update Syncback Middleware Selection

In [src/syncback/mod.rs](src/syncback/mod.rs) get_best_middleware():

middleware = match inst.class.as_str() {
    "Script" => {
        // Check RunContext to determine which middleware
        match inst.properties.get(&ustr("RunContext")) {
            Some(Variant::Enum(e)) => match e.to_u32() {
                // Legacy = 0, Server = 1, Client = 2, Plugin = 3
                0 => Middleware::LegacyScript,
                1 => Middleware::ServerScript,
                2 => Middleware::ClientScript,
                3 => Middleware::PluginScript,
                _ => Middleware::ServerScript, // fallback
            },
            _ => Middleware::LegacyScript, // No RunContext = Legacy behavior
        }
    }
    "LocalScript" => Middleware::LocalScript,
    "ModuleScript" => Middleware::ModuleScript,
    // ... rest unchanged
}

8. Update File Name Extensions

In [src/syncback/file_names.rs](src/syncback/file_names.rs) extension_for_middleware():

Middleware::ServerScript => "server.luau",
Middleware::ClientScript => "client.luau",
Middleware::PluginScript => "plugin.luau",
Middleware::LegacyScript => "legacy.luau",   // NEW
Middleware::LocalScript => "local.luau",      // NEW
Middleware::ModuleScript => "luau",

9. Update Meta File Suffix Stripping

In [src/snapshot_middleware/lua.rs](src/snapshot_middleware/lua.rs) syncback_lua() and [src/snapshot_middleware/meta_file.rs](src/snapshot_middleware/meta_file.rs):

Add .local and .legacy to the suffix stripping logic.

10. Update Web API (Two-Way Sync)

In [src/web/api.rs](src/web/api.rs):

  • Update script handling to use new naming conventions
  • Remove all emit_legacy_scripts references and conditional logic

11. Update Test Files

  • Delete test projects in rojo-test/build-tests/nested_runcontext/ that test emitLegacyScripts
  • Update all snapshot files (.snap) that contain emit_legacy_scripts: true/false
  • Update test cases in src/snapshot_middleware/lua.rs to use the new single behavior

12. Clean Up Comments

Remove all legacy references and comments about the old dual-mode system throughout the codebase.

Files to Modify (Summary)

Core changes:

  • src/path_encoding.rs - Add period encoding (%DOT%) and %% escape for literal %
  • src/syncback/mod.rs - Default encode_windows_invalid_chars to true, update middleware selection
  • src/serve_session.rs - Default decode_windows_invalid_chars to true
  • src/snapshot_middleware/mod.rs - Add middleware variants, update sync rules
  • src/snapshot_middleware/lua.rs - Simplify ScriptType, update logic
  • src/snapshot/metadata.rs - Remove emit_legacy_scripts
  • src/project.rs - Remove emit_legacy_scripts field
  • src/syncback/mod.rs - Update middleware selection with RunContext check
  • src/syncback/file_names.rs - Add new extensions
  • src/snapshot_middleware/util.rs - Remove emit_legacy_scripts_default
  • src/snapshot_middleware/project.rs - Remove emit_legacy_scripts propagation
  • src/serve_session.rs - Remove emit_legacy_scripts usage
  • src/web/api.rs - Update for new conventions
  • src/snapshot_middleware/meta_file.rs - Add new suffix handling

Test updates:

  • ~40 snapshot files need emit_legacy_scripts line removed and decode_windows_invalid_chars changed to true
  • Test functions need updating to not pass emit_legacy_scripts parameter