Skip to content

React.Owin #69

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Jan 18, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions src/React.Owin/AssemblyRegistration.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright (c) 2014-2015, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/

using React.TinyIoC;

namespace React.Owin
{
/// <summary>
/// Handles registration of ReactJS.NET components that are only applicable
/// when used with Owin.
/// </summary>
public class AssemblyRegistration : IAssemblyRegistration
{
/// <summary>
/// Registers components in the React IoC container
/// </summary>
/// <param name="container">Container to register components in</param>
public void Register(TinyIoCContainer container)
{
container.Register<IFileSystem, EntryAssemblyFileSystem>();
container.Register<ICache, MemoryFileCache>();
}
}
}
28 changes: 28 additions & 0 deletions src/React.Owin/EntryAssemblyFileSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright (c) 2014-2015, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/

using System.IO;
using System.Reflection;

namespace React.Owin
{
/// <summary>
/// Implements React file system that maps "~" into entry assembly location.
/// </summary>
internal class EntryAssemblyFileSystem : FileSystemBase
{
public override string MapPath(string relativePath)
{
if (relativePath.StartsWith("~"))
return Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), relativePath.Replace("~", string.Empty));

return relativePath;
}
}
}
27 changes: 27 additions & 0 deletions src/React.Owin/JsxFileExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* Copyright (c) 2014-2015, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/

using Owin;

namespace React.Owin
{
/// <summary>
/// Extensions for JsxFileMiddleware.
/// </summary>
public static class JsxFileExtensions
{
/// <summary>
/// Enables serving static JSX file, compiled to JavaScript with the given options.
/// </summary>
public static IAppBuilder UseJsxFiles(this IAppBuilder builder, JsxFileOptions options = null)
{
return builder.Use<JsxFileMiddleware>(options ?? new JsxFileOptions());
}
}
}
86 changes: 86 additions & 0 deletions src/React.Owin/JsxFileMiddleware.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*
* Copyright (c) 2014-2015, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

using Microsoft.Owin.StaticFiles;

namespace React.Owin
{
/// <summary>
/// Enables serving static JSX files transformed to pure JavaScript. Wraps around StaticFileMiddleware.
/// </summary>
public class JsxFileMiddleware
{
private readonly Func<IDictionary<string, object>, Task> _next;
private readonly StaticFileOptions _fileOptions;
private readonly IEnumerable<string> _extensions;

static JsxFileMiddleware()
{
// Assume that request will ask for the "per request" instances only once.
Initializer.Initialize(options => options.AsMultiInstance());
}

/// <summary>
/// Creates a new instance of the JsxFileMiddleware.
/// </summary>
/// <param name="next">The next middleware in the pipeline.</param>
/// <param name="options">The configuration options.</param>
public JsxFileMiddleware(Func<IDictionary<string, object>, Task> next, JsxFileOptions options)
{
if (next == null)
throw new ArgumentNullException("next");

_next = next;

// Default values
options = options ?? new JsxFileOptions();
_extensions = (options.Extensions == null || !options.Extensions.Any()) ? new[] { ".jsx", ".js" } : options.Extensions;
_fileOptions = options.StaticFileOptions ?? new StaticFileOptions();
}

/// <summary>
/// Processes a request to determine if it matches a known JSX file, and if so, serves it compiled to JavaScript.
/// </summary>
/// <param name="environment">OWIN environment dictionary which stores state information about the request, response and relevant server state.</param>
/// <returns/>
public async Task Invoke(IDictionary<string, object> environment)
{
// Create all "per request" instances
var reactEnvironment = React.AssemblyRegistration.Container.Resolve<IReactEnvironment>();

var internalStaticMiddleware = CreateFileMiddleware(reactEnvironment.JsxTransformer);
await internalStaticMiddleware.Invoke(environment);

// Clean up all "per request" instances
var disposable = reactEnvironment as IDisposable;
if (disposable != null)
disposable.Dispose();
}

private StaticFileMiddleware CreateFileMiddleware(IJsxTransformer jsxTransformer)
{
return new StaticFileMiddleware(
_next,
new StaticFileOptions()
{
ContentTypeProvider = _fileOptions.ContentTypeProvider,
DefaultContentType = _fileOptions.DefaultContentType,
OnPrepareResponse = _fileOptions.OnPrepareResponse,
RequestPath = _fileOptions.RequestPath,
ServeUnknownFileTypes = _fileOptions.ServeUnknownFileTypes,
FileSystem = new JsxFileSystem(jsxTransformer, _fileOptions.FileSystem, _extensions)
});
}
}
}
31 changes: 31 additions & 0 deletions src/React.Owin/JsxFileOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Copyright (c) 2014-2015, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/

using System.Collections.Generic;

using Microsoft.Owin.StaticFiles;

