Giter Site home page Giter Site logo

toddams / razorlight Goto Github PK

View Code? Open in Web Editor NEW
1.5K 63.0 259.0 1.59 MB

Template engine based on Microsoft's Razor parsing engine for .NET Core

License: Apache License 2.0

C# 98.64% Batchfile 0.04% HTML 1.32%
razor razor-parsing-engine template-engine dotnet-core netcore razorengine razorengine-alternative

razorlight's Introduction

RazorLight

Use Razor to build templates from Files / EmbeddedResources / Strings / Database or your custom source outside of ASP.NET MVC. No redundant dependencies and workarounds in pair with excellent performance and .NET Standard 2.0 and .NET Core 3.0 support.

Build Status NuGet Pre Release NuGet downloads Join the chat at https://gitter.im/gitterHQ/gitter

Solidarity with Ukraine

ComeBackAlive Dear friends, my name is Ivan, I am the guy who created this library. I live in Ukraine, and if you are reading this message - I really hope you and your family are safe and healthy. 24 February Russia invaded my country with a series of missle atacks across entire Ukraine, from East to West. They started with destroying military infrastructure, and so-called "special operation", as they call it, in fact is a full scale war against us.

Update: it's been a long time since I first posted this message. Thank you for your enormous support, I am removing my volunteer donation account and instead providing you with the largest and proven charity organization in Ukraine - ComeBackAlive. If you have the possibility and desire to help Ukraine - that is the right place for your valuable donations. Thank you. Be safe

Table of contents

Quickstart

Install the nuget package using following command:

Install-Package RazorLight -Version 2.3.0

The simplest scenario is to create a template from string. Each template must have a templateKey that is associated with it, so you can render the same template next time without recompilation.

var engine = new RazorLightEngineBuilder()
	// required to have a default RazorLightProject type,
	// but not required to create a template from string.
	.UseEmbeddedResourcesProject(typeof(ViewModel))
	.SetOperatingAssembly(typeof(ViewModel).Assembly)
	.UseMemoryCachingProvider()
	.Build();

string template = "Hello, @Model.Name. Welcome to RazorLight repository";
ViewModel model = new ViewModel {Name = "John Doe"};

string result = await engine.CompileRenderStringAsync("templateKey", template, model);

snippet source | anchor

To render a compiled template:

var cacheResult = engine.Handler.Cache.RetrieveTemplate("templateKey");
if(cacheResult.Success)
{
	var templatePage = cacheResult.Template.TemplatePageFactory();
	string result = await engine.RenderTemplateAsync(templatePage, model);
}

snippet source | anchor

Template sources

RazorLight can resolve templates from any source, but there are a built-in providers that resolve template source from filesystem and embedded resources.

File source

When resolving a template from filesystem, templateKey - is a relative path to the root folder, that you pass to RazorLightEngineBuilder.

var engine = new RazorLightEngineBuilder()
	.UseFileSystemProject("C:/RootFolder/With/YourTemplates")
	.UseMemoryCachingProvider()
	.Build();

var model = new {Name = "John Doe"};
string result = await engine.CompileRenderAsync("Subfolder/View.cshtml", model);

snippet source | anchor

EmbeddedResource source

For embedded resource, the key is the namespace of the project where the template exists combined with the template's file name.

The following examples are using this project structure:

Project/
  Model.cs
  Program.cs
  Project.csproj
Project.Core/
  EmailTemplates/
    Body.cshtml
  Project.Core.csproj
  SomeService.cs

var engine = new RazorLightEngineBuilder()
	.UseEmbeddedResourcesProject(typeof(SomeService).Assembly)
	.UseMemoryCachingProvider()
	.Build();

var model = new Model();
string html = await engine.CompileRenderAsync("EmailTemplates.Body", model);

snippet source | anchor

Setting the root namespace allows you to leave that piece off when providing the template name as the key:

var engine = new RazorLightEngineBuilder()
	.UseEmbeddedResourcesProject(typeof(SomeService).Assembly, "Project.Core.EmailTemplates")
	.UseMemoryCachingProvider()
	.Build();

var model = new Model();
string html = await engine.CompileRenderAsync("Body", model);

snippet source | anchor

Custom source

If you store your templates in database - it is recommended to create custom RazorLightProject that is responsible for gettings templates source from it. The class will be used to get template source and ViewImports. RazorLight will use it to resolve Layouts, when you specify it inside the template.

var project = new EntityFrameworkRazorProject(new AppDbContext());
var engine = new RazorLightEngineBuilder()
              .UseProject(project)
              .UseMemoryCachingProvider()
              .Build();

// For key as a GUID
string result = await engine.CompileRenderAsync("6cc277d5-253e-48e0-8a9a-8fe3cae17e5b", new { Name = "John Doe" });

// Or integer
int templateKey = 322;
string result = await engine.CompileRenderAsync(templateKey.ToString(), new { Name = "John Doe" });

You can find a full sample here

Includes (aka Partial views)

Include feature is useful when you have reusable parts of your templates you want to share between different views. Includes are an effective way of breaking up large templates into smaller components. They can reduce duplication of template content and allow elements to be reused. This feature requires you to use the RazorLight Project system, otherwise there is no way to locate the partial.

