Initial commit
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"jsx": true,
|
||||
"useJSXTextNode": true,
|
||||
"ecmaVersion": 2018,
|
||||
"sourceType": "module",
|
||||
"project": "./tsconfig.json"
|
||||
},
|
||||
"plugins": [
|
||||
"@typescript-eslint",
|
||||
"prettier"
|
||||
],
|
||||
"extends": [
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"prettier/@typescript-eslint",
|
||||
"plugin:prettier/recommended"
|
||||
],
|
||||
"rules": {
|
||||
"prettier/prettier": [
|
||||
"warn",
|
||||
{
|
||||
"semi": true,
|
||||
"trailingComma": "all",
|
||||
"singleQuote": false,
|
||||
"printWidth": 120,
|
||||
"tabWidth": 4,
|
||||
"useTabs": true
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/no-explicit-any": [
|
||||
"warn",
|
||||
{
|
||||
"ignoreRestArgs": true
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/no-non-null-assertion": [
|
||||
"off"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
out
|
||||
node_modules
|
||||
@@ -0,0 +1,15 @@
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2020, Fireboltofdeath
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
@@ -0,0 +1,38 @@
|
||||
# rbxts-transformer-services
|
||||
This is a [demo transformer](#template) that converts @rbxts/services imports into plain GetService calls for increased legibility.
|
||||
|
||||
## Example
|
||||
```ts
|
||||
// input.ts
|
||||
import { Players, ServerScriptService } from "@rbxts/services";
|
||||
|
||||
print(Players.LocalPlayer);
|
||||
print(ServerScriptService.GetChildren().size());
|
||||
```
|
||||
|
||||
```lua
|
||||
-- output.lua
|
||||
local Players = game:GetService("Players")
|
||||
local ServerScriptService = game:GetService("ServerScriptService")
|
||||
print(Players.LocalPlayer)
|
||||
print(#ServerScriptService:GetChildren())
|
||||
```
|
||||
|
||||
# Template
|
||||
This transformer is intended to be used as a template for those who are interested in creating their own transformers in roblox-ts.
|
||||
|
||||
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.
|
||||
Generated
+2966
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"name": "rbxts-transformer-services",
|
||||
"version": "1.1.1",
|
||||
"description": "A transformer that converts @rbxts/services imports into plain GetService calls.",
|
||||
"main": "out/index.js",
|
||||
"author": "Fireboltofdeath <fireboltofpublic@gmail.com> (https://github.com/Fireboltofdeath)",
|
||||
"license": "ISC",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/FireTS/rbxts-transformer-services.git"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "^5.2.2"
|
||||
},
|
||||
"scripts": {
|
||||
"prepare": "npm run build",
|
||||
"build": "tsc",
|
||||
"watch": "tsc -w"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@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"
|
||||
},
|
||||
"files": [
|
||||
"out"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import ts from "typescript";
|
||||
import { TransformContext, TransformerConfig } from "./transformer";
|
||||
|
||||
/**
|
||||
* The transformer entry point.
|
||||
* This provides access to necessary resources and the user specified configuration.
|
||||
*/
|
||||
export default function (program: ts.Program, config: TransformerConfig) {
|
||||
return (transformationContext: ts.TransformationContext): ((file: ts.SourceFile) => ts.Node) => {
|
||||
const context = new TransformContext(program, transformationContext, config);
|
||||
return (file) => context.transform(file);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
import ts from "typescript";
|
||||
|
||||
/**
|
||||
* This is the transformer's configuration, the values are passed from the tsconfig.
|
||||
*/
|
||||
export interface TransformerConfig {
|
||||
_: void;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a utility object to pass around your dependencies.
|
||||
*
|
||||
* You can also use this object to store state, e.g prereqs.
|
||||
*/
|
||||
export class TransformContext {
|
||||
public factory: ts.NodeFactory;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
function visitExpression(context: TransformContext, node: ts.Expression): ts.Expression {
|
||||
// This can be used to transform expressions
|
||||
// For example, a call expression for macros.
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"module": "commonjs",
|
||||
"outDir": "out",
|
||||
"rootDir": "src",
|
||||
"strict": true,
|
||||
"lib": [
|
||||
"ES2015",
|
||||
"DOM"
|
||||
],
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user