roblox-ts
Version:
<div align="center"><img width=25% src="https://i.imgur.com/yCjHmng.png"></div> <h1 align="center"><a href="https://roblox-ts.github.io/">roblox-ts</a></h1> <div align="center">A TypeScript-to-Lua Compiler for Roblox</div> <br> <div align="center"> <a hr
233 lines • 7.86 kB
JavaScript
;
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
const ts = __importStar(require("ts-morph"));
const CompilerError_1 = require("../errors/CompilerError");
const typeUtilities_1 = require("../typeUtilities");
const utility_1 = require("../utility");
const LUA_RESERVED_KEYWORDS = [
"and",
"break",
"do",
"else",
"elseif",
"end",
"false",
"for",
"function",
"if",
"in",
"local",
"nil",
"not",
"or",
"repeat",
"return",
"then",
"true",
"until",
"while",
];
const LUA_RESERVED_METAMETHODS = [
"__index",
"__newindex",
"__add",
"__sub",
"__mul",
"__div",
"__mod",
"__pow",
"__unm",
"__eq",
"__lt",
"__le",
"__call",
"__concat",
"__tostring",
"__len",
"__metatable",
"__mode",
];
const LUA_RESERVED_NAMESPACES = [
"ipairs",
"os",
"type",
"select",
"math",
"_G",
"shared",
"string",
"require",
"debug",
"tonumber",
"next",
"_VERSION",
"pairs",
"pcall",
"rawset",
"error",
"utf8",
"setmetatable",
"setfenv",
"xpcall",
"ypcall",
"tostring",
"print",
"collectgarbage",
"rawequal",
"assert",
"table",
"coroutine",
"rawget",
"getmetatable",
"getfenv",
"tick",
"wait",
"delay",
"spawn",
"warn",
"newproxy",
"Random",
"Axes",
"BrickColor",
"CFrame",
"Color3",
"ColorSequence",
"ColorSequenceKeypoint",
"Faces",
"NumberRange",
"NumberSequence",
"NumberSequenceKeypoint",
"Rect",
"Region3",
"Region3int16",
"string",
"UDim",
"UDim2",
"Vector2",
"Vector3",
"Ray",
];
const TS_RESERVED_KEYWORDS = ["_exports", "undefined", "TS", "globalThis", "table"];
function checkReserved(name, node, checkNamespace = false) {
if (LUA_RESERVED_KEYWORDS.indexOf(name) !== -1) {
throw new CompilerError_1.CompilerError(`Cannot use '${name}' as identifier (reserved Lua keyword)`, node, CompilerError_1.CompilerErrorType.ReservedKeyword);
}
else if (!name.match(/^[a-zA-Z_][a-zA-Z0-9_]*$/)) {
throw new CompilerError_1.CompilerError(`Cannot use '${name}' as identifier (doesn't match Lua's identifier rules)`, node, CompilerError_1.CompilerErrorType.InvalidIdentifier);
}
else if (TS_RESERVED_KEYWORDS.indexOf(name) !== -1 || name.match(/^_[0-9]+$/)) {
throw new CompilerError_1.CompilerError(`Cannot use '${name}' as identifier (reserved for Roblox-ts)`, node, CompilerError_1.CompilerErrorType.RobloxTSReservedIdentifier);
}
else if (checkNamespace && LUA_RESERVED_NAMESPACES.indexOf(name) !== -1) {
throw new CompilerError_1.CompilerError(`Cannot use '${name}' as identifier (reserved Lua namespace)`, node, CompilerError_1.CompilerErrorType.ReservedNamespace);
}
}
exports.checkReserved = checkReserved;
function checkMethodReserved(name, node) {
checkReserved(name, node);
if (LUA_RESERVED_METAMETHODS.indexOf(name) !== -1) {
throw new CompilerError_1.CompilerError(`Cannot use '${name}' as a method name (reserved Lua metamethod)`, node, CompilerError_1.CompilerErrorType.ReservedMethodName);
}
}
exports.checkMethodReserved = checkMethodReserved;
const COMPILER_DIRECTIVE_TAG = "rbxts";
function getCompilerDirectiveFromDeclaration(node, directives) {
if (ts.TypeGuards.isJSDocableNode(node)) {
for (const jsDoc of node.getJsDocs()) {
for (const jsTag of jsDoc.getTags()) {
if (jsTag.getTagName() === COMPILER_DIRECTIVE_TAG) {
const comment = jsTag.getComment();
if (comment) {
for (const word of comment.split(" ")) {
for (const directive of directives) {
if (word === directive) {
return directive;
}
}
}
}
}
}
}
}
const parent = node.getParent();
if (parent) {
const result = getCompilerDirectiveFromDeclaration(parent, directives);
if (result !== undefined) {
return result;
}
}
}
/**
* Searches `node` recursively for directives. Returns either the first directive from the given list that it finds.
* If it cannot find a directive from the list, it returns `undefined`.
* Search is:
* - left -> right
* - inner -> outer
* @param node JSDocable node to search
* @param directives list of directives to search for
*/
function getCompilerDirective(symbol, directives) {
for (const node of symbol.getDeclarations()) {
const result = getCompilerDirectiveFromDeclaration(node, directives);
if (result !== undefined) {
return result;
}
}
}
exports.getCompilerDirective = getCompilerDirective;
function checkApiAccess(state, node) {
const symbol = node.getSymbol();
if (!symbol) {
return;
}
if (state.scriptContext === utility_1.ScriptContext.Server) {
if (getCompilerDirective(symbol, ["client" /* Client */, "server" /* Server */]) ===
"client" /* Client */) {
throw new CompilerError_1.CompilerError("Server script attempted to access a client-only API!", node, CompilerError_1.CompilerErrorType.InvalidClientOnlyAPIAccess);
}
}
else if (state.scriptContext === utility_1.ScriptContext.Client) {
if (getCompilerDirective(symbol, ["client" /* Client */, "server" /* Server */]) ===
"server" /* Server */) {
throw new CompilerError_1.CompilerError("Client script attempted to access a server-only API!", node, CompilerError_1.CompilerErrorType.InvalidServerOnlyAPIAccess);
}
}
}
exports.checkApiAccess = checkApiAccess;
function checkNonAny(node, checkArrayType = false) {
const isInCatch = node.getFirstAncestorByKind(ts.SyntaxKind.CatchClause) !== undefined;
let type = typeUtilities_1.getType(node);
if (checkArrayType && type.isArray()) {
const arrayType = type.getArrayElementType();
if (arrayType) {
type = arrayType;
}
}
if (!isInCatch && typeUtilities_1.isAnyType(type)) {
const parent = node.getParent();
if (parent) {
throw new CompilerError_1.CompilerError(`${utility_1.yellow(node.getText())} in ${utility_1.yellow(parent.getText())} is of type ${utility_1.bold("any")} which is not supported! Use type ${utility_1.bold("unknown")} instead.`, node, CompilerError_1.CompilerErrorType.NoAny);
}
else {
throw new CompilerError_1.CompilerError(`${utility_1.yellow(node.getText())} is of type ${utility_1.bold("any")} which is not supported! Use type ${utility_1.bold("unknown")} instead.`, node, CompilerError_1.CompilerErrorType.NoAny);
}
}
return node;
}
exports.checkNonAny = checkNonAny;
function checkReturnsNonAny(node) {
const isInCatch = node.getFirstAncestorByKind(ts.SyntaxKind.CatchClause) !== undefined;
if (!isInCatch && typeUtilities_1.isAnyType(node.getReturnType())) {
throw new CompilerError_1.CompilerError(`Functions with a return type of type ${utility_1.bold("any")} are unsupported! Use type ${utility_1.bold("unknown")} instead!`, node, CompilerError_1.CompilerErrorType.NoAny);
}
}
exports.checkReturnsNonAny = checkReturnsNonAny;
//# sourceMappingURL=security.js.map