@model MyProject.TestViewModel
<div>
    Hello @Model.Title
</div>

@{ await IncludeAsync("SomeView.cshtml", Model); }

First argument takes a key of the template to resolve, second argument is a model of the view (can be null)

Encoding

By the default RazorLight encodes Model values as HTML, but sometimes you want to output them as is. You can disable encoding for specific value using @Raw() function

/* With encoding (default) */

string template = "Render @Model.Tag";
string result = await engine.CompileRenderAsync("templateKey", template, new { Tag = "<html>&" });

Console.WriteLine(result); // Output: &lt;html&gt;&amp

/* Without encoding */

string template = "Render @Raw(Model.Tag)";
string result = await engine.CompileRenderAsync("templateKey", template, new { Tag = "<html>&" });

Console.WriteLine(result); // Output: <html>&

In order to disable encoding for the entire document - just set "DisableEncoding" variable to true

@model TestViewModel
@{
    DisableEncoding = true;
}

<html>
    Hello @Model.Tag
</html>

Enable Intellisense support

Visual Studio tooling knows nothing about RazorLight and assumes, that the view you are using - is a typical ASP.NET MVC template. In order to enable Intellisense for RazorLight templates, you should give Visual Studio a little hint about the base template class, that all your templates inherit implicitly

@using RazorLight
@inherits TemplatePage<MyModel>

<html>
    Your awesome template goes here, @Model.Name
</html>

Intellisense

FAQ

Coding Challenges (FAQ)

How to use templates from memory without setting a project?

The short answer is, you have to set a project to use the memory caching provider. The project doesn't have to do anything. This is by design, as without a project system, RazorLight cannot locate partial views.

❌ You used to be able to write:

var razorEngine = new RazorLightEngineBuilder()
.UseMemoryCachingProvider()
.Build();

... but this now throws an exception, saying, "_razorLightProject cannot be null".

✔️

var razorEngine = new RazorLightEngineBuilder()
                .UseEmbeddedResourcesProject(typeof(AnyTypeInYourSolution)) // exception without this (or another project type)
                .UseMemoryCachingProvider()
                .Build();

Affects: RazorLight-2.0.0-beta1 and later.

Original Issue: #250

How to embed an image in an email?

This isn't a RazorLight question, but please see this StackOverflow answer.

How to embed css in an email?

This isn't a RazorLight question, but please look into PreMailer.Net.

Compilation and Deployment Issues (FAQ)

Most problems with RazorLight deal with deploying it on a new machine, in a docker container, etc. If it works fine in your development environment, read this list of problems to see if it matches yours.

Additional metadata references

When RazorLight compiles your template - it loads all the assemblies from your entry assembly and creates MetadataReference from it. This is a default strategy and it works in 99% of the time. But sometimes compilation crashes with an exception message like "Can not find assembly My.Super.Assembly2000". In order to solve this problem you can pass additional metadata references to RazorLight.

var metadataReference = MetadataReference.CreateFromFile("path-to-your-assembly")

 var engine = new RazorLightEngineBuilder()
                .UseMemoryCachingProvider()
                .AddMetadataReferences(metadataReference)
                .Build();

I'm getting errors after upgrading to ASP.NET Core 3.0 when using runtime compilation

Please see: https://docs.microsoft.com/en-us/aspnet/core/razor-pages/sdk?view=aspnetcore-3.1#use-the-razor-sdk

Starting with ASP.NET Core 3.0, MVC Views or Razor Pages aren't served by default if the RazorCompileOnBuild or RazorCompileOnPublish MSBuild properties in the project file are disabled. Applications must add an explicit reference to the Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation package if the app relies on runtime compilation to process .cshtml files.

I'm getting a Null Reference Exception after upgrading to RazorLight-2.0.0-beta2 or later.

The most common scenario is that some people were using RazorLight's ability to render raw strings as templates. While this is still somewhat supported (you can't use advanced features like partial views), what is not supported (right now) is using the caching provider with raw strings. A workaround is to use a dummy class.

I'm getting "Cannot find compilation library" when I deploy this library on another server

Add these property groups to your entry point csproj. It has to be the entry point project. For example: ASP.NET Core web project, .NET Core Console project, etc.

  <PropertyGroup>
    <!-- This group contains project properties for RazorLight on .NET Core -->
    <PreserveCompilationContext>true</PreserveCompilationContext>
    <MvcRazorCompileOnPublish>false</MvcRazorCompileOnPublish>
    <MvcRazorExcludeRefAssembliesFromPublish>false</MvcRazorExcludeRefAssembliesFromPublish>
  </PropertyGroup>

I'm getting "Can't load metadata reference from the entry assembly" exception

Set PreserveCompilationContext to true in your *.csproj file's PropertyGroup tag.

<PropertyGroup>
    ...
    <PreserveCompilationContext>true</PreserveCompilationContext>
</PropertyGroup>

