Skip to content

siriuslatte/blueprint

Repository files navigation

Blueprint

A Roblox Studio plugin that turns selected Instances into readable source that recreates them. It can emit plain Luau, plain roblox-ts, React in Luau, React in roblox-ts, React JSX, Fusion in Luau, Fusion in roblox-ts, and Fusion JSX from the same selection.

Features

  • Generate plain Luau, plain roblox-ts, React Luau, React roblox-ts, React JSX, Fusion Luau, Fusion roblox-ts, or Fusion JSX output for a single Instance or a multi-Instance selection.
  • Walk descendants (toggle), respect Archivable, and skip properties that match class defaults to keep output short.
  • Per-class property allowlist covering common BasePart, Model, GUI, layout, light, value, sound, and particle classes.
  • Datatype literals for Vector2/Vector3, UDim/UDim2, Color3, BrickColor, CFrame, EnumItem, NumberSequence, ColorSequence, Rect, and NumberRange.
  • Stable, deterministic variable naming with collision handling and two identifier styles (camelCase, snake_case).
  • Three root strategies: parent roots to script.Parent, return a table of roots from a build() function, or leave roots unparented.
  • Scrollable output view inside the plugin widget so long files stay usable in smaller dock sizes.

Requirements

  • Rojo 7.4+ (managed via Rokit).
  • Roblox Studio.

Setup

rokit install

This pulls rojo, stylua, and selene at the versions pinned in rokit.toml.

Running the plugin locally

  1. Build the plugin file:

    rojo build default.project.json -o Blueprint.rbxm
  2. Move Blueprint.rbxm into your local Studio plugins folder:

    • Windows: %LOCALAPPDATA%\Roblox\Plugins
    • macOS: ~/Documents/Roblox/Plugins
  3. Restart Studio. A Blueprint button appears under the Plugins tab. Click it to open the dock widget.

For a faster iteration loop, run rojo serve default.project.json and use the Rojo Studio plugin's "Sync into Studio" option to push the source directly into a place. Wrap the resulting model in a Script parented to ServerScriptService if you want to exercise it without packaging.

Using the test bench

The test place includes representative fixtures (parts, a nested model with duplicate child names, a GUI tree) plus a small spec runner.

rojo build test.project.json -o BlueprintTest.rbxlx

Open BlueprintTest.rbxlx in Studio. On run:

  • Workspace.BlueprintFixtures is populated.
  • The spec runner under ServerScriptService.BlueprintTests.specs prints [PASS] / [FAIL] lines for each test.

You can also poke at the modules from the command bar:

local Blueprint = game.ServerScriptService.Blueprint
print(require(Blueprint.Generator).generate(game:GetService("Selection"):Get(), {}).source)

For day-to-day iteration, run rojo serve test.project.json and use the Rojo plugin to sync into an empty baseplate.

Project layout

src/
  init.server.luau   plugin bootstrap (toolbar, button, dock widget)
  Config.luau        defaults and persisted setting keys
  UI/                widget UI (vanilla Roblox Instances)
  Selection/         Studio selection observer
  Generator/         orchestration, naming, traversal, property allowlist
  Serializer/        value -> Luau expression
  Formatter/         indented line buffer
  Util/              small shared helpers
test/
  fixtures.server.luau  builds a sample tree in Workspace
  specs/                runner + spec modules

Generation options

Option Effect
Include descendants Walk children recursively from each selected root.
Respect Archivable Skip Instances (and their subtrees) where Archivable is false.
Only changed properties Compare to Instance.new(class) defaults; skip matches.
Schema Switches between plain Luau, plain roblox-ts, React Luau, React TS, React JSX, Fusion Luau, Fusion TS, and Fusion JSX.
Naming Variable casing for generated locals.
Root parent How to parent the top-level Instances in the output.

What is supported today

  • All BasePart subclasses with common positional, visual, and physics props.
  • Model, Folder, Attachment, Decal, Texture, lights.
  • GUI: Frame, TextLabel/Button/Box, ImageLabel/Button, ScrollingFrame, UICorner, UIStroke, UIPadding, layouts, and size constraints.
  • Sound, ParticleEmitter, Beam.
  • ValueBase (StringValue, IntValue, etc.) via the Value property.
  • Schema targets: plain Luau with Instance.new, plain roblox-ts with new Instance(...), React Luau with React.createElement(...), React roblox-ts with React.createElement(...), React JSX with tag syntax, Fusion Luau with New(...), Fusion roblox-ts with New(...), and Fusion JSX with tag syntax.

Not yet supported

  • Reference properties (e.g. ObjectValue.Value, Beam.Attachment0/1) that point at Instances outside the emitted subtree are skipped rather than guessed at. In plain Luau and roblox-ts, references to emitted descendants are deferred until the target variable exists. Fusion and React still skip instance-reference properties.
  • Scripts (Script, LocalScript, ModuleScript) are intentionally skipped: their Source is not readable from non-Command-Bar plugins without script injection permission, and silently emitting empty scripts would be misleading.
  • Services and other non-Instance.new-creatable classes are reported in the status bar and skipped.

Extending the property allowlist

Add an entry to BASES in src/Generator/PropertyMap.luau. Each entry lists the ancestor class and the properties to consider. Properties are filtered through Serializer.canSerialize at emission time, so adding an unsupported datatype to the list will simply produce a warning rather than break output.

To add a new datatype, register a formatter in src/Serializer/Literals.luau or src/Serializer/TypeScriptLiterals.luau and wire it into the dialect table in src/Serializer/init.luau.

About

Generate code for Roblox instances in a single click.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages