Using the compiler
Using the WebSharper compiler programmatically involves 3 main steps:
- Gather the WebSharper metadata, which is an object of type
WebSharper.Core.Metadata.Info. If you want to use WebSharper's .NET andFSharp.Coreproxies, or have other WebSharper projects/bindings you rely on, you must load the embedded metadata from your referenced assemblies, then merge them to a single metadata object for optimized dictionary lookups and ensuring no conflicts exist. - Create a
WebSharper.Compiler.Compilationinstance with this metadata, and populate it with types/members to translate. This can be done with different helpers depending on you are working from a checked F#, or C# project, or ReflectedDefinitions in an already compiled F#dll. Then callWebSharper.Compiler.Translator.DotNetToJavaScript.CompileFullon theCompilationto run the full translation. The resulting errors/warnings can be checked and handled. - Depending on the output required, code can be modularized (WebSharper calls creating a standalone module bundling, and creating modules with interdependencies packaging) and written to JavaScript/TypeScript/TypeScript declaration file(s). To create a
dllthat is usable by other WebSharper projects, the resulting current metadata can be serialized and embedded into thedll.
The packages needed are WebSharper.Compiler.Common for core functionality, WebSharper.Compiler.FSharp for processing F# code/projects, and WebSharper.Compiler.CSharp for processing C# code/projects.
Handling metadata and code dependency graphs
WebSharper-compiled dlls may have two kinds of metadata embedded in them: the metadata needed for compilation using that dll as a reference, and for web projects only, a runtime metadata that is optimized for the needs of the WebSharper sitelet and remoting middleware. For compilation purposes we need the non-runtime metadata.
Three options to load metadata from an assembly, all of these return an option value, None if no WebSharper metadata is present:
- Use
WebSharper.Core.Metadata.IO.LoadMetadatato deserialize metadata from aSystem.Reflection.Assembly. This assembly must not be loaded in reflection-only mode (e.g.Assembly.ReflectionOnlyLoad), so it requires loading the assembly to the defaultAssemblyLoadContextor an isolated one. - The
WebSharper.Compiler.FrontEnd.ReadFullFromFileusesMono.Cecilin the back to safely load metadata from adllfile path. - If also want caching and looking up
dlls by file names only in pre-determined locations or directories, you can create aWebSharper.Compiler.AssemblyResolverinstance withAssemblyResolver.Create(). Then you can adddllfile paths with theSearchPathsmethod, or directory paths to enumerate and add alldlls found with theSearchDirectoriesmethod. Once theAssemblyResolveris set up, create aWebSharper.Compiler.LoaderwithLoader.Create. It takes theAssemblyResolverand a logger function. Now you can use itsLoadFilemethod to get aWebSharper.Compiler.Assemblyencapsulation of the assembly, and it is also cached. Now theWebSharper.FrontEnd.TryReadFromAssemblyorReadFromAssemblygets the metadata from this assembly (the difference is thatTryReadFromAssemblyreturns metadata load failures as a value, not an exception). These also take aWebSharper.Core.Metadata.MetadataOptionsvalue, see the next section.
Filtered metadata
The WebSharper.Core.Metadata.Info type has three methods for discarding expressions inside for optimizing memory size.
DiscardExpressionsremoves all expressions, this is the default when creating runtime metadata, however for compilation this is undesirable.DiscardInlineExpressionscan be used when the metadata will be used only to create new bundles, but not generating new code that could possibly use WebSharper's[<Inline>]annotated type members.DiscardNotInlineExpressionscan be used when the metadata will be used only to compile and write new code, that will not include any new code from references, but using JS imports for them.
The WebSharper.Core.Metadata.MetadataOptions type can specify a filter on which of the above to apply or none, FullMetadata applies none, while the other three values are matching the name of the corresponding Discard... method they use.
Merging metadata
When metadata are loaded from all references of a project, they must be merged into a single WebSharper.Core.Metadata.Info object for optimized dictionary lookups. This can be done with the Metadata.Info.UnionWithoutDependencies static method which takes a collection of metadata objects. The method name clarifies that the merged metadata will not contain a merge of the code dependency graph. This is because the dependency graph is only required for DCE (dead code elimination), not for any other compilation steps, so it should be done separately when neeeded, see later.
The UnionWithoutDependencies method can raise exceptions when conflicting members are found (for example the same member is proxied in two different references). This is a fatal error because it cannot be determined which client-side representation to use for a consistently working translation.