Additionally, RazorLight allows you to specifically locate any MetadataReference you can't find, which could happen if you're running in SCD (Self-Contained Deployment) mode, as the C# Compiler used by RazorLight needs to be able to locate mscorlib.dll. This might be a useful trick if future versions of the .NET SDK tools ship with bad MSBuild targets that somehow don't "preserve compilation context" and you need an immediate fix while waiting for Microsoft support.

I'm getting "Cannot find reference assembly 'Microsoft.AspNetCore.Antiforgery.dll'" exception on .NET Core App 3.0 or higher

By default, the 3.0 SDK avoids copying references to the build output. Set PreserveCompilationReferences and PreserveCompilationContext to true in your *.csproj file's PropertyGroup tag.

<PropertyGroup>
    <PreserveCompilationReferences>true</PreserveCompilationReferences>
    <PreserveCompilationContext>true</PreserveCompilationContext>
</PropertyGroup>

For more information, see dotnet/aspnetcore#14418 (comment) (which discusses the above flags) and microsoft/DockerTools#217 (comment) (which discusses that Runtime Compilation feature was marked obsolete in ASP.NET Core 2.2, and removed from the default template in ASP.NET Core 3.0).

RazorLight does not work properly on AWS Lambda or Azure Functions

Serverless solutions are not supported yet. However, for Azure Functions, some users have reported success on Azure Functions 3.0.3. As of 6/3/2020, Azure Functions SDK team has acknowledged a bug in Azure Functions RemoveRuntimeDependencies task, affecting Azure Functions 3.0.4-3.0.6 releases.

For Azure Functions 3.0.4-3.0.5, the known workaround is to disable "Azure Functions dependency trimming". To disable dependency trimming, add the following to your root / entrypoint project:

<PropertyGroup>
  <_FunctionsSkipCleanOutput>true</_FunctionsSkipCleanOutput>
</PropertyGroup>

In addition, Azure Functions has an open pull request outstanding to update runtimeAssemblies.json: Azure/azure-functions-vs-build-sdk#422

Unsupported Scenarios

RazorLight does not work with ASP.NET Core Integration Testing

RazorLight is not currently designed to support such integration tests. If you need to test your RazorLight tests, current recommendation is to simply create a project called <YourCompanyName>.<YourProjectName>.Templating and write your template rendering layer as a Domain Service, and write tests against that service. Then, you can mock in your integration tests any dependencies on RazorLight.

If you happen to get this working, please let us know what you did.

razorlight's People

Contributors

bezysoftware avatar chancie86 avatar coxp avatar dxinfinity avatar gjunge avatar hakanl avatar hamiltonmanalo avatar jzabroski avatar korsg avatar krmr avatar lars-nielsen avatar m0ns1gn0r avatar maartenmensink avatar markkharitonov avatar matthewwren avatar maxbanas avatar mcrio avatar missaouichedy avatar mrkevhunter avatar mscrivo avatar mwasson74 avatar nm-a avatar reinisv1 avatar rizzen avatar simoncropp avatar toddams avatar vdurante avatar vincentvwal avatar xtansia avatar zhongzf avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

razorlight's Issues

Leftover Artifacts

We have been using RazorEngine in a few projects and it seems to create a bunch of compiled artifacts on the file system that require cleanup. Out of curiosity, does RazorLight leave anything in the filesystem that requires cleanup?

Trying to test on netcore 2.0

If I use from a .netcore 2.0 app I get:

when trying to parse (I think this is compile errors when compiling the template)

How does the compiler know which assemblies to use?

RazorLight.TemplateCompilationException: 'Failed to compile generated Razor template:

- The type or namespace name 'Linq' does not exist in the namespace 'System' (are you missing an assembly reference?)

- The type or namespace name 'Threading' does not exist in the namespace 'System' (are you missing an assembly reference?)

- Cannot define a class or member that utilizes 'dynamic' because the compiler required type 'System.Runtime.CompilerServices.DynamicAttribute' cannot be found. Are you missing a reference?

- Predefined type 'System.Boolean' is not defined or imported

- The type 'Object' is defined in an assembly that is not referenced. You must add a reference to assembly 'System.Runtime, Version=4.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'.

- The return type of an async method must be void, Task or Task<T>

- The type 'Object' is defined in an assembly that is not referenced. You must add a reference to assembly 'System.Runtime, Version=4.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'.

- Predefined type 'System.Object' is not defined or imported

- The type or namespace name 'Task' could not be found (are you missing a using directive or an assembly reference?)

- 'ArmLinkerScriptTemplate_template.ExecuteAsync()': return type must be 'Task' to match overridden member 'TemplatePage.ExecuteAsync()'

- The type 'Task' is defined in an assembly that is not referenced. You must add a reference to assembly 'System.Threading.Tasks, Version=4.0.10.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'.

- Predefined type 'System.Void' is not defined or imported

package restore failure

After upgrading to 1.0.0, I am unable to restore packages. I get the following error:

