Add XML docs to core packages

This commit is contained in:
SteveSandersonMS
2016-11-29 16:03:15 +00:00
parent 3b91ad9b39
commit 3ff4447924
24 changed files with 448 additions and 10 deletions

View File

@@ -9,14 +9,31 @@ using Newtonsoft.Json;
namespace Microsoft.AspNetCore.AngularServices
{
/// <summary>
/// Helpers for prepopulating Angular 2's 'http' service with data.
/// </summary>
public static class PrimeCacheHelper
{
/// <summary>
/// Performs an HTTP GET request to the specified URL and adds the resulting JSON data
/// to the Angular 'http' service cache.
/// </summary>
/// <param name="html">The <see cref="IHtmlHelper"/>.</param>
/// <param name="url">The URL to be requested.</param>
/// <returns>A task representing the HTML content to be rendered into the document.</returns>
[Obsolete("Use PrimeCacheAsync instead")]
public static Task<IHtmlContent> PrimeCache(this IHtmlHelper html, string url)
{
return PrimeCacheAsync(html, url);
}
/// <summary>
/// Performs an HTTP GET request to the specified URL and adds the resulting JSON data
/// to the Angular 'http' service cache.
/// </summary>
/// <param name="html">The <see cref="IHtmlHelper"/>.</param>
/// <param name="url">The URL to be requested.</param>
/// <returns>A task representing the HTML content to be rendered into the document.</returns>
public static async Task<IHtmlContent> PrimeCacheAsync(this IHtmlHelper html, string url)
{
// TODO: Consider deduplicating the PrimeCacheAsync calls (that is, if there are multiple requests to precache

View File

@@ -14,7 +14,8 @@
},
"buildOptions": {
"warningsAsErrors": true,
"keyFile": "../../tools/Key.snk"
"keyFile": "../../tools/Key.snk",
"xmlDoc": true
},
"dependencies": {
"Microsoft.AspNetCore.Mvc.TagHelpers": "1.0.1",

View File

@@ -1,8 +1,18 @@
namespace Microsoft.AspNetCore.NodeServices
{
/// <summary>
/// Represents a way of creating and invoking code in a Node.js environment.
/// </summary>
public enum NodeHostingModel
{
/// <summary>
/// An out-of-process Node.js instance where RPC calls are made via HTTP.
/// </summary>
Http,
/// <summary>
/// An out-of-process Node.js instance where RPC calls are made over binary sockets.
/// </summary>
Socket,
}
}

View File

@@ -3,8 +3,16 @@ using Microsoft.AspNetCore.NodeServices.HostingModels;
namespace Microsoft.AspNetCore.NodeServices
{
/// <summary>
/// Supplies INodeServices instances.
/// </summary>
public static class NodeServicesFactory
{
/// <summary>
/// Create an <see cref="INodeServices"/> instance according to the supplied options.
/// </summary>
/// <param name="options">Options for creating the <see cref="INodeServices"/> instance.</param>
/// <returns>An <see cref="INodeServices"/> instance.</returns>
public static INodeServices CreateNodeServices(NodeServicesOptions options)
{
if (options == null)

View File

@@ -8,14 +8,25 @@ using Microsoft.Extensions.Logging.Console;
namespace Microsoft.AspNetCore.NodeServices
{
/// <summary>
/// Describes options used to configure an <see cref="INodeServices"/> instance.
/// </summary>
public class NodeServicesOptions
{
/// <summary>
/// Defines the default <see cref="NodeHostingModel"/>.
/// </summary>
public const NodeHostingModel DefaultNodeHostingModel = NodeHostingModel.Http;
internal const string TimeoutConfigPropertyName = nameof(InvocationTimeoutMilliseconds);
private const int DefaultInvocationTimeoutMilliseconds = 60 * 1000;
private const string LogCategoryName = "Microsoft.AspNetCore.NodeServices";
private static readonly string[] DefaultWatchFileExtensions = { ".js", ".jsx", ".ts", ".tsx", ".json", ".html" };
/// <summary>
/// Creates a new instance of <see cref="NodeServicesOptions"/>.
/// </summary>
/// <param name="serviceProvider">The <see cref="IServiceProvider"/>.</param>
public NodeServicesOptions(IServiceProvider serviceProvider)
{
if (serviceProvider == null)
@@ -44,14 +55,49 @@ namespace Microsoft.AspNetCore.NodeServices
: new ConsoleLogger(LogCategoryName, null, false);
}
/// <summary>
/// Specifies which <see cref="NodeHostingModel"/> should be used.
/// </summary>
public NodeHostingModel HostingModel { get; set; }
/// <summary>
/// If set, this callback function will be invoked to supply the <see cref="INodeServices"/> instance.
/// </summary>
public Func<INodeInstance> NodeInstanceFactory { get; set; }
/// <summary>
/// If set, overrides the path to the root of your application. This path is used when locating Node.js modules relative to your project.
/// </summary>
public string ProjectPath { get; set; }
/// <summary>
/// If set, the Node.js instance should restart when any matching file on disk within your project changes.
/// </summary>
public string[] WatchFileExtensions { get; set; }
/// <summary>
/// The Node.js instance's stdout/stderr will be redirected to this <see cref="ILogger"/>.
/// </summary>
public ILogger NodeInstanceOutputLogger { get; set; }
/// <summary>
/// If true, the Node.js instance will accept incoming V8 debugger connections (e.g., from node-inspector).
/// </summary>
public bool LaunchWithDebugging { get; set; }
public IDictionary<string, string> EnvironmentVariables { get; set; }
/// <summary>
/// If <see cref="LaunchWithDebugging"/> is true, the Node.js instance will listen for V8 debugger connections on this port.
/// </summary>
public int DebuggingPort { get; set; }
/// <summary>
/// If set, starts the Node.js instance with the specified environment variables.
/// </summary>
public IDictionary<string, string> EnvironmentVariables { get; set; }
/// <summary>
/// Specifies the maximum duration, in milliseconds, that your .NET code should wait for Node.js RPC calls to return.
/// </summary>
public int InvocationTimeoutMilliseconds { get; set; }
}
}

View File

@@ -8,9 +8,18 @@ namespace Microsoft.Extensions.DependencyInjection
/// </summary>
public static class NodeServicesServiceCollectionExtensions
{
/// <summary>
/// Adds NodeServices support to the <paramref name="serviceCollection"/>.
/// </summary>
/// <param name="serviceCollection">The <see cref="IServiceCollection"/>.</param>
public static void AddNodeServices(this IServiceCollection serviceCollection)
=> AddNodeServices(serviceCollection, _ => {});
/// <summary>
/// Adds NodeServices support to the <paramref name="serviceCollection"/>.
/// </summary>
/// <param name="serviceCollection">The <see cref="IServiceCollection"/>.</param>
/// <param name="options">Options for configuring the <see cref="INodeServices"/> instances.</param>
[Obsolete("Use the AddNodeServices(Action<NodeServicesOptions> setupAction) overload instead.")]
public static void AddNodeServices(this IServiceCollection serviceCollection, NodeServicesOptions options)
{
@@ -20,6 +29,11 @@ namespace Microsoft.Extensions.DependencyInjection
});
}
/// <summary>
/// Adds NodeServices support to the <paramref name="serviceCollection"/>.
/// </summary>
/// <param name="serviceCollection">The <see cref="IServiceCollection"/>.</param>
/// <param name="setupAction">A callback that will be invoked to populate the <see cref="NodeServicesOptions"/>.</param>
public static void AddNodeServices(this IServiceCollection serviceCollection, Action<NodeServicesOptions> setupAction)
{
if (setupAction == null)

View File

@@ -4,8 +4,20 @@ using System.Threading.Tasks;
namespace Microsoft.AspNetCore.NodeServices.HostingModels
{
/// <summary>
/// Represents an instance of Node.js to which Remote Procedure Calls (RPC) may be sent.
/// </summary>
public interface INodeInstance : IDisposable
{
/// <summary>
/// Asynchronously invokes code in the Node.js instance.
/// </summary>
/// <typeparam name="T">The JSON-serializable data type that the Node.js code will asynchronously return.</typeparam>
/// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to cancel the invocation.</param>
/// <param name="moduleName">The path to the Node.js module (i.e., JavaScript file) relative to your project root that contains the code to be invoked.</param>
/// <param name="exportNameOrNull">If set, specifies the CommonJS export to be invoked. If not set, the module's default CommonJS export itself must be a function to be invoked.</param>
/// <param name="args">Any sequence of JSON-serializable arguments to be passed to the Node.js function.</param>
/// <returns>A <see cref="Task{TResult}"/> representing the completion of the RPC call.</returns>
Task<T> InvokeExportAsync<T>(CancellationToken cancellationToken, string moduleName, string exportNameOrNull, params object[] args);
}
}

View File

@@ -2,15 +2,33 @@ using System;
namespace Microsoft.AspNetCore.NodeServices.HostingModels
{
/// <summary>
/// Represents an exception caused by invoking Node.js code.
/// </summary>
public class NodeInvocationException : Exception
{
/// <summary>
/// If true, indicates that the invocation failed because the Node.js instance could not be reached. For example,
/// it might have already shut down or previously crashed.
/// </summary>
public bool NodeInstanceUnavailable { get; private set; }
/// <summary>
/// Creates a new instance of <see cref="NodeInvocationException"/>.
/// </summary>
/// <param name="message">A description of the exception.</param>
/// <param name="details">Additional information, such as a Node.js stack trace, representing the exception.</param>
public NodeInvocationException(string message, string details)
: base(message + Environment.NewLine + details)
{
}
/// <summary>
/// Creates a new instance of <see cref="NodeInvocationException"/>.
/// </summary>
/// <param name="message">A description of the exception.</param>
/// <param name="details">Additional information, such as a Node.js stack trace, representing the exception.</param>
/// <param name="nodeInstanceUnavailable">Specifies a value for the <see cref="NodeInstanceUnavailable"/> flag.</param>
public NodeInvocationException(string message, string details, bool nodeInstanceUnavailable)
: this(message, details)
{

View File

@@ -1,9 +1,24 @@
namespace Microsoft.AspNetCore.NodeServices.HostingModels
{
/// <summary>
/// Describes an RPC call sent from .NET code to Node.js code.
/// </summary>
public class NodeInvocationInfo
{
/// <summary>
/// Specifies the path to the Node.js module (i.e., .js file) relative to the project root.
/// </summary>
public string ModuleName { get; set; }
/// <summary>
/// If set, specifies the name of CommonJS function export to be invoked.
/// If not set, the Node.js module's default export must itself be a function to be invoked.
/// </summary>
public string ExportedFunctionName { get; set; }
/// <summary>
/// A sequence of JSON-serializable arguments to be passed to the Node.js function being invoked.
/// </summary>
public object[] Args { get; set; }
}
}

View File

@@ -21,7 +21,11 @@ namespace Microsoft.AspNetCore.NodeServices.HostingModels
/// <seealso cref="Microsoft.AspNetCore.NodeServices.HostingModels.INodeInstance" />
public abstract class OutOfProcessNodeInstance : INodeInstance
{
/// <summary>
/// The <see cref="ILogger"/> to which the Node.js instance's stdout/stderr is being redirected.
/// </summary>
protected readonly ILogger OutputLogger;
private const string ConnectionEstablishedMessage = "[Microsoft.AspNetCore.NodeServices:Listening]";
private const string DebuggingStartedMessageFormat = @"-----
*** Node.js debugging is enabled ***
@@ -43,6 +47,18 @@ If you haven't yet installed node-inspector, you can do so as follows:
private bool _nodeProcessNeedsRestart;
private readonly string[] _watchFileExtensions;
/// <summary>
/// Creates a new instance of <see cref="OutOfProcessNodeInstance"/>.
/// </summary>
/// <param name="entryPointScript">The path to the entry point script that the Node instance should load and execute.</param>
/// <param name="projectPath">The root path of the current project. This is used when resolving Node.js module paths relative to the project root.</param>
/// <param name="watchFileExtensions">The filename extensions that should be watched within the project root. The Node instance will automatically shut itself down if any matching file changes.</param>
/// <param name="commandLineArguments">Additional command-line arguments to be passed to the Node.js instance.</param>
/// <param name="nodeOutputLogger">The <see cref="ILogger"/> to which the Node.js instance's stdout/stderr (and other log information) should be written.</param>
/// <param name="environmentVars">Environment variables to be set on the Node.js process.</param>
/// <param name="invocationTimeoutMilliseconds">The maximum duration, in milliseconds, to wait for RPC calls to complete.</param>
/// <param name="launchWithDebugging">If true, passes a flag to the Node.js process telling it to accept V8 debugger connections.</param>
/// <param name="debuggingPort">If debugging is enabled, the Node.js process should listen for V8 debugger connections on this port.</param>
public OutOfProcessNodeInstance(
string entryPointScript,
string projectPath,
@@ -71,6 +87,15 @@ If you haven't yet installed node-inspector, you can do so as follows:
ConnectToInputOutputStreams();
}
/// <summary>
/// Asynchronously invokes code in the Node.js instance.
/// </summary>
/// <typeparam name="T">The JSON-serializable data type that the Node.js code will asynchronously return.</typeparam>
/// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to cancel the invocation.</param>
/// <param name="moduleName">The path to the Node.js module (i.e., JavaScript file) relative to your project root that contains the code to be invoked.</param>
/// <param name="exportNameOrNull">If set, specifies the CommonJS export to be invoked. If not set, the module's default CommonJS export itself must be a function to be invoked.</param>
/// <param name="args">Any sequence of JSON-serializable arguments to be passed to the Node.js function.</param>
/// <returns>A <see cref="Task{TResult}"/> representing the completion of the RPC call.</returns>
public async Task<T> InvokeExportAsync<T>(
CancellationToken cancellationToken, string moduleName, string exportNameOrNull, params object[] args)
{
@@ -154,21 +179,41 @@ If you haven't yet installed node-inspector, you can do so as follows:
}
}
/// <summary>
/// Disposes this instance.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Asynchronously invokes code in the Node.js instance.
/// </summary>
/// <typeparam name="T">The JSON-serializable data type that the Node.js code will asynchronously return.</typeparam>
/// <param name="invocationInfo">Specifies the Node.js function to be invoked and arguments to be passed to it.</param>
/// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to cancel the invocation.</param>
/// <returns>A <see cref="Task{TResult}"/> representing the completion of the RPC call.</returns>
protected abstract Task<T> InvokeExportAsync<T>(
NodeInvocationInfo invocationInfo,
CancellationToken cancellationToken);
// This method is virtual, as it provides a way to override the NODE_PATH or the path to node.exe
/// <summary>
/// Configures a <see cref="ProcessStartInfo"/> instance describing how to launch the Node.js process.
/// </summary>
/// <param name="entryPointFilename">The entrypoint JavaScript file that the Node.js process should execute.</param>
/// <param name="projectPath">The root path of the project. This is used when locating Node.js modules relative to the project root.</param>
/// <param name="commandLineArguments">Command-line arguments to be passed to the Node.js process.</param>
/// <param name="environmentVars">Environment variables to be set on the Node.js process.</param>
/// <param name="launchWithDebugging">If true, passes a flag to the Node.js process telling it to accept V8 debugger connections.</param>
/// <param name="debuggingPort">If debugging is enabled, the Node.js process should listen for V8 debugger connections on this port.</param>
/// <returns></returns>
protected virtual ProcessStartInfo PrepareNodeProcessStartInfo(
string entryPointFilename, string projectPath, string commandLineArguments,
IDictionary<string, string> environmentVars, bool launchWithDebugging, int debuggingPort)
{
// This method is virtual, as it provides a way to override the NODE_PATH or the path to node.exe
string debuggingArgs;
if (launchWithDebugging)
{
@@ -217,16 +262,28 @@ If you haven't yet installed node-inspector, you can do so as follows:
return startInfo;
}
/// <summary>
/// Virtual method invoked whenever the Node.js process emits a line to its stdout.
/// </summary>
/// <param name="outputData">The line emitted to the Node.js process's stdout.</param>
protected virtual void OnOutputDataReceived(string outputData)
{
OutputLogger.LogInformation(outputData);
}
/// <summary>
/// Virtual method invoked whenever the Node.js process emits a line to its stderr.
/// </summary>
/// <param name="errorData">The line emitted to the Node.js process's stderr.</param>
protected virtual void OnErrorDataReceived(string errorData)
{
OutputLogger.LogError(errorData);
}
/// <summary>
/// Disposes the instance.
/// </summary>
/// <param name="disposing">True if the object is disposing or false if it is finalizing.</param>
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
@@ -403,6 +460,9 @@ If you haven't yet installed node-inspector, you can do so as follows:
EnsureFileSystemWatcherIsDisposed();
}
/// <summary>
/// Implements the finalization part of the IDisposable pattern by calling Dispose(false).
/// </summary>
~OutOfProcessNodeInstance()
{
Dispose(false);

View File

@@ -7,6 +7,10 @@ using System.Threading.Tasks;
namespace Microsoft.AspNetCore.NodeServices.HostingModels.VirtualConnections
{
/// <summary>
/// A callback that will be invoked if the <see cref="VirtualConnectionClient"/> encounters a read error.
/// </summary>
/// <param name="ex"></param>
public delegate void VirtualConnectionReadErrorHandler(Exception ex);
/// <summary>

View File

@@ -4,18 +4,71 @@ using System.Threading.Tasks;
namespace Microsoft.AspNetCore.NodeServices
{
/// <summary>
/// Represents the ability to invoke code in a Node.js environment. Although the underlying Node.js instance
/// might change over time (e.g., the process might be restarted), the <see cref="INodeServices"/> instance
/// will remain constant.
/// </summary>
public interface INodeServices : IDisposable
{
/// <summary>
/// Asynchronously invokes code in the Node.js instance.
/// </summary>
/// <typeparam name="T">The JSON-serializable data type that the Node.js code will asynchronously return.</typeparam>
/// <param name="moduleName">The path to the Node.js module (i.e., JavaScript file) relative to your project root whose default CommonJS export is the function to be invoked.</param>
/// <param name="args">Any sequence of JSON-serializable arguments to be passed to the Node.js function.</param>
/// <returns>A <see cref="Task{TResult}"/> representing the completion of the RPC call.</returns>
Task<T> InvokeAsync<T>(string moduleName, params object[] args);
/// <summary>
/// Asynchronously invokes code in the Node.js instance.
/// </summary>
/// <typeparam name="T">The JSON-serializable data type that the Node.js code will asynchronously return.</typeparam>
/// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to cancel the invocation.</param>
/// <param name="moduleName">The path to the Node.js module (i.e., JavaScript file) relative to your project root whose default CommonJS export is the function to be invoked.</param>
/// <param name="args">Any sequence of JSON-serializable arguments to be passed to the Node.js function.</param>
/// <returns>A <see cref="Task{TResult}"/> representing the completion of the RPC call.</returns>
Task<T> InvokeAsync<T>(CancellationToken cancellationToken, string moduleName, params object[] args);
/// <summary>
/// Asynchronously invokes code in the Node.js instance.
/// </summary>
/// <typeparam name="T">The JSON-serializable data type that the Node.js code will asynchronously return.</typeparam>
/// <param name="moduleName">The path to the Node.js module (i.e., JavaScript file) relative to your project root that contains the code to be invoked.</param>
/// <param name="exportedFunctionName">Specifies the CommonJS export to be invoked.</param>
/// <param name="args">Any sequence of JSON-serializable arguments to be passed to the Node.js function.</param>
/// <returns>A <see cref="Task{TResult}"/> representing the completion of the RPC call.</returns>
Task<T> InvokeExportAsync<T>(string moduleName, string exportedFunctionName, params object[] args);
/// <summary>
/// Asynchronously invokes code in the Node.js instance.
/// </summary>
/// <typeparam name="T">The JSON-serializable data type that the Node.js code will asynchronously return.</typeparam>
/// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to cancel the invocation.</param>
/// <param name="moduleName">The path to the Node.js module (i.e., JavaScript file) relative to your project root that contains the code to be invoked.</param>
/// <param name="exportedFunctionName">Specifies the CommonJS export to be invoked.</param>
/// <param name="args">Any sequence of JSON-serializable arguments to be passed to the Node.js function.</param>
/// <returns>A <see cref="Task{TResult}"/> representing the completion of the RPC call.</returns>
Task<T> InvokeExportAsync<T>(CancellationToken cancellationToken, string moduleName, string exportedFunctionName, params object[] args);
/// <summary>
/// Asynchronously invokes code in the Node.js instance.
/// </summary>
/// <typeparam name="T">The JSON-serializable data type that the Node.js code will asynchronously return.</typeparam>
/// <param name="moduleName">The path to the Node.js module (i.e., JavaScript file) relative to your project root whose default CommonJS export is the function to be invoked.</param>
/// <param name="args">Any sequence of JSON-serializable arguments to be passed to the Node.js function.</param>
/// <returns>A <see cref="Task{TResult}"/> representing the completion of the RPC call.</returns>
[Obsolete("Use InvokeAsync instead")]
Task<T> Invoke<T>(string moduleName, params object[] args);
/// <summary>
/// Asynchronously invokes code in the Node.js instance.
/// </summary>
/// <typeparam name="T">The JSON-serializable data type that the Node.js code will asynchronously return.</typeparam>
/// <param name="moduleName">The path to the Node.js module (i.e., JavaScript file) relative to your project root that contains the code to be invoked.</param>
/// <param name="exportedFunctionName">Specifies the CommonJS export to be invoked.</param>
/// <param name="args">Any sequence of JSON-serializable arguments to be passed to the Node.js function.</param>
/// <returns>A <see cref="Task{TResult}"/> representing the completion of the RPC call.</returns>
[Obsolete("Use InvokeExportAsync instead")]
Task<T> InvokeExport<T>(string moduleName, string exportedFunctionName, params object[] args);
}

View File

@@ -4,8 +4,17 @@ using System.Reflection;
namespace Microsoft.AspNetCore.NodeServices
{
/// <summary>
/// Contains methods for reading embedded resources.
/// </summary>
public static class EmbeddedResourceReader
{
/// <summary>
/// Reads the specified embedded resource from a given assembly.
/// </summary>
/// <param name="assemblyContainingType">Any <see cref="Type"/> in the assembly whose resource is to be read.</param>
/// <param name="path">The path of the resource to be read.</param>
/// <returns>The contents of the resource.</returns>
public static string Read(Type assemblyContainingType, string path)
{
var asm = assemblyContainingType.GetTypeInfo().Assembly;

View File

@@ -3,19 +3,31 @@ using System.IO;
namespace Microsoft.AspNetCore.NodeServices
{
// Makes it easier to pass script files to Node in a way that's sure to clean up after the process exits
/// <summary>
/// Makes it easier to pass script files to Node in a way that's sure to clean up after the process exits.
/// </summary>
public sealed class StringAsTempFile : IDisposable
{
private bool _disposedValue;
/// <summary>
/// Create a new instance of <see cref="StringAsTempFile"/>.
/// </summary>
/// <param name="content">The contents of the temporary file to be created.</param>
public StringAsTempFile(string content)
{
FileName = Path.GetTempFileName();
File.WriteAllText(FileName, content);
}
/// <summary>
/// Specifies the filename of the temporary file.
/// </summary>
public string FileName { get; }
/// <summary>
/// Disposes the instance and deletes the associated temporary file.
/// </summary>
public void Dispose()
{
DisposeImpl(true);
@@ -37,6 +49,9 @@ namespace Microsoft.AspNetCore.NodeServices
}
}
/// <summary>
/// Implements the finalization part of the IDisposable pattern by calling Dispose(false).
/// </summary>
~StringAsTempFile()
{
DisposeImpl(false);

View File

@@ -17,7 +17,8 @@
"keyFile": "../../tools/Key.snk",
"embed": [
"Content/**/*"
]
],
"xmlDoc": true
},
"dependencies": {
"Microsoft.AspNetCore.Hosting.Abstractions": "1.0.0",

View File

@@ -14,7 +14,8 @@
},
"buildOptions": {
"warningsAsErrors": true,
"keyFile": "../../tools/Key.snk"
"keyFile": "../../tools/Key.snk",
"xmlDoc": true
},
"dependencies": {
"Microsoft.AspNetCore.Mvc.TagHelpers": "1.0.1",

View File

@@ -1,14 +1,36 @@
using System;
namespace Microsoft.AspNetCore.SpaServices.Prerendering
{
/// <summary>
/// Describes how to find the JavaScript code that performs prerendering.
/// </summary>
public class JavaScriptModuleExport
{
/// <summary>
/// Creates a new instance of <see cref="JavaScriptModuleExport"/>.
/// </summary>
/// <param name="moduleName">The path to the JavaScript module containing prerendering code.</param>
public JavaScriptModuleExport(string moduleName)
{
ModuleName = moduleName;
}
/// <summary>
/// Specifies the path to the JavaScript module containing prerendering code.
/// </summary>
public string ModuleName { get; private set; }
/// <summary>
/// If set, specifies the name of the CommonJS export that is the prerendering function to execute.
/// If not set, the JavaScript module's default CommonJS export must itself be the prerendering function.
/// </summary>
public string ExportName { get; set; }
/// <summary>
/// Obsolete. Do not use. Instead, configure Webpack to build a Node.js-compatible bundle and reference that directly.
/// </summary>
[Obsolete("Do not use. This feature will be removed. Instead, configure Webpack to build a Node.js-compatible bundle and reference that directly.")]
public string WebpackConfig { get; set; }
}
}

View File

@@ -11,6 +11,9 @@ using Newtonsoft.Json;
namespace Microsoft.AspNetCore.SpaServices.Prerendering
{
/// <summary>
/// A tag helper for prerendering JavaScript applications on the server.
/// </summary>
[HtmlTargetElement(Attributes = PrerenderModuleAttributeName)]
public class PrerenderTagHelper : TagHelper
{
@@ -24,6 +27,10 @@ namespace Microsoft.AspNetCore.SpaServices.Prerendering
private readonly string _applicationBasePath;
private readonly INodeServices _nodeServices;
/// <summary>
/// Creates a new instance of <see cref="PrerenderTagHelper"/>.
/// </summary>
/// <param name="serviceProvider">The <see cref="IServiceProvider"/>.</param>
public PrerenderTagHelper(IServiceProvider serviceProvider)
{
var hostEnv = (IHostingEnvironment) serviceProvider.GetService(typeof(IHostingEnvironment));
@@ -39,25 +46,51 @@ namespace Microsoft.AspNetCore.SpaServices.Prerendering
}
}
/// <summary>
/// Specifies the path to the JavaScript module containing prerendering code.
/// </summary>
[HtmlAttributeName(PrerenderModuleAttributeName)]
public string ModuleName { get; set; }
/// <summary>
/// If set, specifies the name of the CommonJS export that is the prerendering function to execute.
/// If not set, the JavaScript module's default CommonJS export must itself be the prerendering function.
/// </summary>
[HtmlAttributeName(PrerenderExportAttributeName)]
public string ExportName { get; set; }
/// <summary>
/// Obsolete. Do not use. Instead, configure Webpack to build a Node.js-compatible bundle and reference that directly.
/// </summary>
[Obsolete("Do not use. This feature will be removed. Instead, configure Webpack to build a Node.js-compatible bundle and reference that directly.")]
[HtmlAttributeName(PrerenderWebpackConfigAttributeName)]
public string WebpackConfigPath { get; set; }
/// <summary>
/// An optional JSON-serializable parameter to be supplied to the prerendering code.
/// </summary>
[HtmlAttributeName(PrerenderDataAttributeName)]
public object CustomDataParameter { get; set; }
/// <summary>
/// The maximum duration to wait for prerendering to complete.
/// </summary>
[HtmlAttributeName(PrerenderTimeoutAttributeName)]
public int TimeoutMillisecondsParameter { get; set; }
/// <summary>
/// The <see cref="ViewContext"/>.
/// </summary>
[HtmlAttributeNotBound]
[ViewContext]
public ViewContext ViewContext { get; set; }
/// <summary>
/// Executes the tag helper to perform server-side prerendering.
/// </summary>
/// <param name="context">The <see cref="TagHelperContext"/>.</param>
/// <param name="output">The <see cref="TagHelperOutput"/>.</param>
/// <returns>A <see cref="Task"/> representing the operation.</returns>
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
// We want to pass the original, unencoded incoming URL data through to Node, so that
@@ -79,7 +112,9 @@ namespace Microsoft.AspNetCore.SpaServices.Prerendering
new JavaScriptModuleExport(ModuleName)
{
ExportName = ExportName,
#pragma warning disable CS0618 // Type or member is obsolete
WebpackConfig = WebpackConfigPath
#pragma warning restore CS0618 // Type or member is obsolete
},
unencodedAbsoluteUrl,
unencodedPathAndQuery,

View File

@@ -4,6 +4,9 @@ using Microsoft.AspNetCore.NodeServices;
namespace Microsoft.AspNetCore.SpaServices.Prerendering
{
/// <summary>
/// Performs server-side prerendering by invoking code in Node.js.
/// </summary>
public static class Prerenderer
{
private static readonly Lazy<StringAsTempFile> NodeScript;
@@ -17,6 +20,17 @@ namespace Microsoft.AspNetCore.SpaServices.Prerendering
});
}
/// <summary>
/// Performs server-side prerendering by invoking code in Node.js.
/// </summary>
/// <param name="applicationBasePath">The root path to your application. This is used when resolving project-relative paths.</param>
/// <param name="nodeServices">The instance of <see cref="INodeServices"/> that will be used to invoke JavaScript code.</param>
/// <param name="bootModule">The path to the JavaScript file containing the prerendering logic.</param>
/// <param name="requestAbsoluteUrl">The URL of the currently-executing HTTP request. This is supplied to the prerendering code.</param>
/// <param name="requestPathAndQuery">The path and query part of the URL of the currently-executing HTTP request. This is supplied to the prerendering code.</param>
/// <param name="customDataParameter">An optional JSON-serializable parameter to be supplied to the prerendering code.</param>
/// <param name="timeoutMilliseconds">The maximum duration to wait for prerendering to complete.</param>
/// <returns></returns>
public static Task<RenderToStringResult> RenderToString(
string applicationBasePath,
INodeServices nodeServices,

View File

@@ -2,10 +2,28 @@ using Newtonsoft.Json.Linq;
namespace Microsoft.AspNetCore.SpaServices.Prerendering
{
/// <summary>
/// Describes the prerendering result returned by JavaScript code.
/// </summary>
public class RenderToStringResult
{
/// <summary>
/// If set, specifies JSON-serializable data that should be added as a set of global JavaScript variables in the document.
/// This can be used to transfer arbitrary data from server-side prerendering code to client-side code (for example, to
/// transfer the state of a Redux store).
/// </summary>
public JObject Globals { get; set; }
/// <summary>
/// The HTML generated by the prerendering logic.
/// </summary>
public string Html { get; set; }
/// <summary>
/// If set, specifies that instead of rendering HTML, the response should be an HTTP redirection to this URL.
/// This can be used if the prerendering code determines that the requested URL would lead to a redirection according
/// to the SPA's routing configuration.
/// </summary>
public string RedirectUrl { get; set; }
}
}

View File

@@ -7,10 +7,22 @@ using Microsoft.AspNetCore.SpaServices;
namespace Microsoft.AspNetCore.Builder
{
/// <summary>
/// Extension methods useful for configuring routing in a single-page application (SPA).
/// </summary>
public static class SpaRouteExtensions
{
private const string ClientRouteTokenName = "clientRoute";
/// <summary>
/// Configures a route that is automatically bypassed if the requested URL appears to be for a static file
/// (e.g., if it has a filename extension).
/// </summary>
/// <param name="routeBuilder">The <see cref="IRouteBuilder"/>.</param>
/// <param name="name">The route name.</param>
/// <param name="defaults">Default route parameters.</param>
/// <param name="constraints">Route constraints.</param>
/// <param name="dataTokens">Route data tokens.</param>
public static void MapSpaFallbackRoute(
this IRouteBuilder routeBuilder,
string name,
@@ -27,6 +39,16 @@ namespace Microsoft.AspNetCore.Builder
dataTokens);
}
/// <summary>
/// Configures a route that is automatically bypassed if the requested URL appears to be for a static file
/// (e.g., if it has a filename extension).
/// </summary>
/// <param name="routeBuilder">The <see cref="IRouteBuilder"/>.</param>
/// <param name="name">The route name.</param>
/// <param name="templatePrefix">The template prefix.</param>
/// <param name="defaults">Default route parameters.</param>
/// <param name="constraints">Route constraints.</param>
/// <param name="dataTokens">Route data tokens.</param>
public static void MapSpaFallbackRoute(
this IRouteBuilder routeBuilder,
string name,

View File

@@ -8,16 +8,29 @@ using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.PlatformAbstractions;
using Newtonsoft.Json;
// Putting in this namespace so it's always available whenever MapRoute is
namespace Microsoft.AspNetCore.Builder
{
/// <summary>
/// Extension methods that can be used to enable Webpack dev middleware support.
/// </summary>
public static class WebpackDevMiddleware
{
private const string WebpackDevMiddlewareScheme = "http";
private const string WebpackHotMiddlewareEndpoint = "/__webpack_hmr";
private const string DefaultConfigFile = "webpack.config.js";
/// <summary>
/// Enables Webpack dev middleware support. This hosts an instance of the Webpack compiler in memory
/// in your application so that you can always serve up-to-date Webpack-built resources without having
/// to run the compiler manually. Since the Webpack compiler instance is retained in memory, incremental
/// compilation is vastly faster that re-running the compiler from scratch.
///
/// Incoming requests that match Webpack-built files will be handled by returning the Webpack compiler
/// output directly, regardless of files on disk. If compilation is in progress when the request arrives,
/// the response will pause until updated compiler output is ready.
/// </summary>
/// <param name="appBuilder">The <see cref="IApplicationBuilder"/>.</param>
/// <param name="options">Options for configuring the Webpack compiler instance.</param>
public static void UseWebpackDevMiddleware(
this IApplicationBuilder appBuilder,
WebpackDevMiddlewareOptions options = null)

View File

@@ -2,13 +2,42 @@ using System.Collections.Generic;
namespace Microsoft.AspNetCore.SpaServices.Webpack
{
/// <summary>
/// Options for configuring a Webpack dev middleware compiler.
/// </summary>
public class WebpackDevMiddlewareOptions
{
/// <summary>
/// If true, hot module replacement (HMR) will be enabled. This automatically updates Webpack-built
/// resources (such as JavaScript, CSS, or images) in your web browser whenever source files are changed.
/// </summary>
public bool HotModuleReplacement { get; set; }
/// <summary>
/// Overrides the internal port number that client-side HMR code will connect to.
/// </summary>
public int HotModuleReplacementServerPort { get; set; }
/// <summary>
/// If true, enables React-specific extensions to Webpack's hot module replacement (HMR) feature.
/// This enables React components to be updated without losing their in-memory state.
/// </summary>
public bool ReactHotModuleReplacement { get; set; }
/// <summary>
/// Specifies the Webpack configuration file to be used. If not set, defaults to 'webpack.config.js'.
/// </summary>
public string ConfigFile { get; set; }
/// <summary>
/// The root path of your project. Webpack runs in this context.
/// </summary>
public string ProjectPath { get; set; }
/// <summary>
/// Specifies additional environment variables to be passed to the Node instance hosting
/// the webpack compiler.
/// </summary>
public IDictionary<string, string> EnvironmentVariables { get; set; }
}
}

View File

@@ -17,7 +17,8 @@
"keyFile": "../../tools/Key.snk",
"embed": [
"Content/**/*"
]
],
"xmlDoc": true
},
"dependencies": {
"Microsoft.AspNetCore.Mvc": "1.0.1",