Skip to content
This repository was archived by the owner on Apr 8, 2020. It is now read-only.

Update dependencies #204

Closed
wants to merge 28 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
00b5981
Minor style tweaks
SteveSandersonMS Jul 6, 2016
7ce5f8d
Remove trailing whitespace in KO template
SteveSandersonMS Jul 6, 2016
4ee09cb
Make Http hosting model able to report exceptions that happened while…
SteveSandersonMS Jul 6, 2016
4fb3b18
Create new top-level DefaultNodeInstance concept that will soon hold …
SteveSandersonMS Jul 6, 2016
a19e37f
Move logic for restarting Node child process into NodeServicesImpl. T…
SteveSandersonMS Jul 7, 2016
26e8bd8
Instead of the Node process exiting instantly on file change, send a …
SteveSandersonMS Jul 7, 2016
be13f0b
Centralise the child-process-terminating logic in NodeServicesImpl - …
SteveSandersonMS Jul 7, 2016
ce127f0
Implement connection draining feature
SteveSandersonMS Jul 7, 2016
eec370e
Move file-watching logic into .NET to avoid Node's fs.watch issues on…
SteveSandersonMS Jul 7, 2016
4b38519
Change all links in docs to point to new main branch ('dev')
SteveSandersonMS Jul 7, 2016
920f1c8
Replace references to Invoke and InvokeExport with InvokeAsync and In…
SteveSandersonMS Jul 7, 2016
3bc35ae
Simplify docs around receiving an INodeServices instance from DI
SteveSandersonMS Jul 7, 2016
b0bc80b
Update docs around custom node instances to match latest API changes
SteveSandersonMS Jul 7, 2016
8b51368
Update remaining doc references to Invoke<T> and InvokeExport<T>
SteveSandersonMS Jul 7, 2016
01d5c90
Include Microsoft.DotNet.Watcher.Tools in templates. Fixes #157
SteveSandersonMS Jul 7, 2016
c1a1bdf
Update React template homepage as per #158
SteveSandersonMS Jul 7, 2016
fc89747
Update domain-task package to version 2.0.1 (major bump because break…
SteveSandersonMS Jul 11, 2016
58bf117
Update templates to domain-task 2.0.0. Fixes #166.
SteveSandersonMS Jul 11, 2016
057efb4
aspnet-webpack module now preserves 'path' and 'publicPath' config se…
SteveSandersonMS Jul 18, 2016
7119815
Added OnBeforeStartExternalProcess callback which to NodeServicesOpti…
thunder7553 Jul 13, 2016
a14d9ba
Change onBeforeStartExternalProcess to a virtual method, so as to avo…
SteveSandersonMS Jul 18, 2016
27ffa72
Adding support for capturing the output of a node instance for custom…
pauldotknopf Jul 15, 2016
f4efcac
Switch to native .NET logging APIs
SteveSandersonMS Jul 18, 2016
fae0a88
Transfer multiline log messages from Node to .NET without treating ea…
SteveSandersonMS Jul 18, 2016
f4afb25
Set ts-loader to "silent" mode until there's a fix for https://github…
SteveSandersonMS Jul 18, 2016
edf1f88
Updating to AutoMapper 5.0
jbogard Jul 18, 2016
749c7cb
Add example of full-page prerendering via a custom action result
SteveSandersonMS Jul 19, 2016
6b52b3f
chore(package): Update to latest ng RC & universal
MarkPieszak Jul 21, 2016
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
20 changes: 10 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ This project is part of ASP.NET Core. You can find samples, documentation and ge
This repo contains:

* A set of NuGet/NPM packages that implement functionality for:
* Invoking arbitrary NPM packages at runtime from .NET code ([docs](https://github.com/aspnet/JavaScriptServices/tree/master/src/Microsoft.AspNetCore.NodeServices#simple-usage-example))
* Server-side prerendering of SPA components ([docs](https://github.com/aspnet/JavaScriptServices/tree/master/src/Microsoft.AspNetCore.SpaServices#server-side-prerendering))
* Webpack dev middleware ([docs](https://github.com/aspnet/JavaScriptServices/tree/master/src/Microsoft.AspNetCore.SpaServices#webpack-dev-middleware))
* Hot module replacement (HMR) ([docs](https://github.com/aspnet/JavaScriptServices/tree/master/src/Microsoft.AspNetCore.SpaServices#webpack-hot-module-replacement))
* Server-side and client-side routing integration ([docs](https://github.com/aspnet/JavaScriptServices/tree/master/src/Microsoft.AspNetCore.SpaServices#routing-helper-mapspafallbackroute))
* Invoking arbitrary NPM packages at runtime from .NET code ([docs](https://github.com/aspnet/JavaScriptServices/tree/dev/src/Microsoft.AspNetCore.NodeServices#simple-usage-example))
* Server-side prerendering of SPA components ([docs](https://github.com/aspnet/JavaScriptServices/tree/dev/src/Microsoft.AspNetCore.SpaServices#server-side-prerendering))
* Webpack dev middleware ([docs](https://github.com/aspnet/JavaScriptServices/tree/dev/src/Microsoft.AspNetCore.SpaServices#webpack-dev-middleware))
* Hot module replacement (HMR) ([docs](https://github.com/aspnet/JavaScriptServices/tree/dev/src/Microsoft.AspNetCore.SpaServices#webpack-hot-module-replacement))
* Server-side and client-side routing integration ([docs](https://github.com/aspnet/JavaScriptServices/tree/dev/src/Microsoft.AspNetCore.SpaServices#routing-helper-mapspafallbackroute))
* Server-side and client-side validation integration
* "Cache priming" for Angular 2 apps
* "Lazy loading" for Knockout apps
Expand All @@ -36,23 +36,23 @@ If you have an existing ASP.NET Core application, or if you just want to use the
* `Microsoft.AspNetCore.NodeServices`
* This provides a fast and robust way for .NET code to run JavaScript on the server inside a Node.js environment. You can use this to consume arbitrary functionality from NPM packages at runtime in your ASP.NET Core app.
* Most applications developers don't need to use this directly, but you can do so if you want to implement your own functionality that involves calling Node.js code from .NET at runtime.
* Find [documentation and usage examples here](https://github.com/aspnet/JavaScriptServices/tree/master/src/Microsoft.AspNetCore.NodeServices#microsoftaspnetcorenodeservices).
* Find [documentation and usage examples here](https://github.com/aspnet/JavaScriptServices/tree/dev/src/Microsoft.AspNetCore.NodeServices#microsoftaspnetcorenodeservices).
* `Microsoft.AspNetCore.SpaServices`
* This provides infrastructure that's generally useful when building Single Page Applications (SPAs) with technologies such as Angular 2 or React (for example, server-side prerendering and webpack middleware). Internally, it uses the `NodeServices` package to implement its features.
* Find [documentation and usage examples here](https://github.com/aspnet/JavaScriptServices/tree/master/src/Microsoft.AspNetCore.SpaServices#microsoftaspnetcorespaservices).
* Find [documentation and usage examples here](https://github.com/aspnet/JavaScriptServices/tree/dev/src/Microsoft.AspNetCore.SpaServices#microsoftaspnetcorespaservices).
* `Microsoft.AspNetCore.AngularServices`
* This builds on the `SpaServices` package and includes features specific to Angular 2. Currently, this includes validation helpers and a "cache priming" feature, which let you pre-evaluate ajax requests on the server so that client-side code doesn't need to make network calls once it's loaded.
* The code is [here](https://github.com/aspnet/JavaScriptServices/tree/master/src/Microsoft.AspNetCore.AngularServices), and you'll find a usage example for [the validation helper here](https://github.com/aspnet/JavaScriptServices/blob/master/samples/angular/MusicStore/wwwroot/ng-app/components/admin/album-edit/album-edit.ts), and for the [cache priming here](https://github.com/aspnet/JavaScriptServices/blob/master/samples/angular/MusicStore/Views/Home/Index.cshtml#L7-8). Full docs are to be written.
* The code is [here](https://github.com/aspnet/JavaScriptServices/tree/dev/src/Microsoft.AspNetCore.AngularServices), and you'll find a usage example for [the validation helper here](https://github.com/aspnet/JavaScriptServices/blob/dev/samples/angular/MusicStore/wwwroot/ng-app/components/admin/album-edit/album-edit.ts), and for the [cache priming here](https://github.com/aspnet/JavaScriptServices/blob/dev/samples/angular/MusicStore/Views/Home/Index.cshtml#L7-8). Full docs are to be written.

There was previously a `Microsoft.AspNetCore.ReactServices` but this is not currently needed - all applicable functionality is in `Microsoft.AspNetCore.SpaServices`, because it's sufficiently general. We might add a new `Microsoft.AspNetCore.ReactServices` package in the future if new React-specific requirements emerge.

If you want to build a helper library for some other SPA framework, you can do so by taking a dependency on `Microsoft.AspNetCore.SpaServices` and wrapping its functionality in whatever way is most useful for your SPA framework.

## Samples and templates

Inside this repo, [the `templates` directory](https://github.com/aspnet/JavaScriptServices/tree/master/templates) contains the application starting points that the `aspnetcore-spa` generator emits. If you want, you can clone this repo and run those applications directly. But it's easier to [use the Yeoman tool to run the generator](http://blog.stevensanderson.com/2016/05/02/angular2-react-knockout-apps-on-aspnet-core/).
Inside this repo, [the `templates` directory](https://github.com/aspnet/JavaScriptServices/tree/dev/templates) contains the application starting points that the `aspnetcore-spa` generator emits. If you want, you can clone this repo and run those applications directly. But it's easier to [use the Yeoman tool to run the generator](http://blog.stevensanderson.com/2016/05/02/angular2-react-knockout-apps-on-aspnet-core/).

Also in this repo, [the `samples` directory](https://github.com/aspnet/JavaScriptServices/tree/master/samples) contains examples of using the JavaScript services family of packages with Angular 2 and React, plus examples of standalone `NodeServices` usage for runtime code transpilation and image processing.
Also in this repo, [the `samples` directory](https://github.com/aspnet/JavaScriptServices/tree/dev/samples) contains examples of using the JavaScript services family of packages with Angular 2 and React, plus examples of standalone `NodeServices` usage for runtime code transpilation and image processing.

**To run the samples:**

Expand Down
19 changes: 11 additions & 8 deletions samples/angular/MusicStore/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,17 @@ public void ConfigureServices(IServiceCollection services)
options.AddPolicy("app-ManageStore", new AuthorizationPolicyBuilder().RequireClaim("app-ManageStore", "Allowed").Build());
});

Mapper.CreateMap<AlbumChangeDto, Album>();
Mapper.CreateMap<Album, AlbumChangeDto>();
Mapper.CreateMap<Album, AlbumResultDto>();
Mapper.CreateMap<AlbumResultDto, Album>();
Mapper.CreateMap<Artist, ArtistResultDto>();
Mapper.CreateMap<ArtistResultDto, Artist>();
Mapper.CreateMap<Genre, GenreResultDto>();
Mapper.CreateMap<GenreResultDto, Genre>();
Mapper.Initialize(cfg =>
{
cfg.CreateMap<AlbumChangeDto, Album>();
cfg.CreateMap<Album, AlbumChangeDto>();
cfg.CreateMap<Album, AlbumResultDto>();
cfg.CreateMap<AlbumResultDto, Album>();
cfg.CreateMap<Artist, ArtistResultDto>();
cfg.CreateMap<ArtistResultDto, Artist>();
cfg.CreateMap<Genre, GenreResultDto>();
cfg.CreateMap<GenreResultDto, Genre>();
});
}

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
Expand Down
2 changes: 1 addition & 1 deletion samples/angular/MusicStore/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"Microsoft.Extensions.Logging.Debug": "1.0.0",
"Microsoft.EntityFrameworkCore.SQLite": "1.0.0",
"Microsoft.AspNetCore.AngularServices": "1.0.0-*",
"AutoMapper": "4.1.1"
"AutoMapper": "5.0.2"
},
"frameworks": {
"netcoreapp1.0": {
Expand Down
6 changes: 3 additions & 3 deletions samples/misc/LatencyTest/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,21 @@ namespace ConsoleApplication
public class Program
{
public static void Main(string[] args) {
using (var nodeServices = CreateNodeServices(Configuration.DefaultNodeHostingModel)) {
using (var nodeServices = CreateNodeServices(NodeServicesOptions.DefaultNodeHostingModel)) {
MeasureLatency(nodeServices).Wait();
}
}

private static async Task MeasureLatency(INodeServices nodeServices) {
// Ensure the connection is open, so we can measure per-request timings below
var response = await nodeServices.Invoke<string>("latencyTest", "C#");
var response = await nodeServices.InvokeAsync<string>("latencyTest", "C#");
Console.WriteLine(response);

// Now perform a series of requests, capturing the time taken
const int requestCount = 100;
var watch = Stopwatch.StartNew();
for (var i = 0; i < requestCount; i++) {
await nodeServices.Invoke<string>("latencyTest", "C#");
await nodeServices.InvokeAsync<string>("latencyTest", "C#");
}

// Display results
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public async Task<IActionResult> Index(string imagePath, int maxWidth, int maxHe
}

// Invoke Node and pipe the result to the response
var imageStream = await _nodeServices.Invoke<Stream>(
var imageStream = await _nodeServices.InvokeAsync<Stream>(
"./Node/resizeImage",
fileInfo.PhysicalPath,
mimeType,
Expand Down
2 changes: 1 addition & 1 deletion samples/misc/NodeServicesExamples/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory, IHo
if (requestPath.StartsWith("/js/") && requestPath.EndsWith(".js")) {
var fileInfo = env.WebRootFileProvider.GetFileInfo(requestPath);
if (fileInfo.Exists) {
var transpiled = await nodeServices.Invoke<string>("./Node/transpilation.js", fileInfo.PhysicalPath, requestPath);
var transpiled = await nodeServices.InvokeAsync<string>("./Node/transpilation.js", fileInfo.PhysicalPath, requestPath);
await context.Response.WriteAsync(transpiled);
return;
}
Expand Down
47 changes: 47 additions & 0 deletions samples/misc/Webpack/ActionResults/PrerenderResult.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.NodeServices;
using Microsoft.AspNetCore.SpaServices.Prerendering;
using Microsoft.Extensions.DependencyInjection;

namespace Webpack.ActionResults
{
// This is an example of how you could invoke the prerendering API from an ActionResult, so as to
// prerender a SPA component as the entire response page (instead of injecting the SPA component
// into a Razor view's output)
public class PrerenderResult : ActionResult
{
private JavaScriptModuleExport _moduleExport;
private object _dataToSupply;

public PrerenderResult(JavaScriptModuleExport moduleExport, object dataToSupply = null)
{
_moduleExport = moduleExport;
_dataToSupply = dataToSupply;
}

public override async Task ExecuteResultAsync(ActionContext context)
{
var nodeServices = context.HttpContext.RequestServices.GetRequiredService<INodeServices>();
var hostEnv = context.HttpContext.RequestServices.GetRequiredService<IHostingEnvironment>();
var applicationBasePath = hostEnv.ContentRootPath;
var request = context.HttpContext.Request;
var response = context.HttpContext.Response;

var prerenderedHtml = await Prerenderer.RenderToString(
applicationBasePath,
nodeServices,
_moduleExport,
request.GetEncodedUrl(),
request.Path + request.QueryString.Value,
_dataToSupply
);

response.ContentType = "text/html";
await response.WriteAsync(prerenderedHtml.Html);
}
}
}
13 changes: 13 additions & 0 deletions samples/misc/Webpack/ActionResults/PrerenderResultExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SpaServices.Prerendering;

namespace Webpack.ActionResults
{
public static class PrerenderResultExtensions
{
public static PrerenderResult Prerender(this ControllerBase controller, JavaScriptModuleExport exportToPrerender, object dataToSupply = null)
{
return new PrerenderResult(exportToPrerender, dataToSupply);
}
}
}
17 changes: 17 additions & 0 deletions samples/misc/Webpack/Clientside/PrerenderingSample.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
export default function (params: any): Promise<{ html: string, globals?: any }> {
return new Promise((resolve, reject) => {

// Here, you could put any logic that synchronously or asynchronously prerenders
// your SPA components. For example, see the boot-server.ts files in the Angular2Spa
// and ReactReduxSpa templates for ways to prerender Angular 2 and React components.
//
// If you wanted, you could use a property on the 'params.data' object to specify
// which SPA component or template to render.

const html = `
<h1>Hello</h1>
It works! You passed <b>${ JSON.stringify(params.data) }</b>
and are currently requesting <b>${ params.location.path }</b>`;
resolve({ html });
});
};
25 changes: 25 additions & 0 deletions samples/misc/Webpack/Controllers/FullPagePrerenderingController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using System;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SpaServices.Prerendering;
using Webpack.ActionResults;

namespace Webpack.Controllers
{
// This sample shows how you could invoke the prerendering APIs directly from an MVC
// action result.
public class FullPagePrerenderingController : Controller
{
private static JavaScriptModuleExport BootModule = new JavaScriptModuleExport("Clientside/PrerenderingSample")
{
// Because the boot module is written in TypeScript, we need to specify a webpack
// config so it can be built. If it was written in JavaScript, this would not be needed.
WebpackConfig = "webpack.config.js"
};

public IActionResult Index()
{
var dataToSupply = new { nowTime = DateTime.Now.Ticks };
return this.Prerender(BootModule, dataToSupply);
}
}
}
2 changes: 2 additions & 0 deletions samples/misc/Webpack/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System.IO;
using Microsoft.AspNetCore.NodeServices;

namespace Webpack
{
Expand All @@ -14,6 +15,7 @@ public class Startup
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddNodeServices();
}

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
Expand Down
3 changes: 3 additions & 0 deletions samples/misc/Webpack/Views/Home/Index.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
<h1>Hello</h1>
Hi there. Enter some text: <input />

<hr />
See also: <a asp-controller='FullPagePrerendering'>Full-page prerendering example</a>

@section scripts {
<script src="dist/main.js"></script>
}
9 changes: 6 additions & 3 deletions samples/misc/Webpack/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,18 @@
"name": "Webpack",
"version": "0.0.0",
"devDependencies": {
"aspnet-webpack": "^1.0.3",
"css-loader": "^0.23.1",
"extendify": "^1.0.0",
"extract-text-webpack-plugin": "^1.0.1",
"less": "^2.6.0",
"less-loader": "^2.2.2",
"style-loader": "^0.13.0",
"ts-loader": "^0.8.1",
"typescript": "^1.7.5",
"webpack-hot-middleware": "^2.7.1"
},
"dependencies": {
"aspnet-webpack": "^1.0.3",
"aspnet-prerendering": "^1.0.4",
"ts-loader": "^0.8.1",
"typescript": "^1.7.5"
}
}
3 changes: 2 additions & 1 deletion samples/misc/Webpack/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
{
"compilerOptions": {
"moduleResolution": "node",
"target": "es5",
"module": "commonjs",
"target": "es6",
"jsx": "preserve",
"sourceMap": true
},
Expand Down
5 changes: 3 additions & 2 deletions samples/react/MusicStore/ReactApp/store/AlbumDetails.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { fetch } from 'domain-task/fetch';
import { fetch, addTask } from 'domain-task';
import { typeName, isActionType, Action, Reducer } from 'redux-typed';
import { ActionCreator } from './';
import { Genre } from './GenreList';
Expand Down Expand Up @@ -51,7 +51,7 @@ export const actionCreators = {
requestAlbumDetails: (albumId: number): ActionCreator => (dispatch, getState) => {
// Only load if it's not already loaded (or currently being loaded)
if (albumId !== getState().albumDetails.requestedAlbumId) {
fetch(`/api/albums/${ albumId }`)
let fetchTask = fetch(`/api/albums/${ albumId }`)
.then(results => results.json())
.then(album => {
// Only replace state if it's still the most recent request
Expand All @@ -60,6 +60,7 @@ export const actionCreators = {
}
});

addTask(fetchTask); // Ensure server-side prerendering waits for this to complete
dispatch(new RequestAlbumDetails(albumId));
}
}
Expand Down
5 changes: 3 additions & 2 deletions samples/react/MusicStore/ReactApp/store/FeaturedAlbums.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { fetch } from 'domain-task/fetch';
import { fetch, addTask } from 'domain-task';
import { typeName, isActionType, Action, Reducer } from 'redux-typed';
import { ActionCreator } from './';

Expand Down Expand Up @@ -39,10 +39,11 @@ class ReceiveFeaturedAlbums extends Action {
export const actionCreators = {
requestFeaturedAlbums: (): ActionCreator => (dispatch, getState) => {
if (!getState().featuredAlbums.isLoaded) {
fetch('/api/albums/mostPopular')
let fetchTask = fetch('/api/albums/mostPopular')
.then(results => results.json())
.then(albums => dispatch(new ReceiveFeaturedAlbums(albums)));

addTask(fetchTask); // Ensure server-side prerendering waits for this to complete
return dispatch(new RequestFeaturedAlbums());
}
}
Expand Down
5 changes: 3 additions & 2 deletions samples/react/MusicStore/ReactApp/store/GenreDetails.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { fetch } from 'domain-task/fetch';
import { fetch, addTask } from 'domain-task';
import { typeName, isActionType, Action, Reducer } from 'redux-typed';
import { ActionCreator } from './';
import { Album } from './FeaturedAlbums';
Expand Down Expand Up @@ -39,7 +39,7 @@ export const actionCreators = {
requestGenreDetails: (genreId: number): ActionCreator => (dispatch, getState) => {
// Only load if it's not already loaded (or currently being loaded)
if (genreId !== getState().genreDetails.requestedGenreId) {
fetch(`/api/genres/${ genreId }/albums`)
let fetchTask = fetch(`/api/genres/${ genreId }/albums`)
.then(results => results.json())
.then(albums => {
// Only replace state if it's still the most recent request
Expand All @@ -48,6 +48,7 @@ export const actionCreators = {
}
});

addTask(fetchTask); // Ensure server-side prerendering waits for this to complete
dispatch(new RequestGenreDetails(genreId));
}
}
Expand Down
Loading