Version conflict detected for Microsoft.CodeAnalysis.CSharp. 
DemoApp (>= 1.0.0) -> DemoApp.Library (>= 1.0.0) -> RazorLight (>= 1.0.0) -> Microsoft.CodeAnalysis.CSharp (>= 1.3.2) 
DemoApp (>= 1.0.0) -> Microsoft.VisualStudio.Web.CodeGeneration.Design (>= 1.1.0) -> Microsoft.VisualStudio.Web.CodeGeneration.Utils (>= 1.1.0) -> Microsoft.CodeAnalysis.CSharp.Workspaces (>= 1.3.0) -> Microsoft.CodeAnalysis.CSharp (>= 1.3.0).

Downgrading to 1.0.0-rc2 resolves the issue.

Are Layouts Fully Supported?

I'm using razorlight to emulate a "Razor Website" from .NET4

I've got some cshtml files in my wwwroot folder, and a generic controller that handles all requests for .cshtml content containing an engine targeting appropriately. Everything seems to work reasonably well until I try to assign a layout to one of the cshtml pages.

The control returns the requested page as such

var result = await ParseAsync(path, model);
Response.StatusCode = 200;
return Content(result, "text/html");

I'm setting Layout the same way it looks like you do in you tests...

@{
    Layout = "_layout.cshtml";
}

The layout and cshtml page being rendered are both in the engine's root path (wwwroot).

Any thoughts? Let me know if this isn't clear.

Add SDK to global.json

Another minor thing, when I pulled your code I had troubles to restore and build it initially because I have a much newer .NET SDK installed than the one used for the project.

I had to add

{
  "projects": [ ".", "src", "sandbox" ],
  "sdk": {
    "version": "1.0.0-preview2-1-003177"
  }
}

to the global.json which basically hints to the dotnet CLI which version to use to guarantee a successful build. Only then my dotnet knew to load an older SDK and I was able to restore and build the project. Maybe if you have time you can add this, will make it easier when other people fork your project or even when you have multiple SDKs installed but not upgraded all your projects to the latest every time.

Failed to compile generated razor view. See CompilationErrors for detailed information

Hello,
I'm trying to use a non-anonymous object and am getting the following error:

