compileFile method
Implementation
Future<void> compileFile(File file) async {
print("Analyzing source code...");
final normalizedPath = p.normalize(file.absolute.path);
final context = collection.contextFor(normalizedPath);
final session = context.currentSession;
final parseResult = await session.getResolvedUnit(normalizedPath);
if (parseResult is! ResolvedUnitResult) {
print("Error: Could not resolve file.");
return;
}
final astRoot = parseResult.unit;
print(
"Source code analized! Found ${astRoot.declarations.length} declarations",
);
final visitor = RobloxVisitor();
visitor.reset();
visitor.rojoResolver = rojoResolver;
visitor.projectRoot = p.normalize(
p.absolute(context.contextRoot.root.path),
);
visitor.currentFilePath = normalizedPath;
if (sourceRoot != null) {
visitor.runtimePath =
'game:GetService("ReplicatedStorage"):WaitForChild("include"):WaitForChild("RuntimeLib")';
} else {
final relativeFilePath = p.relative(
normalizedPath,
from: Directory.current.path,
);
final levels =
p.split(p.dirname(relativeFilePath)).where((s) => s != '.').length;
final parentPrefix = List.filled(levels + 1, 'Parent').join('.');
visitor.runtimePath =
'(script.$parentPrefix :: any):WaitForChild("include"):WaitForChild("RuntimeLib")';
}
for (var dartNode in astRoot.declarations) {
if (dartNode is ClassDeclaration) {
final body = dartNode.body;
if (body is BlockClassBody) {
final members = body.members;
for (var member in members) {
if (member is FieldDeclaration) {
final isStatic = member.isStatic;
for (var variable in member.fields.variables) {
visitor.allClassMembers.add(variable.name.lexeme);
if (isStatic) {
visitor.staticClassMembers.add(variable.name.lexeme);
}
}
} else if (member is MethodDeclaration) {
visitor.allClassMembers.add(member.name.lexeme);
if (member.isStatic) {
visitor.staticClassMembers.add(member.name.lexeme);
}
}
}
}
}
}
String finalLuauCode = "";
if (astRoot.directives.any(
(d) =>
d is ImportDirective &&
!(d.uri.stringValue?.startsWith('package:roblox_dart/') ?? false),
)) {
finalLuauCode += "local _RD = require(${visitor.runtimePath!})\n";
}
bool hasMain = false;
List<String> forwardDeclarations = [];
for (var dartNode in astRoot.declarations) {
if (dartNode is FunctionDeclaration) {
forwardDeclarations.add("local ${dartNode.name.lexeme}");
}
}
if (forwardDeclarations.isNotEmpty) {
finalLuauCode += forwardDeclarations.join("\n");
finalLuauCode += "\n\n";
}
for (var directive in astRoot.directives) {
try {
final lego = directive.accept(visitor);
if (lego != null) {
finalLuauCode += lego.emit();
}
} catch (e) {
print("CRASH during visit of directive: ${directive.toSource()}");
print("ERROR: $e");
rethrow;
}
}
if (astRoot.directives.isNotEmpty) finalLuauCode += "\n";
for (var dartNode in astRoot.declarations) {
if (dartNode is FunctionDeclaration && dartNode.name.lexeme == "main") {
hasMain = true;
}
try {
final masterLego = dartNode.accept(visitor);
if (masterLego != null) {
finalLuauCode += masterLego.emit();
}
} catch (e) {
print("CRASH during visit of: ${dartNode.toSource()}");
print("ERROR: $e");
rethrow;
}
}
if (visitor.exports.isNotEmpty && !hasMain) {
finalLuauCode += "\nlocal Exports = {\n";
for (var export in visitor.exports) {
if (export.contains('.')) {
final parts = export.split('.');
final name = parts.last;
finalLuauCode += " $name = $export,\n";
} else {
finalLuauCode += " $export = $export,\n";
}
}
finalLuauCode += "}\nreturn Exports\n";
}
if (hasMain) {
finalLuauCode += "main()\n";
}
final String from = sourceRoot ?? Directory.current.path;
final String relativePath = p.relative(file.path, from: from);
final String luauRelativePath = relativePath.replaceAll(".dart", ".luau");
final String outDirPath = p.join(Directory.current.path, "out");
final String outPath = p.join(outDirPath, luauRelativePath);
Directory(p.dirname(outPath)).createSync(recursive: true);
final outputFile = File(outPath);
await outputFile.writeAsString(finalLuauCode);
final runtimeDirPath = p.join(outDirPath, "include");
Directory(runtimeDirPath).createSync(recursive: true);
final runtimeFile = File(p.join(runtimeDirPath, "RuntimeLib.luau"));
await runtimeFile.writeAsString('''
local RuntimeLib = {}
function RuntimeLib.import(scriptInstance, ...)
local segments = {...}
local current = scriptInstance
for _, segment in ipairs(segments) do
if segment == ".." or segment == "Parent" then
current = current.Parent
elseif segment == "." then
-- Stay
else
current = current:WaitForChild(segment)
end
end
return require(current)
end
return RuntimeLib
''');
print("Luau code saved to $outPath");
print("\n--- Luau Output ---\n");
print(finalLuauCode);
}