cluaupp 1.5.2 → 2.0.0-beta.2
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 +8 -0
- package/README.md +29 -41
- 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/README.md +1 -1
- package/editors/vscode/package.json +50 -15
- package/editors/vscode/snippets/flare.json +1 -1
- package/editors/vscode/snippets/hive.json +1 -1
- 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/api-cli.js +2 -2
- package/generated/cli.js +18 -17
- 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/intellisense.js +1 -15
- package/generated/lsp.js +1 -1
- package/generated/target/build-cache.js +4 -4
- package/generated/target/datamodel-generate.js +12 -0
- 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 +104 -31
- package/include/README.md +5 -0
- 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/api-cli.ts +2 -2
- package/src/cli.ts +19 -18
- 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/intellisense.ts +1 -13
- package/src/lsp.ts +1 -1
- package/src/target/build-cache.ts +5 -8
- package/src/target/datamodel-generate.ts +12 -1
- 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 +99 -37
- 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,332 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Project compilation: maps `Src/` onto the Roblox DataModel, builds the typed `@game` tree,
|
|
4
|
+
* resolves `link`s into a module graph (each module checked once, cycles reported) and emits Luau.
|
|
5
|
+
*
|
|
6
|
+
* Layout — folders named like a service map onto that service:
|
|
7
|
+
* Src/ServerScriptService/… → game.ServerScriptService
|
|
8
|
+
* Src/ReplicatedStorage/Shared/… → game.ReplicatedStorage.Shared
|
|
9
|
+
* Src/StarterPlayer/StarterPlayerScripts → game.StarterPlayer.StarterPlayerScripts
|
|
10
|
+
* Legacy domain folders still work: Server → ServerScriptService, Client → StarterPlayerScripts,
|
|
11
|
+
* anything else → ReplicatedStorage/<folder>.
|
|
12
|
+
*/
|
|
13
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
14
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.isIdentifier = exports.ProjectCompiler = exports.LIBS_FOLDER = exports.SOURCE_EXT = void 0;
|
|
18
|
+
exports.scriptStem = scriptStem;
|
|
19
|
+
exports.outputName = outputName;
|
|
20
|
+
exports.dataModelPath = dataModelPath;
|
|
21
|
+
exports.classForFile = classForFile;
|
|
22
|
+
exports.collectClp = collectClp;
|
|
23
|
+
exports.buildDataModel = buildDataModel;
|
|
24
|
+
exports.instancePath = instancePath;
|
|
25
|
+
exports.compileSource = compileSource;
|
|
26
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
27
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
28
|
+
const checker_js_1 = require("./checker/checker.js");
|
|
29
|
+
const diagnostics_js_1 = require("./diagnostics.js");
|
|
30
|
+
const emitter_js_1 = require("./emit/emitter.js");
|
|
31
|
+
const names_js_1 = require("./emit/names.js");
|
|
32
|
+
Object.defineProperty(exports, "isIdentifier", { enumerable: true, get: function () { return names_js_1.isIdentifier; } });
|
|
33
|
+
const parser_js_1 = require("./parser.js");
|
|
34
|
+
const api_js_1 = require("./roblox/api.js");
|
|
35
|
+
const context_js_1 = require("./roblox/context.js");
|
|
36
|
+
const source_js_1 = require("./source.js");
|
|
37
|
+
const stdlib_js_1 = require("./stdlib.js");
|
|
38
|
+
exports.SOURCE_EXT = ".clp";
|
|
39
|
+
exports.LIBS_FOLDER = ["ReplicatedStorage", "CluauppLibs"];
|
|
40
|
+
const LEGACY_FOLDERS = {
|
|
41
|
+
Server: ["ServerScriptService"],
|
|
42
|
+
Client: ["StarterPlayer", "StarterPlayerScripts"],
|
|
43
|
+
Character: ["StarterPlayer", "StarterCharacterScripts"],
|
|
44
|
+
Gui: ["StarterGui"],
|
|
45
|
+
};
|
|
46
|
+
const NESTED_SERVICES = {
|
|
47
|
+
StarterPlayerScripts: ["StarterPlayer", "StarterPlayerScripts"],
|
|
48
|
+
StarterCharacterScripts: ["StarterPlayer", "StarterCharacterScripts"],
|
|
49
|
+
};
|
|
50
|
+
function scriptStem(fileName) {
|
|
51
|
+
return node_path_1.default.basename(fileName).replace(/\.(server|client|plugin)?\.?clp$/i, "").replace(/\.clp$/i, "");
|
|
52
|
+
}
|
|
53
|
+
function outputName(rel) {
|
|
54
|
+
return rel.replace(/\.clp$/i, ".luau");
|
|
55
|
+
}
|
|
56
|
+
/** DataModel path of a source file, e.g. `ReplicatedStorage/Shared/Combat.clp` → ["ReplicatedStorage", "Shared", "Combat"]. */
|
|
57
|
+
function dataModelPath(api, rel) {
|
|
58
|
+
const parts = rel.replace(/\\/g, "/").split("/");
|
|
59
|
+
const file = parts.pop();
|
|
60
|
+
const stem = scriptStem(file);
|
|
61
|
+
const first = parts[0];
|
|
62
|
+
let base;
|
|
63
|
+
let rest;
|
|
64
|
+
if (!first) {
|
|
65
|
+
base = ["ReplicatedStorage"];
|
|
66
|
+
rest = [];
|
|
67
|
+
}
|
|
68
|
+
else if (NESTED_SERVICES[first]) {
|
|
69
|
+
base = NESTED_SERVICES[first];
|
|
70
|
+
rest = parts.slice(1);
|
|
71
|
+
}
|
|
72
|
+
else if (first === "StarterPlayer" && NESTED_SERVICES[parts[1]]) {
|
|
73
|
+
base = NESTED_SERVICES[parts[1]];
|
|
74
|
+
rest = parts.slice(2);
|
|
75
|
+
}
|
|
76
|
+
else if (api.isService(first)) {
|
|
77
|
+
base = [first];
|
|
78
|
+
rest = parts.slice(1);
|
|
79
|
+
}
|
|
80
|
+
else if (LEGACY_FOLDERS[first]) {
|
|
81
|
+
base = LEGACY_FOLDERS[first];
|
|
82
|
+
rest = parts.slice(1);
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
base = ["ReplicatedStorage"];
|
|
86
|
+
rest = parts;
|
|
87
|
+
}
|
|
88
|
+
return [...base, ...rest, stem];
|
|
89
|
+
}
|
|
90
|
+
function classForFile(fileName) {
|
|
91
|
+
const ctx = (0, context_js_1.runContextOf)(fileName);
|
|
92
|
+
return ctx === "server" ? "Script" : ctx === "client" ? "LocalScript" : ctx === "plugin" ? "Script" : "ModuleScript";
|
|
93
|
+
}
|
|
94
|
+
function collectClp(dir, out = []) {
|
|
95
|
+
if (!node_fs_1.default.existsSync(dir)) {
|
|
96
|
+
return out;
|
|
97
|
+
}
|
|
98
|
+
for (const entry of node_fs_1.default.readdirSync(dir, { withFileTypes: true })) {
|
|
99
|
+
const full = node_path_1.default.join(dir, entry.name);
|
|
100
|
+
if (entry.isDirectory()) {
|
|
101
|
+
collectClp(full, out);
|
|
102
|
+
}
|
|
103
|
+
else if (entry.name.toLowerCase().endsWith(exports.SOURCE_EXT)) {
|
|
104
|
+
out.push(full);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return out;
|
|
108
|
+
}
|
|
109
|
+
/** Typed `game` tree: services → folders → scripts compiled from Src, plus CluauppLibs. */
|
|
110
|
+
function buildDataModel(api, srcDir, files, libs = []) {
|
|
111
|
+
const root = { name: "game", className: "DataModel", path: [], children: new Map() };
|
|
112
|
+
const ensure = (segments, leafClass, sourceFile) => {
|
|
113
|
+
let node = root;
|
|
114
|
+
segments.forEach((seg, i) => {
|
|
115
|
+
let child = node.children.get(seg);
|
|
116
|
+
const isLeaf = i === segments.length - 1;
|
|
117
|
+
if (!child) {
|
|
118
|
+
const cls = i === 0 && api.isService(seg) ? seg : isLeaf ? leafClass : i === 1 && segments[0] === "StarterPlayer" ? seg : "Folder";
|
|
119
|
+
child = { name: seg, className: cls, path: segments.slice(0, i + 1), children: new Map() };
|
|
120
|
+
node.children.set(seg, child);
|
|
121
|
+
}
|
|
122
|
+
if (isLeaf && sourceFile) {
|
|
123
|
+
child.className = leafClass;
|
|
124
|
+
child.sourceFile = sourceFile;
|
|
125
|
+
}
|
|
126
|
+
node = child;
|
|
127
|
+
});
|
|
128
|
+
};
|
|
129
|
+
for (const svc of ["Workspace", "Players", "ReplicatedStorage", "ReplicatedFirst", "ServerScriptService", "ServerStorage", "StarterGui", "StarterPack", "StarterPlayer", "Lighting", "SoundService", "Teams"]) {
|
|
130
|
+
ensure([svc], svc);
|
|
131
|
+
}
|
|
132
|
+
ensure(["StarterPlayer", "StarterPlayerScripts"], "StarterPlayerScripts");
|
|
133
|
+
ensure(["StarterPlayer", "StarterCharacterScripts"], "StarterCharacterScripts");
|
|
134
|
+
for (const file of files) {
|
|
135
|
+
const rel = node_path_1.default.relative(srcDir, file).replace(/\\/g, "/");
|
|
136
|
+
ensure(dataModelPath(api, rel), classForFile(file), node_path_1.default.resolve(file));
|
|
137
|
+
}
|
|
138
|
+
if (libs.length) {
|
|
139
|
+
ensure(exports.LIBS_FOLDER, "Folder");
|
|
140
|
+
for (const lib of libs) {
|
|
141
|
+
ensure([...exports.LIBS_FOLDER, lib], "ModuleScript");
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return root;
|
|
145
|
+
}
|
|
146
|
+
/** Luau expression that reaches an Instance by its DataModel path, robust to replication order. */
|
|
147
|
+
function instancePath(dm, service, fromDm) {
|
|
148
|
+
if (fromDm && fromDm.length > 1 && dm.length > 1) {
|
|
149
|
+
const fromParent = fromDm.slice(0, -1);
|
|
150
|
+
const targetParent = dm.slice(0, -1);
|
|
151
|
+
if (fromParent.join("/") === targetParent.join("/")) {
|
|
152
|
+
return `script.Parent${childAccess(dm[dm.length - 1])}`;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
const [first, ...rest] = dm;
|
|
156
|
+
let expr = first === "Workspace" ? "workspace" : service(first);
|
|
157
|
+
for (const seg of rest) {
|
|
158
|
+
expr += childAccess(seg);
|
|
159
|
+
}
|
|
160
|
+
return expr;
|
|
161
|
+
}
|
|
162
|
+
function childAccess(name) {
|
|
163
|
+
return `:WaitForChild(${(0, names_js_1.luauString)(name)})`;
|
|
164
|
+
}
|
|
165
|
+
class ProjectCompiler {
|
|
166
|
+
options;
|
|
167
|
+
api;
|
|
168
|
+
files;
|
|
169
|
+
dataModel;
|
|
170
|
+
results = new Map();
|
|
171
|
+
inProgress = [];
|
|
172
|
+
byDmPath = new Map();
|
|
173
|
+
libs;
|
|
174
|
+
constructor(options, libs = []) {
|
|
175
|
+
this.options = options;
|
|
176
|
+
this.api = options.api ?? (0, api_js_1.robloxApi)();
|
|
177
|
+
this.files = collectClp(options.srcDir).map((f) => node_path_1.default.resolve(f));
|
|
178
|
+
this.libs = libs;
|
|
179
|
+
this.dataModel = buildDataModel(this.api, options.srcDir, this.files, libs);
|
|
180
|
+
for (const f of this.files) {
|
|
181
|
+
this.byDmPath.set(this.dmOf(f).join("/"), f);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
rel(file) {
|
|
185
|
+
return node_path_1.default.relative(this.options.srcDir, file).replace(/\\/g, "/");
|
|
186
|
+
}
|
|
187
|
+
dmOf(file) {
|
|
188
|
+
return dataModelPath(this.api, this.rel(file));
|
|
189
|
+
}
|
|
190
|
+
compileAll() {
|
|
191
|
+
return this.files.map((f) => this.compile(f));
|
|
192
|
+
}
|
|
193
|
+
compile(file, text) {
|
|
194
|
+
const key = node_path_1.default.resolve(file);
|
|
195
|
+
const cached = this.results.get(key);
|
|
196
|
+
if (cached && text === undefined) {
|
|
197
|
+
return cached;
|
|
198
|
+
}
|
|
199
|
+
const source = new source_js_1.SourceFile(this.rel(key), text ?? node_fs_1.default.readFileSync(key, "utf8"));
|
|
200
|
+
const diags = new diagnostics_js_1.DiagnosticBag(source);
|
|
201
|
+
const rel = this.rel(key);
|
|
202
|
+
const context = (0, context_js_1.runContextOf)(key);
|
|
203
|
+
const kind = context === "shared" ? "module" : "script";
|
|
204
|
+
this.inProgress.push(key);
|
|
205
|
+
const program = (0, parser_js_1.parse)(source, diags);
|
|
206
|
+
const dmPath = this.dmOf(key);
|
|
207
|
+
let info = null;
|
|
208
|
+
try {
|
|
209
|
+
info = new checker_js_1.Checker({
|
|
210
|
+
program,
|
|
211
|
+
source,
|
|
212
|
+
diags,
|
|
213
|
+
api: this.api,
|
|
214
|
+
moduleId: key,
|
|
215
|
+
kind,
|
|
216
|
+
context,
|
|
217
|
+
strict: this.options.strict,
|
|
218
|
+
resolveLink: (link) => this.resolveLink(link, key, diags),
|
|
219
|
+
dataModel: this.dataModel,
|
|
220
|
+
dmPath,
|
|
221
|
+
}).run();
|
|
222
|
+
}
|
|
223
|
+
finally {
|
|
224
|
+
this.inProgress.pop();
|
|
225
|
+
}
|
|
226
|
+
let luau = null;
|
|
227
|
+
let sourceMap = [];
|
|
228
|
+
if (diags.errorCount === 0) {
|
|
229
|
+
luau = (0, emitter_js_1.emitLuau)(program, info, this.api, {
|
|
230
|
+
sourceLabel: rel,
|
|
231
|
+
sourcePosition: offset => source.position(offset),
|
|
232
|
+
onSourceMap: lines => { sourceMap = lines; },
|
|
233
|
+
version: this.options.version,
|
|
234
|
+
requireTarget: (l, service) => this.requireTarget(l, dmPath, service),
|
|
235
|
+
libsRoot: (service) => instancePath(exports.LIBS_FOLDER, service, null),
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
const result = { file: key, rel, outRel: outputName(rel), luau, sourceMap, diagnostics: diags.items, source, info };
|
|
239
|
+
this.results.set(key, result);
|
|
240
|
+
return result;
|
|
241
|
+
}
|
|
242
|
+
resolveLink(link, fromFile, diags) {
|
|
243
|
+
const p = link.path;
|
|
244
|
+
if (link.isPackage) {
|
|
245
|
+
if (p === "@clpp.roblox" || p === "@roblox") {
|
|
246
|
+
return { kind: "roblox" };
|
|
247
|
+
}
|
|
248
|
+
const std = stdlib_js_1.STD_PATHS[p];
|
|
249
|
+
if (std && std !== "unsupported") {
|
|
250
|
+
return { kind: "std", module: std };
|
|
251
|
+
}
|
|
252
|
+
if (stdlib_js_1.DESKTOP_ONLY[p]) {
|
|
253
|
+
return { kind: "error", message: `${p} is a desktop module of the clpp VM and does not exist on Roblox`, notes: [stdlib_js_1.DESKTOP_ONLY[p]] };
|
|
254
|
+
}
|
|
255
|
+
const lib = /^@(?:clpp\.libs|cluaupp)\.([A-Za-z0-9_]+)$/.exec(p);
|
|
256
|
+
if (lib) {
|
|
257
|
+
const name = this.libs.find((l) => l.toLowerCase() === lib[1].toLowerCase());
|
|
258
|
+
if (!name && this.libs.length) {
|
|
259
|
+
return { kind: "error", message: `CluauppLibs has no library '${lib[1]}'`, notes: [`available: ${this.libs.join(", ")}`] };
|
|
260
|
+
}
|
|
261
|
+
const libName = name ?? lib[1][0].toUpperCase() + lib[1].slice(1);
|
|
262
|
+
return { kind: "luau", dmPath: [...exports.LIBS_FOLDER, libName], label: libName };
|
|
263
|
+
}
|
|
264
|
+
if (p.startsWith("@game.") || /^@[A-Z]/.test(p)) {
|
|
265
|
+
const shorthand = !p.startsWith("@game.");
|
|
266
|
+
const segments = p.slice(shorthand ? 1 : "@game.".length).split(".");
|
|
267
|
+
if (!this.api.isService(segments[0])) {
|
|
268
|
+
return { kind: "error", message: `'${segments[0]}' is not a Roblox service`, notes: ["link @ReplicatedStorage.Shared.Module as Module;"] };
|
|
269
|
+
}
|
|
270
|
+
const fileHit = this.byDmPath.get(segments.join("/"));
|
|
271
|
+
if (fileHit) {
|
|
272
|
+
return this.linkFile(fileHit, fromFile);
|
|
273
|
+
}
|
|
274
|
+
if (shorthand) {
|
|
275
|
+
return { kind: "error", message: `no .clp module at ${p}`, notes: [`create src/${segments.join("/")}.clp or use a relative link to the file`] };
|
|
276
|
+
}
|
|
277
|
+
return { kind: "luau", dmPath: segments, label: segments.join(".") };
|
|
278
|
+
}
|
|
279
|
+
return { kind: "error", message: `unknown module ${p}`, notes: ["@clpp.roblox, @clpp.axiom, @clpp.libs.<Lib>, @ReplicatedStorage.Shared.<Module>, @game.<Service>.<…>"] };
|
|
280
|
+
}
|
|
281
|
+
let target = node_path_1.default.resolve(node_path_1.default.dirname(fromFile), p);
|
|
282
|
+
if (!node_fs_1.default.existsSync(target) && node_fs_1.default.existsSync(`${target}.clp`)) {
|
|
283
|
+
target = `${target}.clp`;
|
|
284
|
+
}
|
|
285
|
+
if (!target.toLowerCase().endsWith(exports.SOURCE_EXT)) {
|
|
286
|
+
return { kind: "error", message: `cannot link '${p}': CL++ modules are .clp files`, notes: [/\.(clh|clpp)$/i.test(p) ? "Legacy .clh/.clpp files use a different syntax: migrate the declarations and imports to .clp; changing the extension alone is insufficient" : "link \"./file.clp\" as Name;"] };
|
|
287
|
+
}
|
|
288
|
+
if (!node_fs_1.default.existsSync(target)) {
|
|
289
|
+
return { kind: "error", message: `cannot open module '${p}'` };
|
|
290
|
+
}
|
|
291
|
+
void diags;
|
|
292
|
+
return this.linkFile(target, fromFile);
|
|
293
|
+
}
|
|
294
|
+
linkFile(target, fromFile) {
|
|
295
|
+
const key = node_path_1.default.resolve(target);
|
|
296
|
+
if (this.inProgress.includes(key)) {
|
|
297
|
+
const cycle = [...this.inProgress.slice(this.inProgress.indexOf(key)), key].map((f) => this.rel(f));
|
|
298
|
+
return { kind: "error", message: "circular link", notes: [cycle.join(" → ")] };
|
|
299
|
+
}
|
|
300
|
+
if (key === node_path_1.default.resolve(fromFile)) {
|
|
301
|
+
return { kind: "error", message: "a module cannot link itself" };
|
|
302
|
+
}
|
|
303
|
+
const compiled = this.compile(key);
|
|
304
|
+
if (!compiled.info) {
|
|
305
|
+
return { kind: "error", message: `module ${this.rel(key)} failed to compile` };
|
|
306
|
+
}
|
|
307
|
+
if (compiled.diagnostics.some((d) => d.severity === "error")) {
|
|
308
|
+
const first = compiled.diagnostics.find((d) => d.severity === "error");
|
|
309
|
+
return { kind: "error", message: `module ${this.rel(key)} has errors`, notes: [`${first.file}:${first.line}:${first.column}: ${first.message}`] };
|
|
310
|
+
}
|
|
311
|
+
const exports = compiled.info.exports;
|
|
312
|
+
return { kind: "clp", exports, dmPath: this.dmOf(key), file: key };
|
|
313
|
+
}
|
|
314
|
+
requireTarget(link, fromDm, service) {
|
|
315
|
+
const t = link.target;
|
|
316
|
+
if (t.kind === "clp" && t.dmPath) {
|
|
317
|
+
return instancePath(t.dmPath, service, fromDm);
|
|
318
|
+
}
|
|
319
|
+
if (t.kind === "luau") {
|
|
320
|
+
return instancePath(t.dmPath, service, fromDm);
|
|
321
|
+
}
|
|
322
|
+
return null;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
exports.ProjectCompiler = ProjectCompiler;
|
|
326
|
+
/** Compile one file with no project around it (tests, playground, `cluaupp check <file>`). */
|
|
327
|
+
function compileSource(text, fileName = "Main.server.clp", options = {}) {
|
|
328
|
+
const root = options.root ?? process.cwd();
|
|
329
|
+
const srcDir = options.srcDir ?? root;
|
|
330
|
+
const compiler = new ProjectCompiler({ root, srcDir, strict: options.strict ?? false, version: options.version ?? "dev", api: options.api });
|
|
331
|
+
return compiler.compile(node_path_1.default.resolve(srcDir, fileName), text);
|
|
332
|
+
}
|