Basic transformer functionality

This commit is contained in:
evil bocchi
2025-04-06 19:54:48 +08:00
parent 269ee8b777
commit 24ab0fef27
12 changed files with 7864 additions and 1433 deletions
+18 -12
View File
@@ -1,15 +1,21 @@
ISC License
MIT License
Copyright (c) 2020, Fireboltofdeath
Copyright (c) 2025 evilbocchi
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+91 -30
View File
@@ -1,38 +1,99 @@
# rbxts-transformer-services
This is a [demo transformer](#template) that converts @rbxts/services imports into plain GetService calls for increased legibility.
# rbxts-transformer-instances
## Example
```ts
// input.ts
import { Players, ServerScriptService } from "@rbxts/services";
Enable the use of instance constructors in roblox-ts, reducing boilerplate by letting you use familiar class-like syntax.
print(Players.LocalPlayer);
print(ServerScriptService.GetChildren().size());
## Overview
This transformer automatically converts constructor-style instance creation (`new Frame()`) into the standard Roblox `Instance.new("Frame")` pattern at compile time.
## Features
- ✨ Cleaner syntax for creating instances
- 🔍 Full TypeScript type safety
- ⚡ Zero runtime overhead (transforms at compile-time)
- 🔄 Supports all creatable Roblox instance types
- 📦 Easy to install and use
## Installation
```bash
npm install rbxts-transformer-instances
```
### Configure in your tsconfig.json
Add the transformer to your `tsconfig.json`:
```json
{
"compilerOptions": {
...
"plugins": [
{
"transform": "rbxts-transformer-instances",
}
]
},
"include": [..., "node_modules/rbxts-transformer-instances"]
}
```
## Usage
### Basic Usage
```ts
// Instead of this:
const part = new Instance("Part");
part.Material = Enum.Material.Neon;
part.Position = new Vector3(0, 10, 0);
// You can write this:
const part = new Part();
part.Material = Enum.Material.Neon;
part.Position = new Vector3(0, 10, 0);
```
## Examples
### Input (TypeScript):
```ts
// Creating a basic UI
const screenGui = new ScreenGui();
screenGui.Parent = game.GetService("Players").LocalPlayer!.WaitForChild("PlayerGui");
const frame = new Frame();
frame.Size = new UDim2(0, 200, 0, 200);
frame.Position = new UDim2(0.5, -100, 0.5, -100);
frame.BackgroundColor3 = Color3.fromRGB(45, 45, 45);
frame.Parent = screenGui;
const textLabel = new TextLabel();
textLabel.Size = new UDim2(1, 0, 0, 50);
textLabel.Text = "Hello, World!";
textLabel.Parent = frame;
```
### Output (Lua):
```lua
-- output.lua
local Players = game:GetService("Players")
local ServerScriptService = game:GetService("ServerScriptService")
print(Players.LocalPlayer)
print(#ServerScriptService:GetChildren())
-- Creates standard Instance.new calls
local screenGui = Instance.new("ScreenGui")
screenGui.Parent = game:GetService("Players").LocalPlayer:WaitForChild("PlayerGui")
local frame = Instance.new("Frame")
frame.Size = UDim2.new(0, 200, 0, 200)
frame.Position = UDim2.new(0.5, -100, 0.5, -100)
frame.BackgroundColor3 = Color3.fromRGB(45, 45, 45)
frame.Parent = screenGui
local textLabel = Instance.new("TextLabel")
textLabel.Size = UDim2.new(1, 0, 0, 50)
textLabel.Text = "Hello, World!"
textLabel.Parent = frame
```
# Template
This transformer is intended to be used as a template for those who are interested in creating their own transformers in roblox-ts.
## License
A necessary resource for those starting out with transformers is [ts-ast-viewer](https://ts-ast-viewer.com/). It shows you the result of AST, relevant properties, symbol information, type information and it automatically generates factory code for nodes. For example, you can see the code this transformer generates [here](https://ts-ast-viewer.com/#code/MYewdgzgLgBACgGwIYE8CmAnCMC8MDmSAtmgHQDiaUAypgG4CWwaAFAESKqYRsCUANACgYImAHUQGANYQADkma4CxMpRr0mrNhIxQZ85nwDcQA).
I'd also recommend downloading the [TypeScript repo](https://github.com/microsoft/TypeScript) locally as it's extremely helpful when you're using undocumented (the majority of the compiler API), internal or uncommon APIs.
Transformers mutate the TypeScript [AST](https://en.wikipedia.org/wiki/Abstract_syntax_tree) by replacing parts of the AST with new nodes. Transformers are also able to utilize symbol and type information giving you access to TypeScript's advanced control flow analysis.
## Other Transformers
Here's a list of transformers if you want to see how they handle working with parts of the TypeScript compiler api not shown here (e.g symbols or types).
- [rbxts-transform-debug](https://github.com/roblox-aurora/rbxts-transform-debug) by [@roblox-aurora](https://github.com/roblox-aurora)
- [rbxts-transform-env](https://github.com/roblox-aurora/rbxts-transform-env) by [@roblox-aurora](https://github.com/roblox-aurora)
- [Flamework](https://github.com/rbxts-flamework/transformer) by [@rbxts-flamework](https://github.com/rbxts-flamework)
One other source you may goto for learning about transformers is actually [roblox-ts](https://github.com/roblox-ts/roblox-ts/tree/master/src/TSTransformer) itself. This generates a Luau AST instead of a TS AST but it may still be a useful resource for learning about the compiler api.
MIT
Vendored
+308
View File
@@ -0,0 +1,308 @@
/// <reference no-default-lib="true"/>
/// <reference types="@rbxts/types" />
/**
* Add constructors to all Roblox CreatableInstances
* This adds `new ClassName()` constructors for all Roblox instance types
* that can be created with Instance.new()
*
* The transformer will convert these to `new Instance("ClassName")`
*/
type CreatableInstancesWithConstructors = {
[K in keyof CreatableInstances]: {
new (...args: any[]): CreatableInstances[K];
}
}
declare const CreatableInstancesWithConstructors: CreatableInstancesWithConstructors;
declare const Accessory: CreatableInstancesWithConstructors["Accessory"];
declare const AccessoryDescription: CreatableInstancesWithConstructors["AccessoryDescription"];
declare const Accoutrement: CreatableInstancesWithConstructors["Accoutrement"];
declare const Actor: CreatableInstancesWithConstructors["Actor"];
declare const AdGui: CreatableInstancesWithConstructors["AdGui"];
declare const AdPortal: CreatableInstancesWithConstructors["AdPortal"];
declare const AirController: CreatableInstancesWithConstructors["AirController"];
declare const AlignOrientation: CreatableInstancesWithConstructors["AlignOrientation"];
declare const AlignPosition: CreatableInstancesWithConstructors["AlignPosition"];
declare const AngularVelocity: CreatableInstancesWithConstructors["AngularVelocity"];
declare const Animation: CreatableInstancesWithConstructors["Animation"];
declare const AnimationConstraint: CreatableInstancesWithConstructors["AnimationConstraint"];
declare const AnimationController: CreatableInstancesWithConstructors["AnimationController"];
declare const AnimationRigData: CreatableInstancesWithConstructors["AnimationRigData"];
declare const Animator: CreatableInstancesWithConstructors["Animator"];
declare const Annotation: CreatableInstancesWithConstructors["Annotation"];
declare const ArcHandles: CreatableInstancesWithConstructors["ArcHandles"];
declare const Atmosphere: CreatableInstancesWithConstructors["Atmosphere"];
declare const AtmosphereSensor: CreatableInstancesWithConstructors["AtmosphereSensor"];
declare const Attachment: CreatableInstancesWithConstructors["Attachment"];
declare const AudioAnalyzer: CreatableInstancesWithConstructors["AudioAnalyzer"];
declare const AudioChannelMixer: CreatableInstancesWithConstructors["AudioChannelMixer"];
declare const AudioChannelSplitter: CreatableInstancesWithConstructors["AudioChannelSplitter"];
declare const AudioChorus: CreatableInstancesWithConstructors["AudioChorus"];
declare const AudioCompressor: CreatableInstancesWithConstructors["AudioCompressor"];
declare const AudioDeviceInput: CreatableInstancesWithConstructors["AudioDeviceInput"];
declare const AudioDeviceOutput: CreatableInstancesWithConstructors["AudioDeviceOutput"];
declare const AudioDistortion: CreatableInstancesWithConstructors["AudioDistortion"];
declare const AudioEcho: CreatableInstancesWithConstructors["AudioEcho"];
declare const AudioEmitter: CreatableInstancesWithConstructors["AudioEmitter"];
declare const AudioEqualizer: CreatableInstancesWithConstructors["AudioEqualizer"];
declare const AudioFader: CreatableInstancesWithConstructors["AudioFader"];
declare const AudioFilter: CreatableInstancesWithConstructors["AudioFilter"];
declare const AudioFlanger: CreatableInstancesWithConstructors["AudioFlanger"];
declare const AudioLimiter: CreatableInstancesWithConstructors["AudioLimiter"];
declare const AudioListener: CreatableInstancesWithConstructors["AudioListener"];
declare const AudioPitchShifter: CreatableInstancesWithConstructors["AudioPitchShifter"];
declare const AudioPlayer: CreatableInstancesWithConstructors["AudioPlayer"];
declare const AudioReverb: CreatableInstancesWithConstructors["AudioReverb"];
declare const AudioSearchParams: CreatableInstancesWithConstructors["AudioSearchParams"];
declare const AudioTextToSpeech: CreatableInstancesWithConstructors["AudioTextToSpeech"];
declare const AuroraScript: CreatableInstancesWithConstructors["AuroraScript"];
declare const Backpack: CreatableInstancesWithConstructors["Backpack"];
declare const BallSocketConstraint: CreatableInstancesWithConstructors["BallSocketConstraint"];
declare const Beam: CreatableInstancesWithConstructors["Beam"];
declare const BillboardGui: CreatableInstancesWithConstructors["BillboardGui"];
declare const BindableEvent: CreatableInstancesWithConstructors["BindableEvent"];
declare const BindableFunction: CreatableInstancesWithConstructors["BindableFunction"];
declare const BlockMesh: CreatableInstancesWithConstructors["BlockMesh"];
declare const BloomEffect: CreatableInstancesWithConstructors["BloomEffect"];
declare const BlurEffect: CreatableInstancesWithConstructors["BlurEffect"];
declare const BodyAngularVelocity: CreatableInstancesWithConstructors["BodyAngularVelocity"];
declare const BodyColors: CreatableInstancesWithConstructors["BodyColors"];
declare const BodyForce: CreatableInstancesWithConstructors["BodyForce"];
declare const BodyGyro: CreatableInstancesWithConstructors["BodyGyro"];
declare const BodyPartDescription: CreatableInstancesWithConstructors["BodyPartDescription"];
declare const BodyPosition: CreatableInstancesWithConstructors["BodyPosition"];
declare const BodyThrust: CreatableInstancesWithConstructors["BodyThrust"];
declare const BodyVelocity: CreatableInstancesWithConstructors["BodyVelocity"];
declare const Bone: CreatableInstancesWithConstructors["Bone"];
declare const BoolValue: CreatableInstancesWithConstructors["BoolValue"];
declare const BoxHandleAdornment: CreatableInstancesWithConstructors["BoxHandleAdornment"];
declare const Breakpoint: CreatableInstancesWithConstructors["Breakpoint"];
declare const BrickColorValue: CreatableInstancesWithConstructors["BrickColorValue"];
declare const BubbleChatMessageProperties: CreatableInstancesWithConstructors["BubbleChatMessageProperties"];
declare const BuoyancySensor: CreatableInstancesWithConstructors["BuoyancySensor"];
declare const CFrameValue: CreatableInstancesWithConstructors["CFrameValue"];
declare const Camera: CreatableInstancesWithConstructors["Camera"];
declare const CanvasGroup: CreatableInstancesWithConstructors["CanvasGroup"];
declare const CharacterMesh: CreatableInstancesWithConstructors["CharacterMesh"];
declare const ChorusSoundEffect: CreatableInstancesWithConstructors["ChorusSoundEffect"];
declare const ClickDetector: CreatableInstancesWithConstructors["ClickDetector"];
declare const ClimbController: CreatableInstancesWithConstructors["ClimbController"];
declare const Clouds: CreatableInstancesWithConstructors["Clouds"];
declare const Color3Value: CreatableInstancesWithConstructors["Color3Value"];
declare const ColorCorrectionEffect: CreatableInstancesWithConstructors["ColorCorrectionEffect"];
declare const ColorGradingEffect: CreatableInstancesWithConstructors["ColorGradingEffect"];
declare const CompressorSoundEffect: CreatableInstancesWithConstructors["CompressorSoundEffect"];
declare const ConeHandleAdornment: CreatableInstancesWithConstructors["ConeHandleAdornment"];
declare const Configuration: CreatableInstancesWithConstructors["Configuration"];
declare const ControllerManager: CreatableInstancesWithConstructors["ControllerManager"];
declare const ControllerPartSensor: CreatableInstancesWithConstructors["ControllerPartSensor"];
declare const CornerWedgePart: CreatableInstancesWithConstructors["CornerWedgePart"];
declare const CurveAnimation: CreatableInstancesWithConstructors["CurveAnimation"];
declare const CustomLog: CreatableInstancesWithConstructors["CustomLog"];
declare const CylinderHandleAdornment: CreatableInstancesWithConstructors["CylinderHandleAdornment"];
declare const CylinderMesh: CreatableInstancesWithConstructors["CylinderMesh"];
declare const CylindricalConstraint: CreatableInstancesWithConstructors["CylindricalConstraint"];
declare const DataStoreGetOptions: CreatableInstancesWithConstructors["DataStoreGetOptions"];
declare const DataStoreIncrementOptions: CreatableInstancesWithConstructors["DataStoreIncrementOptions"];
declare const DataStoreOptions: CreatableInstancesWithConstructors["DataStoreOptions"];
declare const DataStoreSetOptions: CreatableInstancesWithConstructors["DataStoreSetOptions"];
declare const Decal: CreatableInstancesWithConstructors["Decal"];
declare const DepthOfFieldEffect: CreatableInstancesWithConstructors["DepthOfFieldEffect"];
declare const Dialog: CreatableInstancesWithConstructors["Dialog"];
declare const DialogChoice: CreatableInstancesWithConstructors["DialogChoice"];
declare const DistortionSoundEffect: CreatableInstancesWithConstructors["DistortionSoundEffect"];
declare const DoubleConstrainedValue: CreatableInstancesWithConstructors["DoubleConstrainedValue"];
declare const DragDetector: CreatableInstancesWithConstructors["DragDetector"];
declare const Dragger: CreatableInstancesWithConstructors["Dragger"];
declare const EchoSoundEffect: CreatableInstancesWithConstructors["EchoSoundEffect"];
declare const EqualizerSoundEffect: CreatableInstancesWithConstructors["EqualizerSoundEffect"];
declare const EulerRotationCurve: CreatableInstancesWithConstructors["EulerRotationCurve"];
declare const ExperienceInviteOptions: CreatableInstancesWithConstructors["ExperienceInviteOptions"];
declare const ExplorerFilter: CreatableInstancesWithConstructors["ExplorerFilter"];
declare const Explosion: CreatableInstancesWithConstructors["Explosion"];
declare const FaceControls: CreatableInstancesWithConstructors["FaceControls"];
declare const FileMesh: CreatableInstancesWithConstructors["FileMesh"];
declare const Fire: CreatableInstancesWithConstructors["Fire"];
declare const FlangeSoundEffect: CreatableInstancesWithConstructors["FlangeSoundEffect"];
declare const FloatCurve: CreatableInstancesWithConstructors["FloatCurve"];
declare const FloorWire: CreatableInstancesWithConstructors["FloorWire"];
declare const FluidForceSensor: CreatableInstancesWithConstructors["FluidForceSensor"];
declare const Folder: CreatableInstancesWithConstructors["Folder"];
declare const ForceField: CreatableInstancesWithConstructors["ForceField"];
declare const Frame: CreatableInstancesWithConstructors["Frame"];
declare const GetTextBoundsParams: CreatableInstancesWithConstructors["GetTextBoundsParams"];
declare const Glue: CreatableInstancesWithConstructors["Glue"];
declare const GroundController: CreatableInstancesWithConstructors["GroundController"];
declare const Handles: CreatableInstancesWithConstructors["Handles"];
declare const HapticEffect: CreatableInstancesWithConstructors["HapticEffect"];
declare const Hat: CreatableInstancesWithConstructors["Hat"];
declare const HiddenSurfaceRemovalAsset: CreatableInstancesWithConstructors["HiddenSurfaceRemovalAsset"];
declare const Highlight: CreatableInstancesWithConstructors["Highlight"];
declare const HingeConstraint: CreatableInstancesWithConstructors["HingeConstraint"];
declare const Hole: CreatableInstancesWithConstructors["Hole"];
declare const Humanoid: CreatableInstancesWithConstructors["Humanoid"];
declare const HumanoidController: CreatableInstancesWithConstructors["HumanoidController"];
declare const HumanoidDescription: CreatableInstancesWithConstructors["HumanoidDescription"];
declare const HumanoidRigDescription: CreatableInstancesWithConstructors["HumanoidRigDescription"];
declare const IKControl: CreatableInstancesWithConstructors["IKControl"];
declare const ImageButton: CreatableInstancesWithConstructors["ImageButton"];
declare const ImageHandleAdornment: CreatableInstancesWithConstructors["ImageHandleAdornment"];
declare const ImageLabel: CreatableInstancesWithConstructors["ImageLabel"];
declare const InputAction: CreatableInstancesWithConstructors["InputAction"];
declare const InputBinding: CreatableInstancesWithConstructors["InputBinding"];
declare const InputContext: CreatableInstancesWithConstructors["InputContext"];
declare const IntConstrainedValue: CreatableInstancesWithConstructors["IntConstrainedValue"];
declare const IntValue: CreatableInstancesWithConstructors["IntValue"];
declare const InternalSyncItem: CreatableInstancesWithConstructors["InternalSyncItem"];
declare const IntersectOperation: CreatableInstancesWithConstructors["IntersectOperation"];
declare const Keyframe: CreatableInstancesWithConstructors["Keyframe"];
declare const KeyframeMarker: CreatableInstancesWithConstructors["KeyframeMarker"];
declare const KeyframeSequence: CreatableInstancesWithConstructors["KeyframeSequence"];
declare const LineForce: CreatableInstancesWithConstructors["LineForce"];
declare const LineHandleAdornment: CreatableInstancesWithConstructors["LineHandleAdornment"];
declare const LinearVelocity: CreatableInstancesWithConstructors["LinearVelocity"];
declare const LocalScript: CreatableInstancesWithConstructors["LocalScript"];
declare const LocalizationTable: CreatableInstancesWithConstructors["LocalizationTable"];
declare const ManualGlue: CreatableInstancesWithConstructors["ManualGlue"];
declare const ManualWeld: CreatableInstancesWithConstructors["ManualWeld"];
declare const MarkerCurve: CreatableInstancesWithConstructors["MarkerCurve"];
declare const MaterialVariant: CreatableInstancesWithConstructors["MaterialVariant"];
declare const MeshPart: CreatableInstancesWithConstructors["MeshPart"];
declare const Model: CreatableInstancesWithConstructors["Model"];
declare const ModuleScript: CreatableInstancesWithConstructors["ModuleScript"];
declare const Motor: CreatableInstancesWithConstructors["Motor"];
declare const Motor6D: CreatableInstancesWithConstructors["Motor6D"];
declare const MotorFeature: CreatableInstancesWithConstructors["MotorFeature"];
declare const NegateOperation: CreatableInstancesWithConstructors["NegateOperation"];
declare const NoCollisionConstraint: CreatableInstancesWithConstructors["NoCollisionConstraint"];
declare const Noise: CreatableInstancesWithConstructors["Noise"];
declare const NumberPose: CreatableInstancesWithConstructors["NumberPose"];
declare const NumberValue: CreatableInstancesWithConstructors["NumberValue"];
declare const ObjectValue: CreatableInstancesWithConstructors["ObjectValue"];
declare const OperationGraph: CreatableInstancesWithConstructors["OperationGraph"];
declare const Pants: CreatableInstancesWithConstructors["Pants"];
declare const Part: CreatableInstancesWithConstructors["Part"];
declare const PartOperation: CreatableInstancesWithConstructors["PartOperation"];
declare const ParticleEmitter: CreatableInstancesWithConstructors["ParticleEmitter"];
declare const Path2D: CreatableInstancesWithConstructors["Path2D"];
declare const PathfindingLink: CreatableInstancesWithConstructors["PathfindingLink"];
declare const PathfindingModifier: CreatableInstancesWithConstructors["PathfindingModifier"];
declare const PitchShiftSoundEffect: CreatableInstancesWithConstructors["PitchShiftSoundEffect"];
declare const Plane: CreatableInstancesWithConstructors["Plane"];
declare const PlaneConstraint: CreatableInstancesWithConstructors["PlaneConstraint"];
declare const PluginCapabilities: CreatableInstancesWithConstructors["PluginCapabilities"];
declare const PointLight: CreatableInstancesWithConstructors["PointLight"];
declare const Pose: CreatableInstancesWithConstructors["Pose"];
declare const PrismaticConstraint: CreatableInstancesWithConstructors["PrismaticConstraint"];
declare const ProximityPrompt: CreatableInstancesWithConstructors["ProximityPrompt"];
declare const RTAnimationTracker: CreatableInstancesWithConstructors["RTAnimationTracker"];
declare const RayValue: CreatableInstancesWithConstructors["RayValue"];
declare const RelativeGui: CreatableInstancesWithConstructors["RelativeGui"];
declare const RemoteEvent: CreatableInstancesWithConstructors["RemoteEvent"];
declare const RemoteFunction: CreatableInstancesWithConstructors["RemoteFunction"];
declare const ReverbSoundEffect: CreatableInstancesWithConstructors["ReverbSoundEffect"];
declare const RigidConstraint: CreatableInstancesWithConstructors["RigidConstraint"];
declare const RocketPropulsion: CreatableInstancesWithConstructors["RocketPropulsion"];
declare const RodConstraint: CreatableInstancesWithConstructors["RodConstraint"];
declare const RopeConstraint: CreatableInstancesWithConstructors["RopeConstraint"];
declare const Rotate: CreatableInstancesWithConstructors["Rotate"];
declare const RotateP: CreatableInstancesWithConstructors["RotateP"];
declare const RotateV: CreatableInstancesWithConstructors["RotateV"];
declare const RotationCurve: CreatableInstancesWithConstructors["RotationCurve"];
declare const ScreenGui: CreatableInstancesWithConstructors["ScreenGui"];
declare const Script: CreatableInstancesWithConstructors["Script"];
declare const ScrollingFrame: CreatableInstancesWithConstructors["ScrollingFrame"];
declare const Seat: CreatableInstancesWithConstructors["Seat"];
declare const SelectionBox: CreatableInstancesWithConstructors["SelectionBox"];
declare const SelectionPartLasso: CreatableInstancesWithConstructors["SelectionPartLasso"];
declare const SelectionPointLasso: CreatableInstancesWithConstructors["SelectionPointLasso"];
declare const SelectionSphere: CreatableInstancesWithConstructors["SelectionSphere"];
declare const Shirt: CreatableInstancesWithConstructors["Shirt"];
declare const ShirtGraphic: CreatableInstancesWithConstructors["ShirtGraphic"];
declare const SkateboardController: CreatableInstancesWithConstructors["SkateboardController"];
declare const SkateboardPlatform: CreatableInstancesWithConstructors["SkateboardPlatform"];
declare const Sky: CreatableInstancesWithConstructors["Sky"];
declare const Smoke: CreatableInstancesWithConstructors["Smoke"];
declare const Snap: CreatableInstancesWithConstructors["Snap"];
declare const Sound: CreatableInstancesWithConstructors["Sound"];
declare const SoundGroup: CreatableInstancesWithConstructors["SoundGroup"];
declare const Sparkles: CreatableInstancesWithConstructors["Sparkles"];
declare const SpawnLocation: CreatableInstancesWithConstructors["SpawnLocation"];
declare const SpecialMesh: CreatableInstancesWithConstructors["SpecialMesh"];
declare const SphereHandleAdornment: CreatableInstancesWithConstructors["SphereHandleAdornment"];
declare const SpotLight: CreatableInstancesWithConstructors["SpotLight"];
declare const SpringConstraint: CreatableInstancesWithConstructors["SpringConstraint"];
declare const StarterGear: CreatableInstancesWithConstructors["StarterGear"];
declare const StringValue: CreatableInstancesWithConstructors["StringValue"];
declare const StudioAttachment: CreatableInstancesWithConstructors["StudioAttachment"];
declare const StudioCallout: CreatableInstancesWithConstructors["StudioCallout"];
declare const StyleDerive: CreatableInstancesWithConstructors["StyleDerive"];
declare const StyleLink: CreatableInstancesWithConstructors["StyleLink"];
declare const StyleRule: CreatableInstancesWithConstructors["StyleRule"];
declare const StyleSheet: CreatableInstancesWithConstructors["StyleSheet"];
declare const SunRaysEffect: CreatableInstancesWithConstructors["SunRaysEffect"];
declare const SurfaceAppearance: CreatableInstancesWithConstructors["SurfaceAppearance"];
declare const SurfaceGui: CreatableInstancesWithConstructors["SurfaceGui"];
declare const SurfaceLight: CreatableInstancesWithConstructors["SurfaceLight"];
declare const SurfaceSelection: CreatableInstancesWithConstructors["SurfaceSelection"];
declare const SwimController: CreatableInstancesWithConstructors["SwimController"];
declare const Team: CreatableInstancesWithConstructors["Team"];
declare const TeleportOptions: CreatableInstancesWithConstructors["TeleportOptions"];
declare const TerrainDetail: CreatableInstancesWithConstructors["TerrainDetail"];
declare const TerrainRegion: CreatableInstancesWithConstructors["TerrainRegion"];
declare const TextBox: CreatableInstancesWithConstructors["TextBox"];
declare const TextButton: CreatableInstancesWithConstructors["TextButton"];
declare const TextChannel: CreatableInstancesWithConstructors["TextChannel"];
declare const TextChatCommand: CreatableInstancesWithConstructors["TextChatCommand"];
declare const TextChatMessageProperties: CreatableInstancesWithConstructors["TextChatMessageProperties"];
declare const TextLabel: CreatableInstancesWithConstructors["TextLabel"];
declare const Texture: CreatableInstancesWithConstructors["Texture"];
declare const Tool: CreatableInstancesWithConstructors["Tool"];
declare const Torque: CreatableInstancesWithConstructors["Torque"];
declare const TorsionSpringConstraint: CreatableInstancesWithConstructors["TorsionSpringConstraint"];
declare const TrackerStreamAnimation: CreatableInstancesWithConstructors["TrackerStreamAnimation"];
declare const Trail: CreatableInstancesWithConstructors["Trail"];
declare const TremoloSoundEffect: CreatableInstancesWithConstructors["TremoloSoundEffect"];
declare const TrussPart: CreatableInstancesWithConstructors["TrussPart"];
declare const UIAspectRatioConstraint: CreatableInstancesWithConstructors["UIAspectRatioConstraint"];
declare const UICorner: CreatableInstancesWithConstructors["UICorner"];
declare const UIDragDetector: CreatableInstancesWithConstructors["UIDragDetector"];
declare const UIFlexItem: CreatableInstancesWithConstructors["UIFlexItem"];
declare const UIGradient: CreatableInstancesWithConstructors["UIGradient"];
declare const UIGridLayout: CreatableInstancesWithConstructors["UIGridLayout"];
declare const UIListLayout: CreatableInstancesWithConstructors["UIListLayout"];
declare const UIPadding: CreatableInstancesWithConstructors["UIPadding"];
declare const UIPageLayout: CreatableInstancesWithConstructors["UIPageLayout"];
declare const UIScale: CreatableInstancesWithConstructors["UIScale"];
declare const UISizeConstraint: CreatableInstancesWithConstructors["UISizeConstraint"];
declare const UIStroke: CreatableInstancesWithConstructors["UIStroke"];
declare const UITableLayout: CreatableInstancesWithConstructors["UITableLayout"];
declare const UITextSizeConstraint: CreatableInstancesWithConstructors["UITextSizeConstraint"];
declare const UnionOperation: CreatableInstancesWithConstructors["UnionOperation"];
declare const UniversalConstraint: CreatableInstancesWithConstructors["UniversalConstraint"];
declare const UnreliableRemoteEvent: CreatableInstancesWithConstructors["UnreliableRemoteEvent"];
declare const Vector3Curve: CreatableInstancesWithConstructors["Vector3Curve"];
declare const Vector3Value: CreatableInstancesWithConstructors["Vector3Value"];
declare const VectorForce: CreatableInstancesWithConstructors["VectorForce"];
declare const VehicleController: CreatableInstancesWithConstructors["VehicleController"];
declare const VehicleSeat: CreatableInstancesWithConstructors["VehicleSeat"];
declare const VelocityMotor: CreatableInstancesWithConstructors["VelocityMotor"];
declare const VideoDeviceInput: CreatableInstancesWithConstructors["VideoDeviceInput"];
declare const VideoDisplay: CreatableInstancesWithConstructors["VideoDisplay"];
declare const VideoFrame: CreatableInstancesWithConstructors["VideoFrame"];
declare const VideoPlayer: CreatableInstancesWithConstructors["VideoPlayer"];
declare const ViewportFrame: CreatableInstancesWithConstructors["ViewportFrame"];
declare const VisualizationMode: CreatableInstancesWithConstructors["VisualizationMode"];
declare const VisualizationModeCategory: CreatableInstancesWithConstructors["VisualizationModeCategory"];
declare const WedgePart: CreatableInstancesWithConstructors["WedgePart"];
declare const Weld: CreatableInstancesWithConstructors["Weld"];
declare const WeldConstraint: CreatableInstancesWithConstructors["WeldConstraint"];
declare const Wire: CreatableInstancesWithConstructors["Wire"];
declare const WireframeHandleAdornment: CreatableInstancesWithConstructors["WireframeHandleAdornment"];
declare const WorkspaceAnnotation: CreatableInstancesWithConstructors["WorkspaceAnnotation"];
declare const WorldModel: CreatableInstancesWithConstructors["WorldModel"];
declare const WrapDeformer: CreatableInstancesWithConstructors["WrapDeformer"];
declare const WrapLayer: CreatableInstancesWithConstructors["WrapLayer"];
declare const WrapTarget: CreatableInstancesWithConstructors["WrapTarget"];
+6
View File
@@ -0,0 +1,6 @@
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
testMatch: ['**/test/**/*.test.ts'],
moduleFileExtensions: ['ts', 'js'],
};
+7110 -1272
View File
File diff suppressed because it is too large Load Diff
+26 -19
View File
@@ -1,33 +1,40 @@
{
"name": "rbxts-transformer-services",
"version": "1.1.1",
"description": "A transformer that converts @rbxts/services imports into plain GetService calls.",
"name": "rbxts-transformer-instances",
"version": "1.0.0",
"description": "Enable the use of instance constructors, reducing boilerplate.",
"main": "out/index.js",
"author": "Fireboltofdeath <fireboltofpublic@gmail.com> (https://github.com/Fireboltofdeath)",
"license": "ISC",
"author": "evilbocchi",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/FireTS/rbxts-transformer-services.git"
},
"peerDependencies": {
"typescript": "^5.2.2"
"url": "https://github.com/evilbocchi/rbxts-transformer-instances.git"
},
"scripts": {
"prepare": "npm run build",
"prepare": "npm run generate-declarations && npm run build",
"build": "tsc",
"watch": "tsc -w"
"watch": "tsc -w",
"test": "jest",
"generate-declarations": "node scripts/generate-declarations.js"
},
"dependencies": {
"typescript": "^5.5.3"
},
"devDependencies": {
"@rbxts/types": "^1.0.845",
"@types/jest": "^29.5.14",
"@types/node": "^18.18.6",
"@types/ts-expose-internals": "npm:ts-expose-internals@5.2.2",
"@typescript-eslint/eslint-plugin": "^4.9.1",
"@typescript-eslint/parser": "^4.9.1",
"eslint": "^7.15.0",
"eslint-config-prettier": "^7.0.0",
"eslint-plugin-prettier": "^3.2.0",
"prettier": "^2.2.1"
"@typescript-eslint/eslint-plugin": "^8.5.0",
"@typescript-eslint/parser": "^8.5.0",
"eslint": "^8.57.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.2.1",
"jest": "^29.7.0",
"ts-jest": "^29.3.1"
},
"files": [
"out"
]
"out",
"index.d.ts"
],
"types": "index.d.ts"
}
+154
View File
@@ -0,0 +1,154 @@
/**
* Script to generate declaration statements for all creatable Roblox instances
* This will produce declarations in the format:
* declare const INSTANCENAME: CreatableInstancesWithConstructors["INSTANCENAME"]
*/
const fs = require('fs');
const path = require('path');
const { execSync } = require('child_process');
/**
* Get all creatable instance class names by examining the @rbxts/types package
*/
function getCreatableInstanceNames() {
try {
// Find the location of @rbxts/types in node_modules
let typesPath;
try {
// Try to find the path to @rbxts/types
typesPath = path.dirname(require.resolve('@rbxts/types/package.json'));
} catch (e) {
console.error('Could not find @rbxts/types. Make sure it is installed.');
process.exit(1);
}
// Read the CreatableInstances interface definition file
// This could be in a few different locations depending on the package structure
let instancesFile;
const possiblePaths = [
path.join(typesPath, 'include', 'generated', 'None.d.ts'),
path.join(typesPath, 'types', 'generated', 'None.d.ts'),
path.join(typesPath, 'generated', 'None.d.ts')
];
for (const filePath of possiblePaths) {
if (fs.existsSync(filePath)) {
instancesFile = filePath;
break;
}
}
if (!instancesFile) {
throw new Error('Could not find None.d.ts file in @rbxts/types');
}
// Read the file content
const content = fs.readFileSync(instancesFile, 'utf-8');
// Extract interface names that appear in CreatableInstances
// This regex might need adjustment depending on the exact format
const creatableInstancesRegex = /interface CreatableInstances \{([\s\S]*?)\}/;
const creatablesMatch = content.match(creatableInstancesRegex);
if (!creatablesMatch || !creatablesMatch[1]) {
throw new Error('Could not find CreatableInstances interface in the types file');
}
// Extract class names by looking for patterns like "ClassName: ClassName;"
const classNameRegex = /([A-Za-z0-9_]+)\s*:\s*([A-Za-z0-9_]+);/g;
const classNames = [];
let match;
while ((match = classNameRegex.exec(creatablesMatch[1])) !== null) {
classNames.push(match[1]);
}
return classNames.sort();
} catch (error) {
console.error('Error extracting instance names:', error);
// Fallback to a predefined list of common instances
console.log('Falling back to predefined list of common instances...');
return [
"Accessory", "Accoutrement", "Animation", "Attachment", "BillboardGui",
"BindableEvent", "BindableFunction", "BlockMesh", "BodyAngularVelocity",
"BodyForce", "BodyGyro", "BodyPosition", "BodyThrust", "BodyVelocity",
"BoolValue", "BrickColorValue", "Camera", "CFrameValue", "ClickDetector",
"Clothing", "Color3Value", "ColorCorrectionEffect", "Configuration",
"CornerWedgePart", "Decal", "Dialog", "DialogChoice", "DoubleConstrainedValue",
"Explosion", "Folder", "ForceField", "Frame", "GuiButton", "GuiObject",
"Highlight", "Humanoid", "HumanoidDescription", "ImageButton", "ImageLabel",
"IntConstrainedValue", "IntValue", "LocalScript", "MeshPart", "Model",
"ModuleScript", "Motor", "Motor6D", "NumberValue", "ObjectValue", "Part",
"ParticleEmitter", "ProximityPrompt", "RayValue", "RemoteEvent", "RemoteFunction",
"ScreenGui", "Script", "ScrollingFrame", "Seat", "Sky", "Sound", "SpecialMesh",
"SpotLight", "StringValue", "SurfaceGui", "TextBox", "TextButton",
"TextLabel", "Texture", "Tool", "TouchTransmitter", "Trail", "TrussPart",
"UIAspectRatioConstraint", "UICorner", "UIGradient", "UIGridLayout",
"UIListLayout", "UIPadding", "UIPageLayout", "UIScale", "UIStroke",
"UITableLayout", "UITextSizeConstraint", "UnionOperation", "Vector3Value",
"VectorForce", "VideoFrame", "WedgePart", "Weld"
].sort();
}
}
/**
* Generate the declarations file content
* @param {string[]} classNames List of creatable instance class names
* @returns {string} The content for the declarations file
*/
function generateDeclarations(classNames) {
// Header part
let content = `/// <reference no-default-lib="true"/>
/// <reference types="@rbxts/types" />
/**
* Add constructors to all Roblox CreatableInstances
* This adds \`new ClassName()\` constructors for all Roblox instance types
* that can be created with Instance.new()
*
* The transformer will convert these to \`new Instance("ClassName")\`
*/
type CreatableInstancesWithConstructors = {
[K in keyof CreatableInstances]: {
new (...args: any[]): CreatableInstances[K];
}
}
declare const CreatableInstancesWithConstructors: CreatableInstancesWithConstructors;
`;
// Add declarations for each class
for (const className of classNames) {
content += `declare const ${className}: CreatableInstancesWithConstructors["${className}"];\n`;
}
return content;
}
/**
* Main function
*/
function main() {
const classNames = getCreatableInstanceNames();
console.log(`Found ${classNames.length} creatable instance types`);
const declarationsContent = generateDeclarations(classNames);
const outputPath = path.join(__dirname, '..', 'index.d.ts');
// Write to file
fs.writeFileSync(outputPath, declarationsContent, 'utf-8');
console.log(`Successfully generated declarations in ${outputPath}`);
console.log('Example declarations:');
const examples = classNames.slice(0, 5);
for (const className of examples) {
console.log(`declare const ${className}: CreatableInstancesWithConstructors["${className}"];`);
}
console.log(`...and ${classNames.length - 5} more`);
}
// Run the script
main();
+62 -92
View File
@@ -4,7 +4,7 @@ import ts from "typescript";
* This is the transformer's configuration, the values are passed from the tsconfig.
*/
export interface TransformerConfig {
_: void;
_: void;
}
/**
@@ -13,105 +13,75 @@ export interface TransformerConfig {
* You can also use this object to store state, e.g prereqs.
*/
export class TransformContext {
public factory: ts.NodeFactory;
public factory: ts.NodeFactory;
constructor(
public program: ts.Program,
public context: ts.TransformationContext,
public config: TransformerConfig,
) {
this.factory = context.factory;
}
constructor(
public program: ts.Program,
public context: ts.TransformationContext,
public config: TransformerConfig,
) {
this.factory = context.factory;
}
/**
* Transforms the children of the specified node.
*/
transform<T extends ts.Node>(node: T): T {
return ts.visitEachChild(node, (node) => visitNode(this, node), this.context);
}
}
function visitImportDeclaration(context: TransformContext, node: ts.ImportDeclaration) {
const { factory } = context;
const path = node.moduleSpecifier;
const clause = node.importClause;
if (!clause) return node;
if (!ts.isStringLiteral(path)) return node;
if (path.text !== "@rbxts/services") return node;
const namedBindings = clause.namedBindings;
if (!namedBindings) return node;
if (!ts.isNamedImports(namedBindings)) return node;
return [
// We replace the import declaration instead of stripping it to prevent
// issues with isolated modules.
factory.updateImportDeclaration(
node,
undefined,
factory.createImportClause(false, undefined, factory.createNamedImports([])),
node.moduleSpecifier,
undefined,
),
// Creates a multi-variable statement as shown below.
//
// const Players = game.GetService("Players"),
// Workspace = game.GetService("Workspace");
factory.createVariableStatement(
undefined,
factory.createVariableDeclarationList(
namedBindings.elements.map((specifier) => {
const serviceName = specifier.propertyName ? specifier.propertyName.text : specifier.name.text;
const variableName = specifier.name;
return factory.createVariableDeclaration(
variableName,
undefined,
undefined,
factory.createCallExpression(
factory.createPropertyAccessExpression(factory.createIdentifier("game"), "GetService"),
undefined,
[factory.createStringLiteral(serviceName)],
),
);
}),
ts.NodeFlags.Const,
),
),
];
}
function visitStatement(context: TransformContext, node: ts.Statement): ts.Statement | ts.Statement[] {
// This is used to transform statements.
// TypeScript allows you to return multiple statements here.
if (ts.isImportDeclaration(node)) {
// We have encountered an import declaration,
// so we should transform it using a separate function.
return visitImportDeclaration(context, node);
}
return context.transform(node);
/**
* Transforms the children of the specified node.
*/
transform<T extends ts.Node>(node: T): T {
return ts.visitEachChild(node, (node) => visitNode(this, node), this.context);
}
}
function visitExpression(context: TransformContext, node: ts.Expression): ts.Expression {
// This can be used to transform expressions
// For example, a call expression for macros.
// Check for new expressions (new Part())
if (ts.isNewExpression(node)) {
const { expression, arguments: args } = node;
return context.transform(node);
// Check if it's a simple identifier (e.g., Part, not Something.Part)
if (ts.isIdentifier(expression)) {
const className = expression.text;
// Check if it's an Instance-derived class (assuming basic Roblox classes for now)
const instanceClasses = [
"Part", "Model", "Folder", "Script", "LocalScript", "ModuleScript",
"Frame", "TextLabel", "TextButton", "ImageLabel", "ImageButton",
"Sound", "Animation", "BoolValue", "StringValue", "NumberValue",
// Add more Roblox classes as needed
];
if (instanceClasses.includes(className)) {
const { factory } = context;
// Create new Instance("ClassName") expression
return factory.createNewExpression(
factory.createIdentifier("Instance"),
undefined,
[
factory.createStringLiteral(className),
...(args || [])
]
);
}
}
}
// For other expressions, continue traversing
return context.transform(node);
}
function visitNode(context: TransformContext, node: ts.Node): ts.Node | ts.Node[] {
if (ts.isStatement(node)) {
return visitStatement(context, node);
} else if (ts.isExpression(node)) {
return visitExpression(context, node);
}
if (ts.isExpression(node)) {
return visitExpression(context, node);
}
// We encountered a node that we don't handle above,
// but we should keep iterating the AST in case we find something we want to transform.
return context.transform(node);
// Continue traversing the AST
return context.transform(node);
}
export default function(program: ts.Program, config: TransformerConfig = { _: undefined }) {
return (context: ts.TransformationContext) => {
const transformContext = new TransformContext(program, context, config);
return (file: ts.SourceFile) => {
return ts.visitNode(file, (node) => visitNode(transformContext, node));
};
};
}
+6
View File
@@ -0,0 +1,6 @@
/// <reference path="index.d.ts" />
type hi = CreatableInstancesWithConstructors;
new Frame()
new Part().Name = "hi";
+64
View File
@@ -0,0 +1,64 @@
import * as ts from 'typescript';
import transformer from '../src/transformer';
function transpile(source: string): string {
const program = ts.createProgram([], {});
const result = ts.transpileModule(source, {
compilerOptions: {
target: ts.ScriptTarget.ESNext,
module: ts.ModuleKind.CommonJS,
},
transformers: {
before: [transformer(program) as ts.TransformerFactory<ts.SourceFile>]
}
});
return result.outputText;
}
describe('rbxts-transformer-instances', () => {
test('transforms new Part() to new Instance("Part")', () => {
const source = `const part = new Part();`;
const result = transpile(source);
expect(result).toContain('const part = new Instance("Part")');
});
test('transforms new instances with arguments', () => {
const source = `const model = new Model("MyModel");`;
const result = transpile(source);
expect(result).toContain('const model = new Instance("Model", "MyModel")');
});
test('preserves non-instance constructors', () => {
const source = `const obj = new CustomClass();`;
const result = transpile(source);
expect(result).toContain('const obj = new CustomClass()');
});
test('handles multiple constructors in the same file', () => {
const source = `
const part = new Part();
const obj = new CustomClass();
const folder = new Folder("MyFolder");
`;
const result = transpile(source);
expect(result).toContain('const part = new Instance("Part")');
expect(result).toContain('const obj = new CustomClass()');
expect(result).toContain('const folder = new Instance("Folder", "MyFolder")');
});
test('transforms instances in complex expressions', () => {
const source = `
function createParts() {
return [new Part(), new Part(), new Model("MyModel")];
}
`;
const result = transpile(source);
expect(result).toContain('return [new Instance("Part"), new Instance("Part"), new Instance("Model", "MyModel")]');
});
});
+18 -7
View File
@@ -1,16 +1,27 @@
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"outDir": "out",
"rootDir": "src",
"allowSyntheticDefaultImports": true,
"downlevelIteration": true,
"module": "commonjs",
"moduleResolution": "node",
"resolveJsonModule": true,
"experimentalDecorators": true,
"forceConsistentCasingInFileNames": true,
"moduleDetection": "force",
"strict": true,
"target": "ES5",
"lib": [
"ES2015",
"DOM"
],
"skipLibCheck": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
}
"rootDir": "src",
"outDir": "out",
"baseUrl": "src",
"tsBuildInfoFile": "out/tsconfig.tsbuildinfo",
"declaration": true
},
"include": ["src/**/*", "index.d.ts"]
}