rbxts-transformer-t
Version:
TypeScript transformer which converts TypeScript types to t entities
342 lines (341 loc) • 15.1 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __read = (this && this.__read) || function (o, n) {
var m = typeof Symbol === "function" && o[Symbol.iterator];
if (!m) return o;
var i = m.call(o), r, ar = [], e;
try {
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
}
catch (error) { e = { error: error }; }
finally {
try {
if (r && !r.done && (m = i["return"])) m.call(i);
}
finally { if (e) throw e.error; }
}
return ar;
};
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
if (ar || !(i in from)) {
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
ar[i] = from[i];
}
}
return to.concat(ar || Array.prototype.slice.call(from));
};
var __values = (this && this.__values) || function(o) {
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
if (m) return m.call(o);
if (o && typeof o.length === "number") return {
next: function () {
if (o && i >= o.length) o = void 0;
return { value: o && o[i++], done: !o };
}
};
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.buildType = exports.is_t_ImportDeclaration = exports.MARCO_NAME = exports.OBJECT_NAME = void 0;
var typescript_1 = __importStar(require("typescript"));
var get_installed_path_1 = require("get-installed-path");
var path_1 = __importDefault(require("path"));
var fs_1 = __importDefault(require("fs"));
var utility = __importStar(require("./utility"));
exports.OBJECT_NAME = "t";
exports.MARCO_NAME = "$terrify";
var typePath = (0, get_installed_path_1.getInstalledPathSync)("@rbxts/types", { local: true });
var instanceDefType = path_1.default.normalize(path_1.default.join(typePath, "include", "generated", "None.d.ts"));
function get_t_Path() {
try {
return (0, get_installed_path_1.getInstalledPathSync)("@rbxts/t", { local: true });
}
catch (_a) {
throw "[rbxts-transformer-t ERROR]: @rbxts/t must be installed for rbxts-transformer-t to work.";
}
}
var ROBLOX_TYPES = [
"Axes",
"BrickColor",
"CFrame",
"Color3",
"ColorSequence",
"ColorSequenceKeypoint",
"DockWidgetPluginGuiInfo",
"Faces",
"Instance",
"NumberRange",
"NumberSequence",
"NumberSequenceKeypoint",
"PathWaypoint",
"PhysicalProperties",
"Random",
"Ray",
"Rect",
"Region3",
"Region3int16",
"TweenInfo",
"UDim",
"UDim2",
"Vector2",
"Vector3",
"Vector3int16",
"RBXScriptSignal",
"RBXScriptConnection",
"Enum",
"EnumItem",
];
function createPropertyAccess(propertyName) {
return typescript_1.factory.createPropertyAccessExpression(typescript_1.factory.createIdentifier(exports.OBJECT_NAME), typescript_1.factory.createIdentifier(propertyName));
}
function createMethodCall(methodName, params) {
return typescript_1.factory.createCallExpression(createPropertyAccess(methodName), undefined, params);
}
function createLiteral(literal) {
return createMethodCall("literal", [literal]);
}
function createLiteralExpression(type, typeChecker) {
var stringType = typeChecker.typeToString(type);
if (stringType === "true" || stringType === "false") {
return stringType === "true" ? typescript_1.factory.createTrue() : typescript_1.factory.createFalse();
}
if (type.isStringLiteral())
return typescript_1.factory.createStringLiteral(type.value);
if (type.isNumberLiteral())
return typescript_1.factory.createNumericLiteral(type.value.toString());
}
function convertTypesArray(types, typeChecker) {
var transformNextType = function (types, result) {
if (types.length === 0)
return result;
var _a = __read(types), head = _a[0], tail = _a.slice(1);
var res = buildType(head, typeChecker);
return transformNextType(tail, __spreadArray(__spreadArray([], __read(result), false), [res], false));
};
return transformNextType(types, []);
}
/**
* Converts ts.UnionType to a ts.Expression
*/
function convertUnionType(type, typeChecker) {
var _a;
var nodes = (_a = type.aliasSymbol) === null || _a === void 0 ? void 0 : _a.declarations;
var types = nodes !== undefined && nodes.length !== 0
? nodes[0].type.types.map(typeChecker.getTypeFromTypeNode)
: type.types;
var _b = __read(utility.separateArray(types, utility.isLiteral(typeChecker)), 2), literalTypes = _b[0], notLiteralTypes = _b[1];
var literalExpression = literalTypes.length === 0 ?
undefined :
typescript_1.factory.createCallExpression(createPropertyAccess("literal"), undefined, literalTypes.map(function (type) { return createLiteralExpression(type, typeChecker); }));
var result = convertTypesArray(notLiteralTypes, typeChecker);
if (result.length === 0 && literalExpression)
return literalExpression;
if (literalExpression)
result.push(literalExpression);
return createMethodCall("union", result);
}
/**
* Converts ts.TupleType to a ts.Expression
*/
function convertTupleType(type, typeChecker) {
var result = convertTypesArray(type.resolvedTypeArguments, typeChecker);
return createMethodCall("strictArray", result);
}
/**
* Converts Array type to a ts.Expression
*/
function convertArrayType(type, typeChecker) {
var args = type.typeArguments;
if (args === undefined || args.length === 0)
throw new Error("Array must have type arguments");
var result = buildType(args[0], typeChecker);
return createMethodCall("array", [result]);
}
/**
* Converts Map type to a ts.Expression
*/
function convertMapType(type, typeChecker) {
var args = type.typeArguments;
if (args === undefined)
throw new Error("Map must have type arguments");
var result = convertTypesArray(args, typeChecker);
return createMethodCall("map", result);
}
/**
* Converts Array of object properties to t.interface Expression. If there are
* optional properties, they will be built as separate objects
* and mixed to main object using t.intersection function
*/
function convertObjectType(props, typeChecker) {
if (props.length === 0)
return createMethodCall("interface", [typescript_1.factory.createObjectLiteralExpression([])]);
var preparedProps = props.map(function (prop) {
var origin = prop.syntheticOrigin;
return origin !== undefined ? origin : prop;
});
// separate properties by weither
// property is optional or not
// and get 2 property lists
var _a = __read(utility.separateArray(preparedProps, utility.isOptionalPropertyDeclaration), 2), optionalProps = _a[0], nonOptionalProps = _a[1];
// Builds t.interface entity by property list
var handlePropList = function (props, isOptional) {
var handledProps = props.map(function (prop) { return utility.extractProperty(prop, typeChecker); });
var types = handledProps.map(function (prop) { return prop.type; });
var result = convertTypesArray(types, typeChecker);
var properties = result.map(function (p, i) { return typescript_1.factory.createPropertyAssignment(utility.buildPropertyName(handledProps[i].name), isOptional ? createMethodCall("optional", [p]) : p); });
return createMethodCall("interface", [typescript_1.factory.createObjectLiteralExpression(properties)]);
};
// Build 2 or 1 objects for each optional props object and add them to the result array
var result = [];
if (optionalProps.length !== 0)
result.push(handlePropList(optionalProps, true));
if (nonOptionalProps.length !== 0)
result.push(handlePropList(nonOptionalProps));
return result.length === 1
? result[0]
: createMethodCall("intersection", result);
}
/**
* Converts interface type to to ts.Expression
*/
function convertInterfaceType(type, typeChecker) {
var _a;
var props = (_a = type.declaredProperties) !== null && _a !== void 0 ? _a : [];
var object = convertObjectType(props, typeChecker);
var parents = type.symbol.declarations // TODO
.map(function (d) { return d.heritageClauses; })
.filter(Boolean)
.reduce(utility.mergeArrays, [])
.map(function (clause) { return clause.types; })
.reduce(utility.mergeArrays, [])
.map(typeChecker.getTypeFromTypeNode);
if (parents.length === 0)
return object;
var parentsTransformed = convertTypesArray(parents, typeChecker);
var nodesArray = __spreadArray([object], __read(parentsTransformed), false);
return createMethodCall("intersection", nodesArray);
}
function convertEnumType(type, typeChecker) {
var e_1, _a;
var enumDeclaration = type.symbol.valueDeclaration;
var result = [];
try {
for (var _b = __values(enumDeclaration.members), _c = _b.next(); !_c.done; _c = _b.next()) {
var member = _c.value;
var value = typeChecker.getConstantValue(member);
if (typeof value === "string")
result.push(typescript_1.factory.createStringLiteral(value));
else if (typeof value === "number")
result.push(typescript_1.factory.createNumericLiteral(value));
else
throw "Unsupported!";
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
}
finally { if (e_1) throw e_1.error; }
}
return createMethodCall("literal", result);
}
var tTypeDefinitions = fs_1.default.readFileSync(path_1.default.join(get_t_Path(), "lib", "t.d.ts"), "utf8");
function is_t_ImportDeclaration(program) {
return function (node) {
if (!typescript_1.default.isImportDeclaration(node))
return false;
if (!node.importClause)
return false;
var namedBindings = node.importClause.namedBindings;
if (!node.importClause.name && !namedBindings)
return false;
var importSymbol = program.getTypeChecker().getSymbolAtLocation(node.moduleSpecifier);
if (!importSymbol || importSymbol.valueDeclaration.getSourceFile().text !== tTypeDefinitions) // TODO
return false;
return true;
};
}
exports.is_t_ImportDeclaration = is_t_ImportDeclaration;
function buildType(type, typeChecker) {
var _a, _b, _c, _d;
var stringType = typeChecker.typeToString(type);
// Checking for error cases
if (stringType === "never")
throw new Error("Never type transformation is not supported");
if (type.isClass())
throw new Error("Transformation of classes is not supported");
// Basic types transformation
if (["null", "undefined", "void", "unknown"].includes(stringType))
return createPropertyAccess("none");
if (ROBLOX_TYPES.includes(stringType))
return createPropertyAccess(stringType);
var fileName = (_d = (_c = (_b = (_a = type.symbol) === null || _a === void 0 ? void 0 : _a.declarations) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.getSourceFile()) === null || _d === void 0 ? void 0 : _d.fileName;
if (fileName && path_1.default.normalize(fileName) === instanceDefType) {
return createMethodCall("instanceIsA", [typescript_1.factory.createStringLiteral(stringType)]);
}
if (utility.isBrickColorType(type))
return createPropertyAccess("BrickColor");
if (utility.isEnum(type))
return createMethodCall("enum", [typescript_1.factory.createPropertyAccessExpression(typescript_1.factory.createIdentifier("Enum"), stringType)]);
if (stringType === "true" || stringType === "false") {
var literal = stringType === "true" ? typescript_1.factory.createTrue() : typescript_1.factory.createFalse();
return createLiteral(literal);
}
if (type.isStringLiteral())
return createLiteral(typescript_1.factory.createStringLiteral(type.value));
if (type.isNumberLiteral())
return createLiteral(typescript_1.factory.createNumericLiteral(type.value.toString()));
if (["string", "number", "boolean", "any", "thread"].includes(stringType))
return createPropertyAccess(stringType);
if (utility.isFunctionType(type))
return createPropertyAccess("callback");
// Complex types transformation
try {
if (utility.isCustomEnum(type))
return convertEnumType(type, typeChecker);
if (utility.isMapType(type))
return convertMapType(type, typeChecker);
if (type.isUnion())
return convertUnionType(type, typeChecker);
else if (utility.isTupleType(type, typeChecker))
return convertTupleType(type, typeChecker);
else if (utility.isArrayType(type, typeChecker))
return convertArrayType(type, typeChecker);
else if (utility.isObjectType(type) || type.isIntersection())
return convertObjectType(type.getProperties(), typeChecker);
else if (type.isClassOrInterface())
return convertInterfaceType(type, typeChecker);
}
catch (err) {
throw "[t-ts-transformer ERROR]: Failed to build type ".concat(stringType, "\n").concat(err);
}
throw "Cannot build type ".concat(stringType);
}
exports.buildType = buildType;