using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Scripting; using Microsoft.CodeAnalysis.Scripting; namespace ZB.MOM.WW.ScadaBridge.ScriptAnalysis; /// /// M3.1: the single authoritative Roslyn compile gate. Ported from the /// SiteRuntime ScriptCompilationService.CompileCore, but returns /// diagnostic messages rather than a compiled Script delegate — this is /// the design-time gate (the deploy-time validation that previously relied on /// the FAKE substring + brace-balance check in /// TemplateEngine/Validation/ScriptCompiler.cs), which needs to know /// whether the script compiles, not to execute it. /// public static class RoslynScriptCompiler { /// /// Parses the script as C# script source and returns syntax-error diagnostic /// messages (severity Error only). Empty list means the script parses. /// /// The C# script source to parse. /// Error-severity parse diagnostic messages; empty if the script parses. public static IReadOnlyList ParseDiagnostics(string code) { var tree = CSharpSyntaxTree.ParseText( code, new CSharpParseOptions(kind: SourceCodeKind.Script)); return tree.GetDiagnostics() .Where(d => d.Severity == DiagnosticSeverity.Error) .Select(d => d.GetMessage()) .ToList(); } /// /// Compiles the script against the trust-model references and imports and /// returns error-severity compilation diagnostic messages. Empty list means /// the script compiles cleanly against . /// /// The C# script source to compile. /// /// Optional globals type the script binds against — e.g. /// ScriptCompileSurface for instance/shared scripts or /// TriggerCompileSurface for trigger expressions. /// /// Optional additional metadata references. /// Optional additional namespace imports. /// Error-severity compile diagnostic messages; empty if the script compiles. public static IReadOnlyList Compile( string code, Type? globalsType = null, IEnumerable? extraReferences = null, IEnumerable? extraImports = null) { try { var references = ScriptTrustPolicy.DefaultReferences.ToList(); if (extraReferences != null) references.AddRange(extraReferences); var imports = ScriptTrustPolicy.DefaultImports.AsEnumerable(); if (extraImports != null) imports = imports.Concat(extraImports); var options = ScriptOptions.Default .WithReferences(references) .WithImports(imports); var script = CSharpScript.Create(code, options, globalsType); var diagnostics = script.Compile(); return diagnostics .Where(d => d.Severity == DiagnosticSeverity.Error) .Select(d => d.GetMessage()) .ToList(); } catch (Exception ex) { return [$"Compilation exception: {ex.Message}"]; } } }