cluaupp 1.5.2 → 2.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +3 -0
- package/README.md +27 -40
- package/api/generated/bench-latest.json +43 -21
- package/api/generated/lsp-index.json +1 -1
- package/api/manifests/roblox-target.manifest.json +5 -5
- package/api/normalized/roblox-target.profile.json +8729 -267
- package/api/roblox/luau-builtins.d.luau +185 -0
- package/api/roblox/surface.json +1 -0
- package/api/roblox-api.lock.json +11 -2
- package/bin/cluaupp.js +38 -0
- package/editors/vscode/syntaxes/clpp.tmLanguage.json +211 -0
- package/examples/README.md +9 -9
- package/examples/game/cluaupp.config.json +2 -2
- package/examples/game/default.project.json +4 -15
- package/examples/game/out/ReplicatedStorage/Shared/GameInfo.luau +8 -0
- package/examples/game/out/ReplicatedStorage/Shared/GameInfo.luau.map.json +13 -0
- package/examples/game/out/ServerScriptService/Leaderstats.server.luau +26 -0
- package/examples/game/out/ServerScriptService/Leaderstats.server.luau.map.json +13 -0
- package/examples/game/out/ServerScriptService/Main.server.luau +15 -0
- package/examples/game/out/ServerScriptService/Main.server.luau.map.json +13 -0
- package/examples/game/out/StarterPlayer/StarterPlayerScripts/Main.client.luau +10 -0
- package/examples/game/out/StarterPlayer/StarterPlayerScripts/Main.client.luau.map.json +13 -0
- package/examples/game/src/ReplicatedStorage/Shared/GameInfo.clp +1 -0
- package/examples/game/src/ServerScriptService/Leaderstats.server.clp +18 -0
- package/examples/game/src/ServerScriptService/Main.server.clp +9 -0
- package/examples/game/src/StarterPlayer/StarterPlayerScripts/Main.client.clp +4 -0
- package/generated/api/build-profile.js +1 -1
- package/generated/api/loader.js +2 -2
- package/generated/api/surface/build.js +328 -0
- package/generated/api/surface/luau-defs.js +595 -0
- package/generated/api/surface/model.js +8 -0
- package/generated/clpp/contract.js +6 -3
- package/generated/clpp/handshake.js +36 -0
- package/generated/clpp/modules.js +20 -9
- package/generated/clpp/runner.js +62 -31
- package/generated/compile.js +18 -1
- package/generated/compiler/ast.js +3 -0
- package/generated/compiler/checker/calls.js +999 -0
- package/generated/compiler/checker/checker.js +1215 -0
- package/generated/compiler/checker/exprs.js +791 -0
- package/generated/compiler/checker/flow.js +168 -0
- package/generated/compiler/checker/members.js +763 -0
- package/generated/compiler/checker/statements.js +792 -0
- package/generated/compiler/checker/symbols.js +90 -0
- package/generated/compiler/checker/typeops.js +348 -0
- package/generated/compiler/checker/yields.js +115 -0
- package/generated/compiler/diagnostics.js +138 -0
- package/generated/compiler/emit/emitter.js +2356 -0
- package/generated/compiler/emit/luau-types.js +137 -0
- package/generated/compiler/emit/names.js +130 -0
- package/generated/compiler/emit/runtime.js +432 -0
- package/generated/compiler/lexer.js +403 -0
- package/generated/compiler/parser.js +1881 -0
- package/generated/compiler/project.js +332 -0
- package/generated/compiler/roblox/api.js +388 -0
- package/generated/compiler/roblox/context.js +100 -0
- package/generated/compiler/source.js +59 -0
- package/generated/compiler/stdlib.js +232 -0
- package/generated/compiler/types.js +250 -0
- package/generated/target/build-cache.js +4 -4
- package/generated/target/gen-rojo-tree.js +15 -0
- package/generated/target/source-map.js +61 -8
- package/generated/target/studio-bridge.js +1 -0
- package/generated/utils/project.js +83 -25
- package/native/bin/.gitkeep +1 -0
- package/package.json +17 -6
- package/plugins/CluauppNav/Plugin.luau +28 -4
- package/rust/cluaupp/Cargo.lock +795 -0
- package/rust/cluaupp/Cargo.toml +17 -0
- package/rust/cluaupp/src/main.rs +717 -0
- package/scripts/postinstall-native.js +56 -0
- package/skills/README.md +3 -3
- package/skills/clpp-hive-flare/SKILL.md +30 -26
- package/skills/clpp-language/SKILL.md +67 -74
- package/skills/clpp-language/compiler.md +24 -36
- package/skills/clpp-style/SKILL.md +31 -44
- package/src/api/build-profile.ts +1 -1
- package/src/api/loader.ts +2 -2
- package/src/api/surface/build.ts +374 -0
- package/src/api/surface/luau-defs.ts +635 -0
- package/src/api/surface/model.ts +96 -0
- package/src/clpp/contract.ts +20 -3
- package/src/clpp/handshake.ts +25 -0
- package/src/clpp/modules.ts +17 -10
- package/src/clpp/runner.ts +52 -34
- package/src/compile.ts +20 -2
- package/src/compiler/ast.ts +336 -0
- package/src/compiler/checker/calls.ts +1025 -0
- package/src/compiler/checker/checker.ts +1352 -0
- package/src/compiler/checker/exprs.ts +805 -0
- package/src/compiler/checker/flow.ts +174 -0
- package/src/compiler/checker/members.ts +793 -0
- package/src/compiler/checker/statements.ts +802 -0
- package/src/compiler/checker/symbols.ts +219 -0
- package/src/compiler/checker/typeops.ts +355 -0
- package/src/compiler/checker/yields.ts +120 -0
- package/src/compiler/diagnostics.ts +163 -0
- package/src/compiler/emit/emitter.ts +2486 -0
- package/src/compiler/emit/luau-types.ts +143 -0
- package/src/compiler/emit/names.ts +130 -0
- package/src/compiler/emit/runtime.ts +438 -0
- package/src/compiler/lexer.ts +444 -0
- package/src/compiler/parser.ts +1943 -0
- package/src/compiler/project.ts +354 -0
- package/src/compiler/roblox/api.ts +408 -0
- package/src/compiler/roblox/context.ts +108 -0
- package/src/compiler/source.ts +67 -0
- package/src/compiler/stdlib.ts +256 -0
- package/src/compiler/types.ts +313 -0
- package/src/target/build-cache.ts +5 -8
- package/src/target/gen-rojo-tree.ts +15 -0
- package/src/target/source-map.ts +65 -27
- package/src/target/studio-bridge.ts +3 -0
- package/src/types.ts +3 -0
- package/src/utils/project.ts +79 -31
- package/templates/README.md +7 -0
- package/templates/{game → game-modern}/cluaupp.config.json +2 -2
- package/templates/{game → game-modern}/package.json +1 -2
- package/templates/game-modern/rokit.toml +2 -0
- package/templates/game-modern/src/ReplicatedStorage/Shared/GameInfo.clp +1 -0
- package/templates/game-modern/src/ServerScriptService/Handlers/Guard.server.clp +4 -0
- package/templates/game-modern/src/ServerScriptService/Main.server.clp +8 -0
- package/templates/game-modern/src/StarterPlayer/StarterPlayerScripts/Main.client.clp +4 -0
- package/examples/game/out/ReplicatedStorage/Shared/Constants/Datas/TemplateData.luau +0 -29
- package/examples/game/out/ReplicatedStorage/Shared/Constants/Datas/TemplateData.luau.map.json +0 -13
- package/examples/game/out/ReplicatedStorage/Shared/Net/Net.luau +0 -83
- package/examples/game/out/ReplicatedStorage/Shared/Net/Net.luau.map.json +0 -13
- package/examples/game/out/ServerScriptService/Boot/DataBoot.server.luau +0 -18
- package/examples/game/out/ServerScriptService/Boot/DataBoot.server.luau.map.json +0 -13
- package/examples/game/out/ServerScriptService/Configuration/ServerConfig.luau +0 -14
- package/examples/game/out/ServerScriptService/Configuration/ServerConfig.luau.map.json +0 -13
- package/examples/game/out/ServerScriptService/Handlers/Guard.server.luau +0 -15
- package/examples/game/out/ServerScriptService/Handlers/Guard.server.luau.map.json +0 -13
- package/examples/game/out/ServerScriptService/Handlers/PlayerHandler.server.luau +0 -50
- package/examples/game/out/ServerScriptService/Handlers/PlayerHandler.server.luau.map.json +0 -13
- package/examples/game/out/StarterPlayer/StarterCharacterScripts/Character/CharacterReady.client.luau +0 -12
- package/examples/game/out/StarterPlayer/StarterCharacterScripts/Character/CharacterReady.client.luau.map.json +0 -13
- package/examples/game/out/StarterPlayer/StarterPlayerScripts/Controllers/DataController.client.luau +0 -16
- package/examples/game/out/StarterPlayer/StarterPlayerScripts/Controllers/DataController.client.luau.map.json +0 -13
- package/examples/game/src/Client/Controllers/DataController.client.clpp +0 -7
- package/examples/game/src/Client/Main.client.clpp +0 -4
- package/examples/game/src/Declarations/Net.flare +0 -5
- package/examples/game/src/Include/GameTypes.clh +0 -40
- package/examples/game/src/Modules/Core/Guard.server.clpp +0 -7
- package/examples/game/src/Modules/Core/PlayerHandler.server.clpp +0 -23
- package/examples/game/src/Server/Boot/DataBoot.server.clpp +0 -13
- package/examples/game/src/Server/Main.server.clpp +0 -4
- package/templates/game/.clangd +0 -25
- package/templates/game/.vscode/extensions.json +0 -8
- package/templates/game/.vscode/settings.json +0 -17
- package/templates/game/Packages/.gitkeep +0 -0
- package/templates/game/compile_flags.txt +0 -6
- package/templates/game/default.project.json +0 -41
- package/templates/game/include/clpp/datatypes.clh +0 -575
- package/templates/game/include/clpp/generated/enums.clh +0 -4920
- package/templates/game/include/clpp/generated/instances.clh +0 -87182
- package/templates/game/include/clpp/libs/arrayindexer.clh +0 -12
- package/templates/game/include/clpp/libs/axiom.clh +0 -154
- package/templates/game/include/clpp/libs/bloom.clh +0 -58
- package/templates/game/include/clpp/libs/coil.clh +0 -37
- package/templates/game/include/clpp/libs/crest.clh +0 -22
- package/templates/game/include/clpp/libs/echo.clh +0 -52
- package/templates/game/include/clpp/libs/ember.clh +0 -17
- package/templates/game/include/clpp/libs/flare.clh +0 -65
- package/templates/game/include/clpp/libs/gleam.clh +0 -111
- package/templates/game/include/clpp/libs/guide.clh +0 -42
- package/templates/game/include/clpp/libs/helm.clh +0 -31
- package/templates/game/include/clpp/libs/hive.clh +0 -20
- package/templates/game/include/clpp/libs/keep.clh +0 -158
- package/templates/game/include/clpp/libs/lens.clh +0 -24
- package/templates/game/include/clpp/libs/mint.clh +0 -28
- package/templates/game/include/clpp/libs/net.clh +0 -31
- package/templates/game/include/clpp/libs/occlude.clh +0 -15
- package/templates/game/include/clpp/libs/pin.clh +0 -28
- package/templates/game/include/clpp/libs/promise.clh +0 -24
- package/templates/game/include/clpp/libs/roster.clh +0 -123
- package/templates/game/include/clpp/libs/shift.clh +0 -22
- package/templates/game/include/clpp/libs/spark.clh +0 -28
- package/templates/game/include/clpp/libs/stage.clh +0 -23
- package/templates/game/include/clpp/libs/sweep.clh +0 -23
- package/templates/game/include/clpp/libs/trace.clh +0 -18
- package/templates/game/include/clpp/libs/ward.clh +0 -19
- package/templates/game/include/clpp/libs.clh +0 -31
- package/templates/game/include/clpp/roblox.clh +0 -41
- package/templates/game/rokit.toml +0 -5
- package/templates/game/src/Client/Controllers/DataController.client.clpp +0 -7
- package/templates/game/src/Client/Main.client.clpp +0 -4
- package/templates/game/src/Declarations/Net.flare +0 -5
- package/templates/game/src/Include/GameTypes.clh +0 -40
- package/templates/game/src/Modules/Core/Guard.server.clpp +0 -7
- package/templates/game/src/Modules/Core/PlayerHandler.server.clpp +0 -24
- package/templates/game/src/Server/Boot/DataBoot.server.clpp +0 -13
- package/templates/game/src/Server/Main.server.clpp +0 -4
- package/templates/game/stylua.toml +0 -9
- package/templates/game/wally.toml +0 -12
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* `@clpp.roblox` — the typed Roblox API surface (classes, datatypes, enums, services, globals,
|
|
4
|
+
* libraries) as the checker sees it. Loaded once from `api/roblox/surface.json`.
|
|
5
|
+
*/
|
|
6
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
7
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
8
|
+
};
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.RobloxApi = void 0;
|
|
11
|
+
exports.surfacePath = surfacePath;
|
|
12
|
+
exports.robloxApi = robloxApi;
|
|
13
|
+
exports.setRobloxApi = setRobloxApi;
|
|
14
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
15
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
16
|
+
const package_info_js_1 = require("../../package-info.js");
|
|
17
|
+
const types_js_1 = require("../types.js");
|
|
18
|
+
/** Library functions whose `number` results are always integers on Roblox. */
|
|
19
|
+
const INT_RESULTS = new Set([
|
|
20
|
+
"math.floor", "math.ceil", "math.round", "math.sign", "string.len", "string.byte", "utf8.len", "utf8.codepoint",
|
|
21
|
+
"os.time", "buffer.len", "buffer.readi8", "buffer.readu8", "buffer.readi16", "buffer.readu16", "buffer.readi32",
|
|
22
|
+
"buffer.readu32", "buffer.readbits", "table.find", "table.maxn", "bit32.band", "bit32.bor", "bit32.bxor", "bit32.bnot",
|
|
23
|
+
"bit32.lshift", "bit32.rshift", "bit32.arshift", "bit32.lrotate", "bit32.rrotate", "bit32.extract", "bit32.replace",
|
|
24
|
+
"bit32.countlz", "bit32.countrz", "bit32.byteswap", "rawlen",
|
|
25
|
+
]);
|
|
26
|
+
class RobloxApi {
|
|
27
|
+
surface;
|
|
28
|
+
classes;
|
|
29
|
+
datatypes;
|
|
30
|
+
enums;
|
|
31
|
+
globals;
|
|
32
|
+
libraries;
|
|
33
|
+
aliases;
|
|
34
|
+
services;
|
|
35
|
+
memberCache = new Map();
|
|
36
|
+
convertCache = new WeakMap();
|
|
37
|
+
constructor(surface) {
|
|
38
|
+
this.surface = surface;
|
|
39
|
+
this.classes = surface.classes;
|
|
40
|
+
this.datatypes = surface.datatypes;
|
|
41
|
+
this.enums = surface.enums;
|
|
42
|
+
this.globals = surface.globals;
|
|
43
|
+
this.libraries = surface.libraries;
|
|
44
|
+
this.aliases = surface.aliases;
|
|
45
|
+
this.services = Object.values(surface.classes)
|
|
46
|
+
.filter((c) => c.service)
|
|
47
|
+
.map((c) => c.name);
|
|
48
|
+
}
|
|
49
|
+
hasClass(name) {
|
|
50
|
+
return Object.prototype.hasOwnProperty.call(this.classes, name);
|
|
51
|
+
}
|
|
52
|
+
hasDatatype(name) {
|
|
53
|
+
return Object.prototype.hasOwnProperty.call(this.datatypes, name);
|
|
54
|
+
}
|
|
55
|
+
hasEnum(name) {
|
|
56
|
+
return Object.prototype.hasOwnProperty.call(this.enums, name);
|
|
57
|
+
}
|
|
58
|
+
isService(name) {
|
|
59
|
+
return this.hasClass(name) && this.classes[name].service;
|
|
60
|
+
}
|
|
61
|
+
isA(cls, base) {
|
|
62
|
+
let cur = cls;
|
|
63
|
+
let guard = 0;
|
|
64
|
+
while (cur && guard++ < 64) {
|
|
65
|
+
if (cur === base) {
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
cur = this.classes[cur]?.superclass ?? null;
|
|
69
|
+
}
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
/** All members visible on a class (own first, then inherited). */
|
|
73
|
+
members(cls) {
|
|
74
|
+
const cached = this.memberCache.get(cls);
|
|
75
|
+
if (cached) {
|
|
76
|
+
return cached;
|
|
77
|
+
}
|
|
78
|
+
const out = new Map();
|
|
79
|
+
let cur = cls;
|
|
80
|
+
let guard = 0;
|
|
81
|
+
while (cur && guard++ < 64) {
|
|
82
|
+
const c = this.classes[cur];
|
|
83
|
+
if (!c) {
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
for (const [name, member] of Object.entries(c.members)) {
|
|
87
|
+
if (!out.has(name)) {
|
|
88
|
+
out.set(name, { member, owner: cur });
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
cur = c.superclass;
|
|
92
|
+
}
|
|
93
|
+
this.memberCache.set(cls, out);
|
|
94
|
+
return out;
|
|
95
|
+
}
|
|
96
|
+
member(cls, name) {
|
|
97
|
+
return this.members(cls).get(name) ?? null;
|
|
98
|
+
}
|
|
99
|
+
datatypeMember(name, member) {
|
|
100
|
+
return this.datatypes[name]?.members[member] ?? null;
|
|
101
|
+
}
|
|
102
|
+
datatypeStatic(name, member) {
|
|
103
|
+
return this.datatypes[name]?.statics[member] ?? null;
|
|
104
|
+
}
|
|
105
|
+
library(name) {
|
|
106
|
+
return this.libraries[name] ?? null;
|
|
107
|
+
}
|
|
108
|
+
// ---------- LType → Type ----------
|
|
109
|
+
convert(lt, context) {
|
|
110
|
+
const cached = this.convertCache.get(lt);
|
|
111
|
+
if (cached && !context) {
|
|
112
|
+
return cached;
|
|
113
|
+
}
|
|
114
|
+
const out = this.convertUncached(lt, context);
|
|
115
|
+
if (!context) {
|
|
116
|
+
this.convertCache.set(lt, out);
|
|
117
|
+
}
|
|
118
|
+
return out;
|
|
119
|
+
}
|
|
120
|
+
convertUncached(lt, context) {
|
|
121
|
+
switch (lt.k) {
|
|
122
|
+
case "name":
|
|
123
|
+
return this.convertName(lt.name, lt.args);
|
|
124
|
+
case "opt":
|
|
125
|
+
return types_js_1.T.option(this.convert(lt.t));
|
|
126
|
+
case "union": {
|
|
127
|
+
const hasNil = lt.ts.some((t) => t.k === "name" && t.name === "nil");
|
|
128
|
+
const rest = lt.ts.filter((t) => !(t.k === "name" && t.name === "nil")).map((t) => this.convert(t));
|
|
129
|
+
const allLit = rest.every((r) => r.k === "lit");
|
|
130
|
+
let base;
|
|
131
|
+
if (rest.length === 1) {
|
|
132
|
+
base = rest[0];
|
|
133
|
+
}
|
|
134
|
+
else if (!allLit && rest.every((r) => r.k === "instance")) {
|
|
135
|
+
base = types_js_1.T.instance("Instance");
|
|
136
|
+
}
|
|
137
|
+
else if (!allLit && rest.some((r) => r.k === "any")) {
|
|
138
|
+
base = types_js_1.T.any;
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
base = { k: "union", members: rest };
|
|
142
|
+
}
|
|
143
|
+
return hasNil ? types_js_1.T.option(base) : base;
|
|
144
|
+
}
|
|
145
|
+
case "inter": {
|
|
146
|
+
const fns = lt.ts.filter((t) => t.k === "fn");
|
|
147
|
+
if (fns.length === lt.ts.length && fns.length > 0) {
|
|
148
|
+
const converted = fns.map((f) => this.convertFn(f));
|
|
149
|
+
const first = converted[0];
|
|
150
|
+
return { ...first, overloads: converted };
|
|
151
|
+
}
|
|
152
|
+
const fields = {};
|
|
153
|
+
for (const t of lt.ts) {
|
|
154
|
+
if (t.k === "table") {
|
|
155
|
+
for (const [k, v] of Object.entries(t.fields)) {
|
|
156
|
+
fields[k] = this.convert(v);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return { k: "table", fields };
|
|
161
|
+
}
|
|
162
|
+
case "array":
|
|
163
|
+
return types_js_1.T.list(this.convert(lt.t));
|
|
164
|
+
case "map":
|
|
165
|
+
return types_js_1.T.dict(this.convert(lt.key), this.convert(lt.value));
|
|
166
|
+
case "table": {
|
|
167
|
+
const fields = {};
|
|
168
|
+
for (const [k, v] of Object.entries(lt.fields)) {
|
|
169
|
+
fields[k] = this.convert(v);
|
|
170
|
+
}
|
|
171
|
+
return { k: "table", fields };
|
|
172
|
+
}
|
|
173
|
+
case "fn":
|
|
174
|
+
return this.convertFn(lt, context);
|
|
175
|
+
case "lit":
|
|
176
|
+
return { k: "lit", value: lt.value };
|
|
177
|
+
case "pack":
|
|
178
|
+
return types_js_1.T.any;
|
|
179
|
+
default:
|
|
180
|
+
return types_js_1.T.any;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
convertFn(fn, qualifiedName) {
|
|
184
|
+
const params = fn.params.map((p, i) => {
|
|
185
|
+
const t = this.convert(p.t);
|
|
186
|
+
return { name: p.name || `arg${i + 1}`, type: t.k === "option" ? t.inner : t, optional: t.k === "option" || (t.k === "any" && false) };
|
|
187
|
+
});
|
|
188
|
+
let ret;
|
|
189
|
+
const rets = fn.returns.map((r) => this.convert(r));
|
|
190
|
+
if (rets.length === 0) {
|
|
191
|
+
ret = fn.returnsVariadic ? types_js_1.T.any : types_js_1.T.void;
|
|
192
|
+
}
|
|
193
|
+
else if (rets.length === 1) {
|
|
194
|
+
ret = rets[0];
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
ret = { k: "tuple", elems: rets };
|
|
198
|
+
}
|
|
199
|
+
if (qualifiedName && INT_RESULTS.has(qualifiedName)) {
|
|
200
|
+
ret = ret.k === "option" ? types_js_1.T.option(types_js_1.T.int) : ret.k === "float" ? types_js_1.T.int : ret;
|
|
201
|
+
}
|
|
202
|
+
const out = { k: "func", params, ret, variadic: fn.variadic ? this.convert(fn.variadic) : null };
|
|
203
|
+
if (fn.generics?.length) {
|
|
204
|
+
out.generics = fn.generics;
|
|
205
|
+
}
|
|
206
|
+
return out;
|
|
207
|
+
}
|
|
208
|
+
convertName(name, args) {
|
|
209
|
+
switch (name) {
|
|
210
|
+
case "number":
|
|
211
|
+
case "double":
|
|
212
|
+
case "float":
|
|
213
|
+
return types_js_1.T.float;
|
|
214
|
+
case "int":
|
|
215
|
+
case "int64":
|
|
216
|
+
return types_js_1.T.int;
|
|
217
|
+
case "string":
|
|
218
|
+
return types_js_1.T.string;
|
|
219
|
+
case "boolean":
|
|
220
|
+
case "bool":
|
|
221
|
+
return types_js_1.T.bool;
|
|
222
|
+
case "nil":
|
|
223
|
+
case "()":
|
|
224
|
+
return types_js_1.T.void;
|
|
225
|
+
case "any":
|
|
226
|
+
case "unknown":
|
|
227
|
+
case "userdata":
|
|
228
|
+
case "Object":
|
|
229
|
+
return name === "Object" ? types_js_1.T.instance("Object") : types_js_1.T.any;
|
|
230
|
+
case "never":
|
|
231
|
+
return types_js_1.T.never;
|
|
232
|
+
case "thread":
|
|
233
|
+
return types_js_1.T.thread;
|
|
234
|
+
case "buffer":
|
|
235
|
+
return types_js_1.T.buffer;
|
|
236
|
+
case "vector":
|
|
237
|
+
return types_js_1.T.datatype("Vector3");
|
|
238
|
+
case "RBXScriptSignal":
|
|
239
|
+
return { k: "rbxSignal", args: (args || []).filter((a) => !(a.k === "name" && a.name === "()")).map((a) => this.convert(a)) };
|
|
240
|
+
case "Instances":
|
|
241
|
+
return types_js_1.T.list(types_js_1.T.instance("Instance"));
|
|
242
|
+
case "Objects":
|
|
243
|
+
return types_js_1.T.list(types_js_1.T.instance("Instance"));
|
|
244
|
+
case "Enum":
|
|
245
|
+
return { k: "rbxEnum", enumName: "*" };
|
|
246
|
+
case "EnumItem":
|
|
247
|
+
return { k: "rbxEnumItem", enumName: "*" };
|
|
248
|
+
default:
|
|
249
|
+
break;
|
|
250
|
+
}
|
|
251
|
+
if (this.hasClass(name)) {
|
|
252
|
+
return types_js_1.T.instance(name);
|
|
253
|
+
}
|
|
254
|
+
if (this.hasDatatype(name)) {
|
|
255
|
+
return types_js_1.T.datatype(name);
|
|
256
|
+
}
|
|
257
|
+
if (name.startsWith("Enum") && this.hasEnum(name.slice(4))) {
|
|
258
|
+
return { k: "rbxEnumItem", enumName: name.slice(4) };
|
|
259
|
+
}
|
|
260
|
+
if (name.startsWith("Enum.") && this.hasEnum(name.slice(5))) {
|
|
261
|
+
return { k: "rbxEnumItem", enumName: name.slice(5) };
|
|
262
|
+
}
|
|
263
|
+
const alias = this.aliases[name];
|
|
264
|
+
if (alias) {
|
|
265
|
+
return this.convert(alias);
|
|
266
|
+
}
|
|
267
|
+
return types_js_1.T.any;
|
|
268
|
+
}
|
|
269
|
+
/** Function type of a library member (`math.floor`), with integer results applied. */
|
|
270
|
+
libraryFunction(lib, member) {
|
|
271
|
+
const st = this.libraries[lib]?.[member];
|
|
272
|
+
if (!st) {
|
|
273
|
+
return null;
|
|
274
|
+
}
|
|
275
|
+
if (st.kind === "value") {
|
|
276
|
+
return st.type ? this.convert(st.type) : types_js_1.T.any;
|
|
277
|
+
}
|
|
278
|
+
const fns = (st.overloads || []).map((f) => this.convertFn(f, `${lib}.${member}`));
|
|
279
|
+
if (fns.length === 0) {
|
|
280
|
+
return types_js_1.T.any;
|
|
281
|
+
}
|
|
282
|
+
const first = fns[0];
|
|
283
|
+
return fns.length > 1 ? { ...first, overloads: fns } : first;
|
|
284
|
+
}
|
|
285
|
+
globalFunction(name) {
|
|
286
|
+
const g = this.globals[name];
|
|
287
|
+
if (!g) {
|
|
288
|
+
return null;
|
|
289
|
+
}
|
|
290
|
+
if (g.overloads) {
|
|
291
|
+
const fns = g.overloads.map((f) => this.convertFn(f, name));
|
|
292
|
+
const first = fns[0];
|
|
293
|
+
return fns.length > 1 ? { ...first, overloads: fns } : first;
|
|
294
|
+
}
|
|
295
|
+
return g.type ? this.convert(g.type) : types_js_1.T.any;
|
|
296
|
+
}
|
|
297
|
+
methodType(member) {
|
|
298
|
+
const fns = (member.overloads || []).map((f) => this.convertFn(f));
|
|
299
|
+
if (fns.length === 0) {
|
|
300
|
+
return types_js_1.T.func([], types_js_1.T.void);
|
|
301
|
+
}
|
|
302
|
+
const first = fns[0];
|
|
303
|
+
return fns.length > 1 ? { ...first, overloads: fns } : first;
|
|
304
|
+
}
|
|
305
|
+
staticFunctionType(st) {
|
|
306
|
+
if (st.kind === "value") {
|
|
307
|
+
return st.type ? this.convert(st.type) : types_js_1.T.any;
|
|
308
|
+
}
|
|
309
|
+
const fns = (st.overloads || []).map((f) => this.convertFn(f));
|
|
310
|
+
const first = fns[0];
|
|
311
|
+
return fns.length > 1 ? { ...first, overloads: fns } : first;
|
|
312
|
+
}
|
|
313
|
+
/** Result type of `left <op> right` for datatypes (Vector3 + Vector3, CFrame * Vector3, 2 * v). */
|
|
314
|
+
operator(op, left, right) {
|
|
315
|
+
const meta = { "+": "__add", "-": "__sub", "*": "__mul", "/": "__div", "%": "__mod" };
|
|
316
|
+
const name = meta[op];
|
|
317
|
+
if (!name) {
|
|
318
|
+
return null;
|
|
319
|
+
}
|
|
320
|
+
const tryOn = (dt, other) => {
|
|
321
|
+
for (const fn of this.datatypes[dt]?.operators[name] || []) {
|
|
322
|
+
const param = fn.params[0] ? this.convert(fn.params[0].t) : null;
|
|
323
|
+
if (!param || this.acceptsOperand(param, other)) {
|
|
324
|
+
return fn.returns[0] ? this.convert(fn.returns[0]) : null;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
return null;
|
|
328
|
+
};
|
|
329
|
+
if (left.k === "datatype") {
|
|
330
|
+
const r = tryOn(left.name, right);
|
|
331
|
+
if (r) {
|
|
332
|
+
return r;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
if (right.k === "datatype" && (op === "*" || op === "+") && (left.k === "int" || left.k === "float" || left.k === "lit")) {
|
|
336
|
+
return tryOn(right.name, left);
|
|
337
|
+
}
|
|
338
|
+
return null;
|
|
339
|
+
}
|
|
340
|
+
acceptsOperand(param, arg) {
|
|
341
|
+
if (param.k === "union") {
|
|
342
|
+
return param.members.some((m) => this.acceptsOperand(m, arg));
|
|
343
|
+
}
|
|
344
|
+
if (param.k === "float" || param.k === "int") {
|
|
345
|
+
return arg.k === "int" || arg.k === "float" || (arg.k === "lit" && typeof arg.value === "number");
|
|
346
|
+
}
|
|
347
|
+
if (param.k === "datatype") {
|
|
348
|
+
return arg.k === "datatype" && arg.name === param.name;
|
|
349
|
+
}
|
|
350
|
+
return param.k === "any";
|
|
351
|
+
}
|
|
352
|
+
unaryMinus(t) {
|
|
353
|
+
return t.k === "datatype" && Boolean(this.datatypes[t.name]?.operators.__unm);
|
|
354
|
+
}
|
|
355
|
+
/** Creation policy for `Instance.new("X")` / `X()`. */
|
|
356
|
+
creationError(cls) {
|
|
357
|
+
const c = this.classes[cls];
|
|
358
|
+
if (!c) {
|
|
359
|
+
return `'${cls}' is not a Roblox class`;
|
|
360
|
+
}
|
|
361
|
+
if (c.service) {
|
|
362
|
+
return `${cls} is a service; get it with game.GetService("${cls}") instead of creating it`;
|
|
363
|
+
}
|
|
364
|
+
if (!c.creatable) {
|
|
365
|
+
return `${cls} cannot be created with Instance.new (it is ${c.abstract ? "abstract" : "not creatable"})`;
|
|
366
|
+
}
|
|
367
|
+
return null;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
exports.RobloxApi = RobloxApi;
|
|
371
|
+
let shared = null;
|
|
372
|
+
function surfacePath() {
|
|
373
|
+
return node_path_1.default.join(package_info_js_1.projectRoot, "api", "roblox", "surface.json");
|
|
374
|
+
}
|
|
375
|
+
function robloxApi() {
|
|
376
|
+
if (!shared) {
|
|
377
|
+
const file = surfacePath();
|
|
378
|
+
if (!node_fs_1.default.existsSync(file)) {
|
|
379
|
+
throw new Error(`cluaupp: missing Roblox surface ${file}. Run \`cluaupp api surface\` to rebuild it.`);
|
|
380
|
+
}
|
|
381
|
+
shared = new RobloxApi(JSON.parse(node_fs_1.default.readFileSync(file, "utf8")));
|
|
382
|
+
}
|
|
383
|
+
return shared;
|
|
384
|
+
}
|
|
385
|
+
/** Test hook. */
|
|
386
|
+
function setRobloxApi(api) {
|
|
387
|
+
shared = api;
|
|
388
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Server/client authority rules. Each rule names a service, a class member or a service member,
|
|
4
|
+
* and the only run context where it works. Members apply to subclasses (RemoteEvent rules also
|
|
5
|
+
* cover UnreliableRemoteEvent through the explicit entries).
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.RISKY_MEMBERS = exports.MEMBER_RULES = exports.SERVICE_RULES = void 0;
|
|
9
|
+
exports.contextLabel = contextLabel;
|
|
10
|
+
exports.runContextOf = runContextOf;
|
|
11
|
+
/** `service:Name` → rule for touching the service at all. */
|
|
12
|
+
exports.SERVICE_RULES = {
|
|
13
|
+
ServerStorage: { only: "server", why: "ServerStorage is never replicated to clients" },
|
|
14
|
+
ServerScriptService: { only: "server", why: "ServerScriptService is never replicated to clients" },
|
|
15
|
+
DataStoreService: { only: "server", why: "DataStores can only be accessed by the server" },
|
|
16
|
+
MessagingService: { only: "server", why: "MessagingService only works on the server" },
|
|
17
|
+
MemoryStoreService: { only: "server", why: "MemoryStoreService only works on the server" },
|
|
18
|
+
AnalyticsService: { only: "server", why: "AnalyticsService events are logged from the server" },
|
|
19
|
+
OpenCloudService: { only: "server", why: "OpenCloudService only works on the server" },
|
|
20
|
+
UserInputService: { only: "client", why: "UserInputService reads the local device; on the server it has no input" },
|
|
21
|
+
ContextActionService: { only: "client", why: "ContextActionService binds local input" },
|
|
22
|
+
HapticService: { only: "client", why: "HapticService drives local devices" },
|
|
23
|
+
VRService: { only: "client", why: "VRService reads the local headset" },
|
|
24
|
+
GuiService: { only: "client", why: "GuiService controls the local player's GUI", severity: "warning" },
|
|
25
|
+
};
|
|
26
|
+
/** `Class.Member` → rule (checked against the member's owner chain). */
|
|
27
|
+
exports.MEMBER_RULES = {
|
|
28
|
+
"Players.LocalPlayer": { only: "client", why: "Players.LocalPlayer is nil on the server" },
|
|
29
|
+
"Player.GetMouse": { only: "client", why: "GetMouse only works for the local player" },
|
|
30
|
+
"RunService.RenderStepped": { only: "client", why: "RenderStepped only fires on clients" },
|
|
31
|
+
"RunService.BindToRenderStep": { only: "client", why: "BindToRenderStep only works on clients" },
|
|
32
|
+
"RunService.UnbindFromRenderStep": { only: "client", why: "UnbindFromRenderStep only works on clients" },
|
|
33
|
+
"StarterGui.SetCore": { only: "client", why: "SetCore only works from a LocalScript" },
|
|
34
|
+
"StarterGui.GetCore": { only: "client", why: "GetCore only works from a LocalScript" },
|
|
35
|
+
"StarterGui.SetCoreGuiEnabled": { only: "client", why: "SetCoreGuiEnabled only works from a LocalScript" },
|
|
36
|
+
"RemoteEvent.FireServer": { only: "client", why: "FireServer sends from a client to the server" },
|
|
37
|
+
"RemoteEvent.OnClientEvent": { only: "client", why: "OnClientEvent fires on clients" },
|
|
38
|
+
"RemoteEvent.FireClient": { only: "server", why: "FireClient sends from the server to one client" },
|
|
39
|
+
"RemoteEvent.FireAllClients": { only: "server", why: "FireAllClients sends from the server" },
|
|
40
|
+
"RemoteEvent.OnServerEvent": { only: "server", why: "OnServerEvent fires on the server" },
|
|
41
|
+
"UnreliableRemoteEvent.FireServer": { only: "client", why: "FireServer sends from a client to the server" },
|
|
42
|
+
"UnreliableRemoteEvent.OnClientEvent": { only: "client", why: "OnClientEvent fires on clients" },
|
|
43
|
+
"UnreliableRemoteEvent.FireClient": { only: "server", why: "FireClient sends from the server to one client" },
|
|
44
|
+
"UnreliableRemoteEvent.FireAllClients": { only: "server", why: "FireAllClients sends from the server" },
|
|
45
|
+
"UnreliableRemoteEvent.OnServerEvent": { only: "server", why: "OnServerEvent fires on the server" },
|
|
46
|
+
"RemoteFunction.InvokeServer": { only: "client", why: "InvokeServer calls the server from a client" },
|
|
47
|
+
"RemoteFunction.OnClientInvoke": { only: "client", why: "OnClientInvoke runs on clients" },
|
|
48
|
+
"RemoteFunction.InvokeClient": { only: "server", why: "InvokeClient calls a client from the server" },
|
|
49
|
+
"RemoteFunction.OnServerInvoke": { only: "server", why: "OnServerInvoke runs on the server" },
|
|
50
|
+
"DataModel.BindToClose": { only: "server", why: "BindToClose runs when the server shuts down" },
|
|
51
|
+
"Players.BanAsync": { only: "server", why: "bans are issued by the server" },
|
|
52
|
+
"Players.UnbanAsync": { only: "server", why: "bans are issued by the server" },
|
|
53
|
+
"Players.CreateHumanoidModelFromUserId": { only: "server", why: "only the server can build character models" },
|
|
54
|
+
"Players.CreateHumanoidModelFromDescription": { only: "server", why: "only the server can build character models" },
|
|
55
|
+
"Player.LoadCharacter": { only: "server", why: "characters are spawned by the server" },
|
|
56
|
+
"Player.LoadCharacterAsync": { only: "server", why: "characters are spawned by the server" },
|
|
57
|
+
"BasePart.SetNetworkOwner": { only: "server", why: "network ownership is assigned by the server" },
|
|
58
|
+
"BasePart.SetNetworkOwnershipAuto": { only: "server", why: "network ownership is assigned by the server" },
|
|
59
|
+
"MarketplaceService.ProcessReceipt": { only: "server", why: "developer product receipts are processed by the server" },
|
|
60
|
+
"BadgeService.AwardBadge": { only: "server", why: "badges are awarded by the server" },
|
|
61
|
+
"BadgeService.AwardBadgeAsync": { only: "server", why: "badges are awarded by the server" },
|
|
62
|
+
"TextService.FilterStringAsync": { only: "server", why: "text filtering runs on the server" },
|
|
63
|
+
"HttpService.GetAsync": { only: "server", why: "HTTP requests can only be made by the server" },
|
|
64
|
+
"HttpService.PostAsync": { only: "server", why: "HTTP requests can only be made by the server" },
|
|
65
|
+
"HttpService.RequestAsync": { only: "server", why: "HTTP requests can only be made by the server" },
|
|
66
|
+
"TeleportService.TeleportAsync": { only: "server", why: "TeleportAsync is server-only" },
|
|
67
|
+
"TeleportService.ReserveServer": { only: "server", why: "ReserveServer is server-only" },
|
|
68
|
+
"TeleportService.ReserveServerAsync": { only: "server", why: "ReserveServerAsync is server-only" },
|
|
69
|
+
"InsertService.LoadAsset": { only: "server", why: "LoadAsset is server-only" },
|
|
70
|
+
"PhysicsService.RegisterCollisionGroup": { only: "server", why: "collision groups are registered by the server" },
|
|
71
|
+
};
|
|
72
|
+
/** Members that work but are a known exploit or freeze vector. */
|
|
73
|
+
exports.RISKY_MEMBERS = {
|
|
74
|
+
"RemoteFunction.InvokeClient": "the server yields until the client answers; a malicious client can hang it forever — prefer a RemoteEvent",
|
|
75
|
+
};
|
|
76
|
+
function contextLabel(ctx) {
|
|
77
|
+
switch (ctx) {
|
|
78
|
+
case "server":
|
|
79
|
+
return "a server Script";
|
|
80
|
+
case "client":
|
|
81
|
+
return "a LocalScript";
|
|
82
|
+
case "plugin":
|
|
83
|
+
return "a plugin";
|
|
84
|
+
default:
|
|
85
|
+
return "a shared module";
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
function runContextOf(fileName) {
|
|
89
|
+
const base = fileName.replace(/\\/g, "/").split("/").pop().toLowerCase();
|
|
90
|
+
if (/\.server\.clp$/.test(base)) {
|
|
91
|
+
return "server";
|
|
92
|
+
}
|
|
93
|
+
if (/\.client\.clp$/.test(base)) {
|
|
94
|
+
return "client";
|
|
95
|
+
}
|
|
96
|
+
if (/\.plugin\.clp$/.test(base)) {
|
|
97
|
+
return "plugin";
|
|
98
|
+
}
|
|
99
|
+
return "shared";
|
|
100
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/** Source text with fast offset → line/column mapping. Columns are 1-based UTF-16 units. */
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.SourceFile = void 0;
|
|
5
|
+
exports.spanOf = spanOf;
|
|
6
|
+
exports.joinSpans = joinSpans;
|
|
7
|
+
class SourceFile {
|
|
8
|
+
path;
|
|
9
|
+
text;
|
|
10
|
+
lineStarts;
|
|
11
|
+
constructor(path, text) {
|
|
12
|
+
this.path = path;
|
|
13
|
+
this.text = text;
|
|
14
|
+
const starts = [0];
|
|
15
|
+
for (let i = 0; i < text.length; i++) {
|
|
16
|
+
const c = text.charCodeAt(i);
|
|
17
|
+
if (c === 10) {
|
|
18
|
+
starts.push(i + 1);
|
|
19
|
+
}
|
|
20
|
+
else if (c === 13) {
|
|
21
|
+
if (text.charCodeAt(i + 1) === 10) {
|
|
22
|
+
i++;
|
|
23
|
+
}
|
|
24
|
+
starts.push(i + 1);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
this.lineStarts = starts;
|
|
28
|
+
}
|
|
29
|
+
position(offset) {
|
|
30
|
+
const starts = this.lineStarts;
|
|
31
|
+
let lo = 0;
|
|
32
|
+
let hi = starts.length - 1;
|
|
33
|
+
while (lo < hi) {
|
|
34
|
+
const mid = (lo + hi + 1) >> 1;
|
|
35
|
+
if (starts[mid] <= offset) {
|
|
36
|
+
lo = mid;
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
hi = mid - 1;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return { line: lo + 1, column: offset - starts[lo] + 1 };
|
|
43
|
+
}
|
|
44
|
+
lineText(line) {
|
|
45
|
+
const start = this.lineStarts[line - 1] ?? 0;
|
|
46
|
+
const next = this.lineStarts[line] ?? this.text.length;
|
|
47
|
+
return this.text.slice(start, next).replace(/\r?\n$|\r$/, "");
|
|
48
|
+
}
|
|
49
|
+
slice(span) {
|
|
50
|
+
return this.text.slice(span.start, span.end);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
exports.SourceFile = SourceFile;
|
|
54
|
+
function spanOf(start, end) {
|
|
55
|
+
return { start, end };
|
|
56
|
+
}
|
|
57
|
+
function joinSpans(a, b) {
|
|
58
|
+
return { start: Math.min(a.start, b.start), end: Math.max(a.end, b.end) };
|
|
59
|
+
}
|