namespace React.Owin
{
/// <summary>
/// Options for serving JSX files.
/// </summary>
public class JsxFileOptions
{
/// <summary>
/// Collection of extensions that will be treated as JSX files. Defaults to ".jsx" and ".js".
/// </summary>
public IEnumerable<string> Extensions { get; set; }

/// <summary>
/// Options for static file middleware used to server JSX files.
/// </summary>
public StaticFileOptions StaticFileOptions { get; set; }
}
}
140 changes: 140 additions & 0 deletions src/React.Owin/JsxFileSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
/*
* Copyright (c) 2014-2015, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;

using Microsoft.Owin.FileSystems;

namespace React.Owin
{
/// <summary>
/// Owin file system that serves transformed JSX files.
/// </summary>
public class JsxFileSystem : Microsoft.Owin.FileSystems.IFileSystem
{
private readonly IJsxTransformer _transformer;
private readonly Microsoft.Owin.FileSystems.IFileSystem _physicalFileSystem;
private readonly string[] _extensions;

/// <summary>
/// Creates a new instance of the JsxFileSystem.
/// </summary>
/// <param name="transformer">JSX transformer used to compile JSX files</param>
/// <param name="root">The root directory</param>
/// <param name="extensions">Extensions of files that will be treated as JSX files</param>
public JsxFileSystem(IJsxTransformer transformer, string root, IEnumerable<string> extensions)
: this(transformer, new PhysicalFileSystem(root), extensions)
{
}

/// <summary>
/// Creates a new instance of the JsxFileSystem.
/// </summary>
/// <param name="transformer">JSX transformer used to compile JSX files</param>
/// <param name="fileSystem">File system used to look up files</param>
/// <param name="extensions">Extensions of files that will be treated as JSX files</param>
public JsxFileSystem(IJsxTransformer transformer, Microsoft.Owin.FileSystems.IFileSystem fileSystem, IEnumerable<string> extensions)
{
_transformer = transformer;
_physicalFileSystem = fileSystem;

// Make sure the extensions start with dot
_extensions = extensions.Select(extension => extension.StartsWith(".") ? extension : "." + extension).ToArray();
}

/// <summary>
/// Locate a JSX file at the given path.
/// </summary>
/// <param name="subpath">The path that identifies the file</param>
/// <param name="fileInfo">The discovered file if any</param>
/// <returns>
/// True if a JSX file was located at the given path
/// </returns>
public bool TryGetFileInfo(string subpath, out IFileInfo fileInfo)
{
IFileInfo internalFileInfo;
fileInfo = null;

if (!_physicalFileSystem.TryGetFileInfo(subpath, out internalFileInfo))
return false;

if (internalFileInfo.IsDirectory || !_extensions.Any(internalFileInfo.Name.EndsWith))
return false;

fileInfo = new JsxFileInfo(_transformer, internalFileInfo);
return true;
}

/// <summary>
/// Enumerate a directory at the given path, if any
/// </summary>
/// <param name="subpath">The path that identifies the directory</param>
/// <param name="contents">The contents if any</param>
/// <returns>
/// True if a directory was located at the given path
/// </returns>
public bool TryGetDirectoryContents(string subpath, out IEnumerable<IFileInfo> contents)
{
return _physicalFileSystem.TryGetDirectoryContents(subpath, out contents);
}

private class JsxFileInfo : IFileInfo
{
private readonly IJsxTransformer _jsxTransformer;
private readonly IFileInfo _fileInfo;
private readonly Lazy<byte[]> _content;

public JsxFileInfo(IJsxTransformer jsxTransformer, IFileInfo fileInfo)
{
_jsxTransformer = jsxTransformer;
_fileInfo = fileInfo;

_content = new Lazy<byte[]>(
() =>
{
return Encoding.UTF8.GetBytes(_jsxTransformer.TransformJsxFile(fileInfo.PhysicalPath));
});
}

public Stream CreateReadStream()
{
return new MemoryStream(_content.Value);
}

public long Length
{
get { return _content.Value.Length; }
}

public string PhysicalPath
{
get { return _fileInfo.PhysicalPath; }
}

public string Name
{
get { return _fileInfo.Name; }
}

public DateTime LastModified
{
get { return _fileInfo.LastModified; }
}

public bool IsDirectory
{
get { return _fileInfo.IsDirectory; }
}
}
}
}
7 changes: 7 additions & 0 deletions src/React.Owin/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
using System.Reflection;
using System.Runtime.InteropServices;

[assembly: AssemblyTitle("React.Owin")]
[assembly: AssemblyDescription("Owin integration for ReactJS.NET")]
[assembly: ComVisible(false)]
[assembly: Guid("7f0500f5-5a9f-48c9-b136-41cfd1787724")]
Loading