Failed to compile generated razor view. See CompilationErrors for detailed information

    public class TestModel
    {
        public string Name { get; set; }
    }
    var model = new TestModel()
    {
        Name = "John Doe"
    };
    var engine = EngineFactory.CreatePhysical(@"D:\");
    string result = engine.ParseString("Hello @Model.Name ", model, model.GetType());


How can I escape out of parsing

I am trying to do this:

#include "@{Model.ClassName}.h"

to produce
#include "classname.h"

but I get string does not contain a definition for h!

How do I escape out of parsing before the .

Expose compilation errors

I'm trying out RazorLight in a F#/Suave.IO project I'm tinkering with, and so far it seems like a good fit because it's generally easy to use. But one thing that has made things a bit more opaque is that compilation errors caused by the views themselves are not surfaced in the exception thrown by the host. Could you expose these?

Support to @inject

Hi!

I'm trying to use @Inject to inject via .netCore DI system some services in my View.
But "@Inject" it's not a known tag in RazorLight.

Can you implement this in some future version? Do you want some help to do this?

To now I use a workaround.

Rather than
@inject IServiceUri ServiceUri

I used
@{var ServiceUri = new ServiceUri();}

But... I lost DI :'(

Not compatible with .NetFramework v4.6.2

I'm trying to support netcoreapp1.0 and net462 in my project.json but it fails to restore with error message:

error: Package RazorLight 1.0.0-beta3 is not compatible with net462 (.NETFramework,Version=v4.6.2). Package RazorLight 1.0.0-beta3 supports: netstandard1.6 (.NETStandard,Version=v1.6)
error: One or more packages are incompatible with .NETFramework,Version=v4.6.2.

I found on the homepage that

it is also possible to use it on full .NET Framework (>= 4.6.2)

Is it?

Malicious code in a Razor template

I'm using RazorLight in my web application where the user can provide mail templates in Razor syntax.

However this means that the user can do stuff like this

@{
var i = 0;
while (true)
    {
    i += 1;
    <p>Line @i</p>
    }
}

which causes my web application to crash at some point - or stuff like this

@using System.IO
@{ var f = File.ReadAllText(@"/etc/passwd"); }
@f

which is also really bad.

What can I do to prevent such attacks? Is it possible to run the Razor engine in a sandbox?

Cannot use HtmlHelper from view

Using @Html.Raw("<span>test</span>") returns a TemplateCompilationException.
With internal error message "The name 'Html' does not exist in the current context".

I assume you need to setup the HtmlHelper() for it to work?
Any other way i can pass inn a Html string from a model?

services.AddRazorLight() fails on MAC do to invalid watch path construction.

The following call exceptions on MAC
services.AddRazorLight(HostingEnvironment.WebRootPath);

The exception

  The directory name '<path>' is invalid. 
  at System.IO.FileSystemWatcher..ctor(String path, String filter)                                                                        
  at System.IO.FileSystemWatcher..ctor(String path)                                                                                       
  at Microsoft.Extensions.FileProviders.PhysicalFileProvider.CreateFileWatcher(String root)                                               
  at RazorLight.Caching.TrackingCompilerCache..ctor(String root)                                                                          
  at RazorLight.MVC.MvcServiceCollectionExtensions.AddRazorLight(IServiceCollection services, String 

It looks like the path is being concatenated with iteself for some reason... for a path like
/project/src/wwwroot
the reported invalid is
/project/src/wwwroot/project/src/wwwroot

I can confirm the value being passed into the AddRazorLight call is correct (the value returned from HostingEnvironment.WebRootPath).

EngineFactory for ParseString

You have this example

string content = "Hello @Model.Name. Welcome to @Model.Title repository";

var model = new
{
  Name = "John Doe",
  Title = "RazorLight"
};

string result = engine.ParseString(content, model); //Output: Hello John Doe, Welcome to RazorLight repository

Where do I get the engine object from, because there is no constructor CreateString() or sth.

Setting base type and setting namespaces

Hi again. I'm trying to replace RazorEngine with RazorLight in my FormFactory project, but am getting some errors when I run my templates.

My templates are very similar to MVC views (in fact they are MVC views when I run them inside MVC, but I read them and change them when running as standalone templates), e.g. one might look like this:

@using System.ComponentModel.DataAnnotations
@using System.Linq
@using System.Xml.Linq
@using FormFactory  

@{
    @Model.GetCustomAttribute<DisplayAttribute>().Name
    var value = Model.Value as XElement;
    if (value != null) { 
        @Html.Raw(value.ToString())
    }
}

So I have using statements, and am using @model and Model and also Html.

I got this to work with RazorEngine by making the template inherit from a base type with Html and Model on it. How can I set the base type with RazorEngine? It looks like Model is set from the .Parse(..., model) but that won't work for Html

-Also I'm getting errors like - The type or namespace name 'DataAnnotations' does not exist in the namespace 'System.ComponentModel' (are you missing an assembly reference?). Is there a way to tell RazorEngine what assemblies to use?- caused by DataAnnotations having a different namespace in 451/standard

thanks!

ExpandoObject as model type throws RuntimeBinderException

Hi,

This code throws RuntimeBinderException (Additional information: 'System.Dynamic.ExpandoObject' does not contain a definition for 'price')

var engine = EngineFactory.CreatePhysical("D:\\views");

var obj = new ExpandoObject();
dynamic dynamicObj = obj;
dynamicObj.price = 30;

engine.Parse("test.cshtml", obj);

I believe problem occurs because of this line:
https://github.com/toddams/RazorLight/blob/master/src/RazorLight/ModelTypeInfo.cs#L42

Seems that ToExpando() doesn't handle ExpandoObject parameter correctly, as it returns new empty ExpandoObject as a result.

Thanks!

Model value in layout page

It looks like that model object passed to Parse method cannot be accessed in layout page.

Is it intentional or I missing something?

Implement string caching

This issue is caused by #39.

That is a real world scenario, when a template source is loaded from database, and running "ParseString" causes performance issues on more-or-less loaded systems, as templates are not cached

ASP.NET MVC Core integration

Implement integration with ASP.NET MVC Core, so it would become possible to use built-in Dependency Injection and inject services into RazorLight templates via @Inject directory

(Documentation request) How does parse from string work?

The IRazorViewEngine has "get" and "find" methods for parsing views from cshtml files.

This library has an incredibly useful "parse from string" function.

I've looked into the code and can't figure out how it does it. I assume that in order to leverage what's in the framework, it writes the string to a temporary file, then loads it as usual. Unless it's a custom engine?

Since this is a specialised feature that is not available in the framework, please consider documenting it so we can make informed decisions about whether to use it, or rely on cshtml files instead?

For example, how is a string converted to a view? Is it a customised engine, or is it written to file first? If so, does it read the file every time? Is it cached, etc.? High level stuff only.

Thanks for this very useful library.


(Doesn't need to be part of the docs, even some quick notes in this issue would help.)

RazorLight is prerelease

Hi I'd like to use RazorLight standalone in my FormFactory project, but I can't take a dependency on a prerelease package. Could it be released as a normal package?

thanks!

Not compatible with .NetFramework v4.6.1

Hello. Thank you for the great library. It has simple features which are most important. I like this library.

I have a question. Is it possible to make it work with framework 4.6.1? For some reason I can't use next frameworks. So it would be great to make it work with .net 4.6.1.

Thanks.
Igor.

System.IO.IOException: Too many open files

My software periodically compiles mail templates using RazorLight. On one of the installations (running under Linux) I got the following exception:

System.IO.IOException: Too many open files
   at Interop.ThrowExceptionForIoErrno(ErrorInfo errorInfo, String path, Boolean isDirectory, Func`2 errorRewriter)
   at Interop.CheckIo[TSafeHandle](TSafeHandle handle, String path, Boolean isDirectory, Func`2 errorRewriter)
   at Microsoft.Win32.SafeHandles.SafeFileHandle.Open(String path, OpenFlags flags, Int32 mode)
   at System.IO.UnixFileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, FileOptions options, FileStream parent)
   at System.IO.UnixFileSystem.Open(String fullPath, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, FileOptions options, FileStream parent)
   at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, FileOptions options)
   at Microsoft.Extensions.DependencyModel.FileWrapper.OpenRead(String path)
   at Microsoft.Extensions.DependencyModel.DependencyContextLoader.LoadEntryAssemblyContext(IDependencyContextReader reader)
   at Microsoft.Extensions.DependencyModel.DependencyContextLoader.Load(Assembly assembly)
   at RazorLight.UseEntryAssemblyMetadataResolver.GetMetadataReferences()
   at System.Threading.LazyInitializer.EnsureInitializedCore[T](T& target, Boolean& initialized, Object& syncLock, Func`1 valueFactory)
   at RazorLight.Compilation.RoslynCompilerService.get_CompilationReferences()
   at RazorLight.Compilation.RoslynCompilerService.Compile(CompilationContext context)
   at RazorLight.EngineCore.CompileSource(ITemplateSource templateSource, ModelTypeInfo modelTypeInfo)

This looks like a resource leak caused by RazorLight.

I noticed that none of the RazorLight classes implement the IDisposable interface. What do I need to do to make sure RazorLight releases its (unmanaged) resources properly? Or is it a bug in RazorLight?

Project status and roadmap

Hello there,

I'd like to inform you, that project is NOT abandoned. At first, I wanted to collect the issues and then roll a stable version, but since RC-1 is released - not so many issues were reported.
So here is a roadmap for the nearest future:

RC-2

  • As everybody is moving to MSBuild - I will ensure that RazorLight is completely compatiable with it

1.1

  • HTML Helpers

1.2

  • TagHelpers (maybe)
  • async/await support

Thank you all for supporting and contributing, you are awesome

Pass an EngineConfiguration to factory

Now it's not possible to pass EngineConfiguration to EngineFactory to create an engine with a specified configuration. The only way to solve this, is to create engine by hands. Here is an example how to do it for physical files engine

ITemplateManager manager = new FilesystemTemplateManager(root);
ICompilerCache compilerCache = new TrackingCompilerCache(root);

var config = EngineConfiguration.Default;

//Here it is, add something to config file.
config.Activator = new MyFancyTypeActivator();
config.Namespaces.Add("System.Diagnostics");

IEngineCore core = new EngineCore(manager, compilerCache, config); //Pass it here

IPageFactoryProvider pageFactory = new DefaultPageFactory(core.KeyCompile, compilerCache);
IPageLookup pageLookup = new FilesystemPageLookup(pageFactory);

var engine = new RazorLightEngine(core,  pageLookup);

TagHelpers are not supported

Hi,

I would like to use your library for email-templates, which contains links to the application itself. Sadly, RazorLight seems to parse razor code, but not the ASP.NET Core TagHelpers. For example, the asp-controller and asp-action attribute to generate an action-controller link like this:

<a asp-controller="Account" asp-action="Login">Test</a>

Using dependency injection, a instance of IRazorLightEngineis injected in my constructor. There I call the Parsemethod like this:

var html = razorEngineLight.Parse("Shared/Test.cshtml", new object());

But in the rendered html, I don't get a parsed link like

<a href="/Account/Login">Test</a>

as expected. Instead, I get 1:1 the source code with asp-controller and action attribute like showed above. So it seems that the _ViewImports.cshtml file in the View-folder is not globally parsed like ASP.NET Core do it. I tried to add the tag helpers manually using

@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

in the Razor-View, but its not working. As a workaround, I tried to use the old UrlHelpers from ASP.NET like this way:

@{
    var link = Url.Action("Login", "Account", null);
}

<a href="@link">Testlink</a>

This is valid code, as it works in a normal Razor-View. But using RazorLight, I got a RazorLight.TemplateCompilationException: Failed to compile generated razor view. See CompilationErrors for detailed information. In the details, Visual Studio show me that there is one CompilationError (Cound = 1) but there seems no way to extend or otherwise view them.

As a result, it seems not possible to create action-controller links using Razor, which is a problem in my point of view. Would be pleased if this could be fixed.

async/await

Hi,

This library looks really cool. I wanted to ask if there was a specific reason why you used .GetAwaiter().GetResult() instead of the async/await pattern?

I thought this could cause potential deadlocks or at least it is not leveraging the potential performance gain from running IO bound tasks asynchronously.

Was there a reason for it or would you be open to the idea of changing it to async/await?

TagHelpers not supported?

The following should render different Hello World variants based on the current
runtime environment. It appears the addTagHelper directive is not being honored since it is what recognizes the environment tag.

@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<environment names="Development">
    Hello World (Dev)
</environment>
<environment names="Staging,Production">
    Hello World (Prod)
</environment>

project.json is using
"RazorLight.MVC": "1.0.0"

Problem generating list

My code:

var engine = EngineFactory.CreatePhysical(@"D:\engine");
string content = "@foreach (var x in @Model.Items){<li>@x.Name</li>}";
            var model = new
            {
                Name = "John Doe",
                Title = "RazorLight",
                Items = new List<object>
                {
                    new
                    {
                        Name = "a"
                    },
                    new
                    {
                        Name = "B"
                    }
                }
            };
            string result = engine.ParseString(content, model);

Error:
Additional information: 'object' does not contain a definition for 'Name'

DateTime arithmetic inside templates

Hi,
Inside a template I have an expression like "@((Model.ExpirationDate - System.DateTime.UtcNow).Days)".
Let's say that ExpirationDate = DateTime.UtcNow.AddDays(10);

In my unit test I have:

var expected = (ExpirationDate - DateTime.UtcNow).Days.ToString();
var actual = renderer.Render("@((Model.ExpirationDate - System.DateTime.UtcNow).Days)", new {ExpirationDate});
Assert.Equal(expected, actual);

And it throws as actual in this case is 10, but expected is 9. This test works fine when I use DateTime.Now.

Exception on parse when html attribute value templated

repro

using Microsoft.Extensions.PlatformAbstractions;

namespace ConsoleApplication {
    public class Program {
        public static void Main(string[] args) {
            var engine =
                RazorLight.EngineFactory.CreatePhysical(
                    PlatformServices.Default.Application.ApplicationBasePath
                );
            try {
                System.Console.WriteLine(
                    engine.Parse(
                        "Template.cshtml",
                        new TemplateModel {
                            Source = "test",
                            Text = "text"
                        }
                    )
                );
            }
            catch (RazorLight.TemplateCompilationException e) {
                foreach (var item in e.CompilationErrors) {
                    System.Console.WriteLine(item);
                }
            }
        }
    }
    public class TemplateModel {
        public string Source { get; set; }
        public string Text { get; set; }
    }
}

Template.cshtml

<html>
<body>
    <p>
        <a href="@Model.Source">@Model.Text</a>
    </p>
</body>
</html>

project.json

{
    "version": "1.0.0-*",
    "buildOptions": {
        "emitEntryPoint": true,
        "preserveCompilationContext": true,
        "copyToOutput": [
            "Template.cshtml"
        ]
    },
    "dependencies": {
        "Microsoft.Extensions.PlatformAbstractions": "1.0.0",
        "RazorLight": "1.0.0-beta2"
    },
    "frameworks": {
        "netcoreapp1.0": {
            "dependencies": {
                "Microsoft.NetCore.App": {
                    "type": "platform",
                    "version": "1.0.0"
                }
            }
        }
    }
}

Errors:

The name 'BeginWriteAttribute' does not exist in the current context
The name 'WriteAttributeValue' does not exist in the current context
The name 'EndWriteAttribute' does not exist in the current context

No issues when @Model.Source removed from template

Performance of Parsing Strings

My html for emails is stored in the db. I am using the ParseString. I have many emails to send that use different html strings. I ran a test to use the same html string with a new model with different properties. The parse time goes from 200 milliseconds and gradually increases about 25 milliseconds to parse. Why does the parse time increase at all? This can add up significantly when sending thousands of emails. Any way to support caching for parsing strings to reduce this gradual increase? I can see the first parse have the biggest time impact, but subsequent parsing of same string with different model properties should go down significantly and be fairly slower than the original parse. Right??

MvcServiceCollectionExtensions exception

After latest version of vs2017 and sdk for .net core - getting

Message "Method not found: 'System.IServiceProvider Microsoft.Extensions.DependencyInjection.ServiceCollectionContainerBuilderExtensions.BuildServiceProvider(Microsoft.Extensions.DependencyInjection.IServiceCollection)'." string
$exception {System.MissingMethodException: Method not found: 'System.IServiceProvider Microsoft.Extensions.DependencyInjection.ServiceCollectionContainerBuilderExtensions.BuildServiceProvider(Microsoft.Extensions.DependencyInjection.IServiceCollection)'.
at RazorLight.MVC.MvcServiceCollectionExtensions.AddRazorLight(IServiceCollection services, String root, Action`1 config)
at air.Startup.ConfigureServices(IServiceCollection services)} System.MissingMethodException

Tried updating the package to for razorlight to rc3 and razorlight.mvc to 1.0.3
last try updated Microsoft.Extensions.DependencyInjection to latest version 1.2.preview1-24163

still same exception.
not sure if its a configuration issue I have or if it is something that is broken due to SDK updates.
Microsoft Visual Studio Enterprise 2017
Version 15.0.26228.10 D15RTWSVC
Microsoft .NET Framework
Version 4.6.01586
ASP.NET and Web Tools 2017 15.0.30223.0
ASP.NET Web Frameworks and Tools 2017 5.2.50127.0
.NET Core SDK 1.0.0

Base model always null

I have a base model:

public class EmailBaseDto
{
   public string Subject { get; set; }
   public string Title { get; set; }
}

and a layout view __LayoutEmail.cshtml where I want to use base model class:
@model EmailBaseDto

Then I have a derived model:

public class EmailForgotPasswordDto : EmailBaseDto
{
   public string CallbackUrl { get; set; }
}

and ForgotPassword.cshtml view which uses derived class:
@model EmailForgotPasswordDto

In the runtime @Model.CallbackUrl is as expected but @Model.Subject in layout view crashes because Model is null! For rendering I use this method: engine.Parse(viewName + ".cshtml", model);

What could be a problem here?

net462 TemplateCompilationException

Hi,

We're getting the TemplateCompilationException on net462.

I noticed that CompilationReferences in RoslynCompilerService doesn't contain the reference to Microsoft.CSharp.dll on net462 (the library isn't in list of dlls returned by System.AppDomain.CurrentDomain.GetAssemblies event if package explicitly referenced for all target frameworks).

I've referenced the package and explicitly added the dll in MetadataResolver and its worked, but there should be a better way to do this :)

metadataReferences.Add(MetadataReference.CreateFromFile(System.IO.Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, "Microsoft.CSharp.dll")));

Thank you

Special characters

Hello,
I'm trying to send data with accent and special characters, but it's returning me the data in an incorrect way.

See my example, the result should be:
Olá - Olá

But it is returning:
Olá - Ol&#xE1;

My code:

    public class TestModel
    {
        public string Name { get; set; }
    }
    public class Program
    {
        public static void Main(string[] args)
        {
            var model = new TestModel()
            {
                Name = "Olá"
            };

            var engine = EngineFactory.CreatePhysical(@"D:\");
            string result = engine.ParseString("Olá - @Model.Name", model, model.GetType());
            //=> "Olá? - Ol&#xE1;?"
        }
    }

Version: "RazorLight": "1.0.0"

Configuration - Adding Namespaces

Engine Configuration has a collection property called Namespaces. Is this where I can add namespaces that are used in the html instead of putting the using namespaces in the html? If so, what is the format. I tried adding namespaces and it still didn't work. This will be very useful for a lot templates I have that use the namespaces defined in the web.config instead explicitly putting them in the razor.

Thanks

Getting an exception when published to azure

When project is deployed to Azure, I get an exception below when trying to use RazorLight. In development enviroment everything works ok. Any ideas why this may happen?

System.InvalidOperationException: Can not find compilation library location for package 'microsoft.netcore.portable.compatibility'
   at Microsoft.Extensions.DependencyModel.CompilationLibrary.ResolveReferencePaths()
   at RazorLight.UseEntryAssemblyMetadataResolver.GetMetadataReferences()
   at System.Threading.LazyInitializer.EnsureInitializedCore[T](T& target, Boolean& initialized, Object& syncLock, Func`1 valueFactory)
   at RazorLight.Compilation.RoslynCompilerService.get_CompilationReferences()
   at RazorLight.Compilation.RoslynCompilerService.Compile(CompilationContext context)
   at RazorLight.EngineCore.CompileSource(ITemplateSource templateSource, ModelTypeInfo modelTypeInfo)
   at RazorLight.EngineCore.KeyCompile(String key)
   at RazorLight.Templating.DefaultPageFactory.CreateFactory(String key)
   at RazorLight.Templating.DefaultPageLookup.GetPage(String key)
   at RazorLight.RazorLightEngine.Parse(String key, Object model, Type modelType, ExpandoObject viewBag, Action`1 prerenderCallback)
   at RazorLight.RazorLightEngine.Parse[T](String key, T model)
   at CheapWatcher.Services.NotificationService.SendConfirmRegistrationEmail(ConfirmRegistration arguments) in C:\Projects\CheapWatcher\src\CheapWatcher.Services\NotificationService.cs:line 65
   at CheapWatcher.Web.Controllers.AccountController.<Register>d__7.MoveNext() in C:\Projects\CheapWatcher\src\CheapWatcher.Web\Controllers\AccountController.cs:line 98
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at Microsoft.AspNetCore.Mvc.Internal.ObjectMethodExecutor.<CastToObject>d__40`1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.<InvokeActionMethodAsync>d__27.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
   at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.<InvokeNextActionFilterAsync>d__25.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Rethrow(ActionExecutedContext context)
   at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.<InvokeNextResourceFilter>d__22.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Rethrow(ResourceExecutedContext context)
   at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.<InvokeAsync>d__20.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
   at Microsoft.AspNetCore.Builder.RouterMiddleware.<Invoke>d__4.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
   at Microsoft.AspNetCore.Session.SessionMiddleware.<Invoke>d__9.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at Microsoft.AspNetCore.Session.SessionMiddleware.<Invoke>d__9.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
   at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware`1.<Invoke>d__18.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware`1.<Invoke>d__18.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
   at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware`1.<Invoke>d__18.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware`1.<Invoke>d__18.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
   at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware`1.<Invoke>d__18.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware`1.<Invoke>d__18.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
   at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware`1.<Invoke>d__18.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware`1.<Invoke>d__18.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
   at Microsoft.AspNetCore.Cors.Infrastructure.CorsMiddleware.<Invoke>d__7.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
   at Microsoft.AspNetCore.Server.IISIntegration.IISMiddleware.<Invoke>d__8.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
   at Microsoft.AspNetCore.Hosting.Internal.RequestServicesContainerMiddleware.<Invoke>d__3.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd(Task task)
   at Microsoft.AspNetCore.Server.Kestrel.Internal.Http.Frame`1.<RequestProcessingAsync>d__2.MoveNext()

Error while using IncludeAsync from layout page

RazorLight.RazorLightException: Can't locate a page as PageLookup is not set

It looks like trouble is from PageRenderer

private TemplatePage GetLayoutPage(string layoutKey)

where you create from page factory, but doesn't set PageLookup property of created object

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.