From c46eee11c61e4c08b629836f40a157de89544cc9 Mon Sep 17 00:00:00 2001 From: Scott Addie Date: Tue, 20 Jun 2017 12:46:09 -0500 Subject: [PATCH 01/23] Initial commit for revised SpaServices doc --- aspnetcore/client-side/spa-services.md | 160 + .../sample/SpaServicesSampleApp/.gitignore | 237 ++ .../ClientApp/app/app.module.client.ts | 21 + .../ClientApp/app/app.module.server.ts | 14 + .../ClientApp/app/app.module.shared.ts | 34 + .../app/components/app/app.component.css | 6 + .../app/components/app/app.component.html | 10 + .../app/components/app/app.component.ts | 9 + .../app/components/blog/blog.component.html | 18 + .../app/components/blog/blog.component.ts | 22 + .../components/counter/counter.component.html | 7 + .../counter/counter.component.spec.ts | 29 + .../components/counter/counter.component.ts | 13 + .../app/components/counter/counter.module.ts | 13 + .../fetchdata/fetchdata.component.html | 24 + .../fetchdata/fetchdata.component.ts | 23 + .../app/components/home/home.component.html | 16 + .../app/components/home/home.component.ts | 8 + .../components/navmenu/navmenu.component.css | 59 + .../components/navmenu/navmenu.component.html | 38 + .../components/navmenu/navmenu.component.ts | 9 + .../app/components/post/post.component.html | 24 + .../app/components/post/post.component.ts | 26 + .../ClientApp/boot-client.ts | 22 + .../ClientApp/boot-server.ts | 36 + .../ClientApp/test/boot-tests.ts | 33 + .../ClientApp/test/karma.conf.js | 26 + .../Controllers/BlogsController.cs | 17 + .../Controllers/HomeController.cs | 11 + .../Controllers/PostsController.cs | 18 + .../Controllers/SampleDataController.cs | 43 + .../SpaServicesSampleApp/Data/SampleData.cs | 28 + .../SpaServicesSampleApp/Models/Blog.cs | 13 + .../SpaServicesSampleApp/Models/Post.cs | 11 + .../sample/SpaServicesSampleApp/Program.cs | 24 + .../SpaServicesSampleApp.csproj | 46 + .../sample/SpaServicesSampleApp/Startup.cs | 67 + .../Views/Home/Index.cshtml | 10 + .../Views/Shared/Error.cshtml | 6 + .../Views/Shared/Layout.cshtml | 68 + .../Views/Shared/_Layout.cshtml | 15 + .../Views/_ViewImports.cshtml | 3 + .../Views/_ViewStart.cshtml | 3 + .../SpaServicesSampleApp/appsettings.json | 10 + .../sample/SpaServicesSampleApp/bower.json | 10 + .../SpaServicesSampleApp/bundleconfig.json | 24 + .../sample/SpaServicesSampleApp/global.json | 3 + .../SpaServicesSampleApp/package-lock.json | 3588 +++++++++++++++++ .../sample/SpaServicesSampleApp/package.json | 64 + .../sample/SpaServicesSampleApp/tsconfig.json | 14 + .../sample/SpaServicesSampleApp/web.config | 14 + .../SpaServicesSampleApp/webpack.config.js | 71 + .../webpack.config.vendor.js | 88 + 53 files changed, 5206 insertions(+) create mode 100644 aspnetcore/client-side/spa-services.md create mode 100644 aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/.gitignore create mode 100644 aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/app.module.client.ts create mode 100644 aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/app.module.server.ts create mode 100644 aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/app.module.shared.ts create mode 100644 aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/components/app/app.component.css create mode 100644 aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/components/app/app.component.html create mode 100644 aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/components/app/app.component.ts create mode 100644 aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/components/blog/blog.component.html create mode 100644 aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/components/blog/blog.component.ts create mode 100644 aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/components/counter/counter.component.html create mode 100644 aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/components/counter/counter.component.spec.ts create mode 100644 aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/components/counter/counter.component.ts create mode 100644 aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/components/counter/counter.module.ts create mode 100644 aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/components/fetchdata/fetchdata.component.html create mode 100644 aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/components/fetchdata/fetchdata.component.ts create mode 100644 aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/components/home/home.component.html create mode 100644 aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/components/home/home.component.ts create mode 100644 aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/components/navmenu/navmenu.component.css create mode 100644 aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/components/navmenu/navmenu.component.html create mode 100644 aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/components/navmenu/navmenu.component.ts create mode 100644 aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/components/post/post.component.html create mode 100644 aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/components/post/post.component.ts create mode 100644 aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/boot-client.ts create mode 100644 aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/boot-server.ts create mode 100644 aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/test/boot-tests.ts create mode 100644 aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/test/karma.conf.js create mode 100644 aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Controllers/BlogsController.cs create mode 100644 aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Controllers/HomeController.cs create mode 100644 aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Controllers/PostsController.cs create mode 100644 aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Controllers/SampleDataController.cs create mode 100644 aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Data/SampleData.cs create mode 100644 aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Models/Blog.cs create mode 100644 aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Models/Post.cs create mode 100644 aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Program.cs create mode 100644 aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/SpaServicesSampleApp.csproj create mode 100644 aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Startup.cs create mode 100644 aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Views/Home/Index.cshtml create mode 100644 aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Views/Shared/Error.cshtml create mode 100644 aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Views/Shared/Layout.cshtml create mode 100644 aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Views/Shared/_Layout.cshtml create mode 100644 aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Views/_ViewImports.cshtml create mode 100644 aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Views/_ViewStart.cshtml create mode 100644 aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/appsettings.json create mode 100644 aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/bower.json create mode 100644 aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/bundleconfig.json create mode 100644 aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/global.json create mode 100644 aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/package-lock.json create mode 100644 aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/package.json create mode 100644 aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/tsconfig.json create mode 100644 aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/web.config create mode 100644 aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/webpack.config.js create mode 100644 aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/webpack.config.vendor.js diff --git a/aspnetcore/client-side/spa-services.md b/aspnetcore/client-side/spa-services.md new file mode 100644 index 000000000000..78977f6762a9 --- /dev/null +++ b/aspnetcore/client-side/spa-services.md @@ -0,0 +1,160 @@ +--- +title: Using SpaServices for Creating Universal Applications | Microsoft Docs +author: scottaddie +description: Learn about the benefits of using SpaServices to build a SPA with ASP.NET Core +keywords: ASP.NET Core, Angular, SPA, JavaScriptServices, SpaServices +ms.author: scaddie +manager: wpickett +ms.date: 6/19/2017 +ms.topic: article +ms.assetid: 4b30576b-2718-4c39-9253-a59966747893 +ms.technology: aspnet +ms.prod: asp.net-core +uid: client-side/spa-services +ms.custom: H1Hack27Feb2017 +--- +# Using SpaServices for Creating Universal Applications with ASP.NET Core + +By [Scott Addie](https://github.com/scottaddie) + +In this article, you will learn about the value proposition of [SpaServices](https://github.com/aspnet/JavaScriptServices/tree/dev/src/Microsoft.AspNetCore.SpaServices) in building a Single Page Application (SPA) with ASP.NET Core. + +[View or download sample code](https://github.com/aspnet/Docs/tree/master/aspnetcore/client-side/spa-services/sample) + +## Using SpaServices with ASP.NET Core + +A SPA is a popular type of web application due to its inherent rich user experience; however, integrating client-side SPA frameworks or libraries, such as [Angular](https://angular.io/) or [React](https://facebook.github.io/react/), with server-side frameworks like ASP.NET Core can be daunting. The `Microsoft.AspNetCore.SpaServices` NuGet package, or SpaServices for short, was developed to reduce friction in the integration process. It enables seamless operation between the disparate client and server technology stacks. + +## What is SpaServices? + +SpaServices is not required to develop SPAs with ASP.NET Core. Since SpaServices is a nonopinionated, client framework-agnostic library, it doesn't lock you into a particular client framework, library, or coding style. It provides useful infrastructure such as server-side prerendering, Webpack middleware, Hot Module Replacement, and routing helpers. + +### Server-side prerendering + +A universal (also known as isomorphic) application is a JavaScript application capable of running both on the server and the client. Angular, React, and other popular frameworks provide a universal platform for this application development style. The idea is to first render the framework components on the server-side via Node.js and then delegate further execution to the client. SpaServices' Tag Helpers make the implementation of server-side prerendering simple by invoking the JavaScript functions on the server for you. + +#### Tag Helpers + +SpaServices provides a suite of ASP.NET Core [Tag Helpers](xref:mvc/views/tag-helpers/intro) to support the prerendering process. Using them requires installation of the following mutually inclusive prerequisites: +1. [Microsoft.AspNetCore.SpaServices](http://www.nuget.org/packages/Microsoft.AspNetCore.SpaServices/) NuGet package +1. [aspnet-prerendering](https://www.npmjs.com/package/aspnet-prerendering) npm package + +With the required packages installed, the Tag Helpers are made discoverable via registration in the project's `_ViewImports.cshtml` file: + +[!code-csharp[Main](../client-side/spa-services/sample/SpaServicesSampleApp/Views/_ViewImports.cshtml?highlight=3)] + +These Tag Helpers abstract away the intricacies of communicating directly with low-level APIs by leveraging an HTML-like syntax within the Razor view: + +[!code-csharp[Main](../client-side/spa-services/sample/SpaServicesSampleApp/Views/Home/Index.cshtml?highlight=5)] + +#### The asp-prerender-module Tag Helper + +The `asp-prerender-module` Tag Helper executes `ClientApp/dist/main-server.js` on the server via Node.js. To clarify, `main-server.js` file is an artifact of the [Webpack](http://webpack.github.io/) build process' TypeScript-to-JavaScript transpilation task. Webpack defines an entry point alias of `main-server`; and, traversal of the dependency graph for this alias begins at the `ClientApp/boot-server.ts` file: + +[!code-javascript[Main](../client-side/spa-services/sample/SpaServicesSampleApp/webpack.config.js?range=53)] + +The `ClientApp/boot-server.ts` file utilizes the `createServerRenderer` function and `RenderResult` type of the `aspnet-prerendering` npm package to configure server rendering via Node.js. The HTML markup destined for server-side rendering is passed to a `resolve` function call, which is wrapped in a JavaScript `Promise` object of type `RenderResult`. The `Promise` object is significant in that it asynchronously supplies the HTML markup to the page for injection in the placeholder element. + +[!code-javascript[Main](../client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/boot-server.ts?range=6,10-)] + +#### The asp-prerender-data Tag Helper + +Sometimes contextual information must be passed as arguments from the Razor view to the server-side JavaScript. To satisfy this requirement, the `asp-prerender-data` Tag Helper is used in conjunction with the aforementioned `asp-prerender-module` Tag Helper. For example, the following markup passes user data to the `main-server` module: + +```html +
+``` + +The received data is serialized using the built-in JSON serializer and is stored in the `params.data` object. The following is an example of using the data from the `param.data` object to construct some simple markup: +[again, best to import a snippet from a working sample] + +```javascript +var prerendering = require('aspnet-prerendering'); + +module.exports = prerendering.createServerRenderer(function (params) { + return new Promise(function (resolve, reject) { + var result = '

Good Morning, ' + params.data.userName + '!

'; + resolve({ html: result }); + }); +}); +``` + +*PascalCase* notation is used for property names in the `asp-prerender-data` Tag Helper; however, in JavaScript they are used in **camelCase**. The default JSON serialization configuration is responsible for this difference. + +Data can be passed back to the view. ~While returning the `Promise` object that resolves markup, use the `globals` property to send data back to the view:~ Maybe something like, In the preceding code, the `Promise` object bla bla bla. The following code uses the `globals` property to send data to the view: + +```javascript +resolve({ + html: result, + globals: { + postList: [ + 'Introduction to ASP.NET Core', + 'Making apps with Angular and ASP.NET Core' + ] + } +}); +``` + +Each of the properties set inside the `globals` object will create individual, globally-accessible JavaScript variables with the same property names and associated values. + +## Webpack middleware + +### MapSpaFallbackRoute + +### UseWebpackDevMiddleware + +## Hot Module Replacement + +## Routing helpers + + + + + + +## Prerequisites + +## Creating a new project + +### Using the .NET Core CLI + +### Using npm / Yarn + +### What does it produce + +### Web API + +### Angular + +## Developing your project + +### Create the Backend (Controller / REST) + +### Create Angular Module & Components + +### Add Lazy Loading + +## Debugging your application + +### Visual Studio Code + +### Visual Studio + +### Chrome + +## Distribution + +### Use Ahead of Time Compilation + +### Use Tree Shaking with Webpack + +## Deploying your application + +### Azure + +## Additional resources + +* [Angular Docs](https://angular.io/docs) \ No newline at end of file diff --git a/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/.gitignore b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/.gitignore new file mode 100644 index 000000000000..d261e7c72984 --- /dev/null +++ b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/.gitignore @@ -0,0 +1,237 @@ +/Properties/launchSettings.json + +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +build/ +bld/ +bin/ +Bin/ +obj/ +Obj/ + +# Visual Studio 2015 cache/options directory +.vs/ +/wwwroot/dist/ +/ClientApp/dist/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# TODO: Comment the next line if you want to checkin your web deploy settings +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Microsoft Azure ApplicationInsights config file +ApplicationInsights.config + +# Windows Store app package directory +AppPackages/ +BundleArtifacts/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.pfx +*.publishsettings +orleans.codegen.cs + +/node_modules + +/yarn.lock + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe + +# FAKE - F# Make +.fake/ diff --git a/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/app.module.client.ts b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/app.module.client.ts new file mode 100644 index 000000000000..ee77812262e4 --- /dev/null +++ b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/app.module.client.ts @@ -0,0 +1,21 @@ +import { NgModule } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; +import { FormsModule } from '@angular/forms'; +import { HttpModule } from '@angular/http'; +import { sharedConfig } from './app.module.shared'; + +@NgModule({ + bootstrap: sharedConfig.bootstrap, + declarations: sharedConfig.declarations, + imports: [ + BrowserModule, + FormsModule, + HttpModule, + ...sharedConfig.imports + ], + providers: [ + { provide: 'ORIGIN_URL', useValue: location.origin } + ] +}) +export class AppModule { +} diff --git a/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/app.module.server.ts b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/app.module.server.ts new file mode 100644 index 000000000000..f2b6eb4b37a8 --- /dev/null +++ b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/app.module.server.ts @@ -0,0 +1,14 @@ +import { NgModule } from '@angular/core'; +import { ServerModule } from '@angular/platform-server'; +import { sharedConfig } from './app.module.shared'; + +@NgModule({ + bootstrap: sharedConfig.bootstrap, + declarations: sharedConfig.declarations, + imports: [ + ServerModule, + ...sharedConfig.imports + ] +}) +export class AppModule { +} diff --git a/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/app.module.shared.ts b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/app.module.shared.ts new file mode 100644 index 000000000000..570e638f6327 --- /dev/null +++ b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/app.module.shared.ts @@ -0,0 +1,34 @@ +import { NgModule } from '@angular/core'; +import { RouterModule } from '@angular/router'; + +import { AppComponent } from './components/app/app.component' +import { NavMenuComponent } from './components/navmenu/navmenu.component'; +import { HomeComponent } from './components/home/home.component'; +import { FetchDataComponent } from './components/fetchdata/fetchdata.component'; +import { CounterComponent } from './components/counter/counter.component'; +import { BlogComponent } from './components/blog/blog.component'; +import { PostComponent } from './components/post/post.component'; + +export const sharedConfig: NgModule = { + bootstrap: [ AppComponent ], + declarations: [ + AppComponent, + NavMenuComponent, + CounterComponent, + FetchDataComponent, + HomeComponent, + BlogComponent, + PostComponent + ], + imports: [ + RouterModule.forRoot([ + { path: '', redirectTo: 'home', pathMatch: 'full' }, + { path: 'home', component: HomeComponent }, + { path: 'counter', component: CounterComponent }, + { path: 'fetch-data', component: FetchDataComponent }, + { path: 'blog', component: BlogComponent }, + { path: 'blog/:id', component: PostComponent }, + { path: '**', redirectTo: 'home' } + ]) + ] +}; diff --git a/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/components/app/app.component.css b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/components/app/app.component.css new file mode 100644 index 000000000000..63926006af63 --- /dev/null +++ b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/components/app/app.component.css @@ -0,0 +1,6 @@ +@media (max-width: 767px) { + /* On small screens, the nav menu spans the full width of the screen. Leave a space for it. */ + .body-content { + padding-top: 50px; + } +} diff --git a/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/components/app/app.component.html b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/components/app/app.component.html new file mode 100644 index 000000000000..f208d1e83e9f --- /dev/null +++ b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/components/app/app.component.html @@ -0,0 +1,10 @@ +
+
+
+ +
+
+ +
+
+
diff --git a/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/components/app/app.component.ts b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/components/app/app.component.ts new file mode 100644 index 000000000000..b20a1aed2b73 --- /dev/null +++ b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/components/app/app.component.ts @@ -0,0 +1,9 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app', + templateUrl: './app.component.html', + styleUrls: ['./app.component.css'] +}) +export class AppComponent { +} diff --git a/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/components/blog/blog.component.html b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/components/blog/blog.component.html new file mode 100644 index 000000000000..9655e04950c5 --- /dev/null +++ b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/components/blog/blog.component.html @@ -0,0 +1,18 @@ +

Blogs

+ +

Loading...

+ + + + + + + + + + + + + + +
TitleUrl
{{ blog.title }}{{ blog.url }}
\ No newline at end of file diff --git a/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/components/blog/blog.component.ts b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/components/blog/blog.component.ts new file mode 100644 index 000000000000..d39dfcc8dea2 --- /dev/null +++ b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/components/blog/blog.component.ts @@ -0,0 +1,22 @@ +import { Component } from '@angular/core'; +import { Http } from '@angular/http'; + +@Component({ + selector: 'blog', + templateUrl: './blog.component.html' +}) +export class BlogComponent { + public blogs: Blog[]; + + constructor(http: Http) { + http.get('/api/blogs').subscribe(result => { + this.blogs = result.json() as Blog[]; + }); + } +} + +interface Blog { + blogId: number; + title: string; + url: string; +} \ No newline at end of file diff --git a/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/components/counter/counter.component.html b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/components/counter/counter.component.html new file mode 100644 index 000000000000..2521eda7afde --- /dev/null +++ b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/components/counter/counter.component.html @@ -0,0 +1,7 @@ +

Counter

+ +

This is a simple example of an Angular component.

+ +

Current count: {{ currentCount }}

+ + diff --git a/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/components/counter/counter.component.spec.ts b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/components/counter/counter.component.spec.ts new file mode 100644 index 000000000000..61e49bcd7680 --- /dev/null +++ b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/components/counter/counter.component.spec.ts @@ -0,0 +1,29 @@ +/// +import { assert } from 'chai'; +import { CounterComponent } from './counter.component'; +import { TestBed, async, ComponentFixture } from '@angular/core/testing'; + +let fixture: ComponentFixture; + +describe('Counter component', () => { + beforeEach(() => { + TestBed.configureTestingModule({ declarations: [CounterComponent] }); + fixture = TestBed.createComponent(CounterComponent); + fixture.detectChanges(); + }); + + it('should display a title', async(() => { + const titleText = fixture.nativeElement.querySelector('h1').textContent; + expect(titleText).toEqual('Counter'); + })); + + it('should start with count 0, then increments by 1 when clicked', async(() => { + const countElement = fixture.nativeElement.querySelector('strong'); + expect(countElement.textContent).toEqual('0'); + + const incrementButton = fixture.nativeElement.querySelector('button'); + incrementButton.click(); + fixture.detectChanges(); + expect(countElement.textContent).toEqual('1'); + })); +}); diff --git a/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/components/counter/counter.component.ts b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/components/counter/counter.component.ts new file mode 100644 index 000000000000..69de17d9c34a --- /dev/null +++ b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/components/counter/counter.component.ts @@ -0,0 +1,13 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'counter', + templateUrl: './counter.component.html' +}) +export class CounterComponent { + public currentCount = 0; + + public incrementCounter() { + this.currentCount++; + } +} diff --git a/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/components/counter/counter.module.ts b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/components/counter/counter.module.ts new file mode 100644 index 000000000000..303c457819d2 --- /dev/null +++ b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/components/counter/counter.module.ts @@ -0,0 +1,13 @@ +import { NgModule } from '@angular/core'; +import { RouterModule } from '@angular/router'; +import { CounterComponent } from './counter.component'; + +@NgModule({ + imports: [ + RouterModule.forChild([{ path: '', component: CounterComponent }]) + ], + exports: [RouterModule], + declarations: [CounterComponent] +}) + +export class CounterModule { } \ No newline at end of file diff --git a/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/components/fetchdata/fetchdata.component.html b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/components/fetchdata/fetchdata.component.html new file mode 100644 index 000000000000..8d24e7102ca6 --- /dev/null +++ b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/components/fetchdata/fetchdata.component.html @@ -0,0 +1,24 @@ +

Weather forecast

+ +

This component demonstrates fetching data from the server.

+ +

Loading...

+ + + + + + + + + + + + + + + + + + +
DateTemp. (C)Temp. (F)Summary
{{ forecast.dateFormatted }}{{ forecast.temperatureC }}{{ forecast.temperatureF }}{{ forecast.summary }}
diff --git a/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/components/fetchdata/fetchdata.component.ts b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/components/fetchdata/fetchdata.component.ts new file mode 100644 index 000000000000..9c98b76efb1c --- /dev/null +++ b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/components/fetchdata/fetchdata.component.ts @@ -0,0 +1,23 @@ +import { Component, Inject } from '@angular/core'; +import { Http } from '@angular/http'; + +@Component({ + selector: 'fetchdata', + templateUrl: './fetchdata.component.html' +}) +export class FetchDataComponent { + public forecasts: WeatherForecast[]; + + constructor(http: Http, @Inject('ORIGIN_URL') originUrl: string) { + http.get(originUrl + '/api/SampleData/WeatherForecasts').subscribe(result => { + this.forecasts = result.json() as WeatherForecast[]; + }); + } +} + +interface WeatherForecast { + dateFormatted: string; + temperatureC: number; + temperatureF: number; + summary: string; +} diff --git a/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/components/home/home.component.html b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/components/home/home.component.html new file mode 100644 index 000000000000..3f2c0584f041 --- /dev/null +++ b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/components/home/home.component.html @@ -0,0 +1,16 @@ +

Hello, world!

+

Welcome to your new single-page application, built with:

+ +

To help you get started, we've also set up:

+
    +
  • Client-side navigation. For example, click Counter then Back to return here.
  • +
  • Server-side prerendering. For faster initial loading and improved SEO, your Angular app is prerendered on the server. The resulting HTML is then transferred to the browser where a client-side copy of the app takes over.
  • +
  • Webpack dev middleware. In development mode, there's no need to run the webpack build tool. Your client-side resources are dynamically built on demand. Updates are available as soon as you modify any file.
  • +
  • Hot module replacement. In development mode, you don't even need to reload the page after making most changes. Within seconds of saving changes to files, your Angular app will be rebuilt and a new instance injected is into the page.
  • +
  • Efficient production builds. In production mode, development-time features are disabled, and the webpack build tool produces minified static CSS and JavaScript files.
  • +
diff --git a/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/components/home/home.component.ts b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/components/home/home.component.ts new file mode 100644 index 000000000000..81846cee82aa --- /dev/null +++ b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/components/home/home.component.ts @@ -0,0 +1,8 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'home', + templateUrl: './home.component.html' +}) +export class HomeComponent { +} diff --git a/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/components/navmenu/navmenu.component.css b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/components/navmenu/navmenu.component.css new file mode 100644 index 000000000000..e15c61289531 --- /dev/null +++ b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/components/navmenu/navmenu.component.css @@ -0,0 +1,59 @@ +li .glyphicon { + margin-right: 10px; +} + +/* Highlighting rules for nav menu items */ +li.link-active a, +li.link-active a:hover, +li.link-active a:focus { + background-color: #4189C7; + color: white; +} + +/* Keep the nav menu independent of scrolling and on top of other items */ +.main-nav { + position: fixed; + top: 0; + left: 0; + right: 0; + z-index: 1; +} + +@media (min-width: 768px) { + /* On small screens, convert the nav menu to a vertical sidebar */ + .main-nav { + height: 100%; + width: calc(25% - 20px); + } + .navbar { + border-radius: 0px; + border-width: 0px; + height: 100%; + } + .navbar-header { + float: none; + } + .navbar-collapse { + border-top: 1px solid #444; + padding: 0px; + } + .navbar ul { + float: none; + } + .navbar li { + float: none; + font-size: 15px; + margin: 6px; + } + .navbar li a { + padding: 10px 16px; + border-radius: 4px; + } + .navbar a { + /* If a menu item's text is too long, truncate it */ + width: 100%; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } +} diff --git a/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/components/navmenu/navmenu.component.html b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/components/navmenu/navmenu.component.html new file mode 100644 index 000000000000..e6501d251ef1 --- /dev/null +++ b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/components/navmenu/navmenu.component.html @@ -0,0 +1,38 @@ + diff --git a/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/components/navmenu/navmenu.component.ts b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/components/navmenu/navmenu.component.ts new file mode 100644 index 000000000000..7a1691cb2075 --- /dev/null +++ b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/components/navmenu/navmenu.component.ts @@ -0,0 +1,9 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'nav-menu', + templateUrl: './navmenu.component.html', + styleUrls: ['./navmenu.component.css'] +}) +export class NavMenuComponent { +} diff --git a/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/components/post/post.component.html b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/components/post/post.component.html new file mode 100644 index 000000000000..b81e13f3c21c --- /dev/null +++ b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/components/post/post.component.html @@ -0,0 +1,24 @@ +

Posts

+ +

Loading...

+ + + + + + + + + + + + + + + + +
TitleAuthorPost Link
{{ post.title }}{{ post.author }} + + Read + +
\ No newline at end of file diff --git a/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/components/post/post.component.ts b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/components/post/post.component.ts new file mode 100644 index 000000000000..103a14e0af15 --- /dev/null +++ b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/components/post/post.component.ts @@ -0,0 +1,26 @@ +import { Component } from '@angular/core'; +import { Http } from '@angular/http'; +import { ActivatedRoute, Params } from '@angular/router'; +import { Location } from '@angular/common'; +import 'rxjs/add/operator/switchMap'; + +@Component({ + selector: 'post', + templateUrl: './post.component.html' +}) +export class PostComponent { + public posts: Post[]; + + constructor(private route: ActivatedRoute, private location: Location, private http: Http) { + this.route.params + .switchMap((params: Params) => http.get('/api/blogs/' + params['id'] + '/posts')) + .subscribe(result => this.posts = result.json() as Post[]); + } +} + +interface Post { + postId: number; + title: string; + author: string; + link: string; +} \ No newline at end of file diff --git a/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/boot-client.ts b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/boot-client.ts new file mode 100644 index 000000000000..45c471c417b0 --- /dev/null +++ b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/boot-client.ts @@ -0,0 +1,22 @@ +import 'reflect-metadata'; +import 'zone.js'; +import { enableProdMode } from '@angular/core'; +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; +import { AppModule } from './app/app.module.client'; + +if (module['hot']) { + module['hot'].accept(); + module['hot'].dispose(() => { + // Before restarting the app, we create a new root element and dispose the old one + const oldRootElem = document.querySelector('app'); + const newRootElem = document.createElement('app'); + oldRootElem.parentNode.insertBefore(newRootElem, oldRootElem); + modulePromise.then(appModule => appModule.destroy()); + }); +} else { + enableProdMode(); +} + +// Note: @ng-tools/webpack looks for the following expression when performing production +// builds. Don't change how this line looks, otherwise you may break tree-shaking. +const modulePromise = platformBrowserDynamic().bootstrapModule(AppModule); diff --git a/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/boot-server.ts b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/boot-server.ts new file mode 100644 index 000000000000..61e7472c304a --- /dev/null +++ b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/boot-server.ts @@ -0,0 +1,36 @@ +import 'reflect-metadata'; +import 'zone.js'; +import 'rxjs/add/operator/first'; +import { enableProdMode, ApplicationRef, NgZone, ValueProvider } from '@angular/core'; +import { platformDynamicServer, PlatformState, INITIAL_CONFIG } from '@angular/platform-server'; +import { createServerRenderer, RenderResult } from 'aspnet-prerendering'; +import { AppModule } from './app/app.module.server'; + +enableProdMode(); + +export default createServerRenderer(params => { + const providers = [ + { provide: INITIAL_CONFIG, useValue: { document: '', url: params.url } }, + { provide: 'ORIGIN_URL', useValue: params.origin } + ]; + + return platformDynamicServer(providers).bootstrapModule(AppModule).then(moduleRef => { + const appRef = moduleRef.injector.get(ApplicationRef); + const state = moduleRef.injector.get(PlatformState); + const zone = moduleRef.injector.get(NgZone); + + return new Promise((resolve, reject) => { + zone.onError.subscribe(errorInfo => reject(errorInfo)); + appRef.isStable.first(isStable => isStable).subscribe(() => { + // Because 'onStable' fires before 'onError', we have to delay slightly before + // completing the request in case there's an error to report + setImmediate(() => { + resolve({ + html: state.renderToString() + }); + moduleRef.destroy(); + }); + }); + }); + }); +}); \ No newline at end of file diff --git a/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/test/boot-tests.ts b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/test/boot-tests.ts new file mode 100644 index 000000000000..fe3591e904b5 --- /dev/null +++ b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/test/boot-tests.ts @@ -0,0 +1,33 @@ +// Load required polyfills and testing libraries +import 'reflect-metadata'; +import 'zone.js'; +import 'zone.js/dist/long-stack-trace-zone'; +import 'zone.js/dist/proxy.js'; +import 'zone.js/dist/sync-test'; +import 'zone.js/dist/jasmine-patch'; +import 'zone.js/dist/async-test'; +import 'zone.js/dist/fake-async-test'; +import * as testing from '@angular/core/testing'; +import * as testingBrowser from '@angular/platform-browser-dynamic/testing'; + +// There's no typing for the `__karma__` variable. Just declare it as any +declare var __karma__: any; +declare var require: any; + +// Prevent Karma from running prematurely +__karma__.loaded = function () {}; + +// First, initialize the Angular testing environment +testing.getTestBed().initTestEnvironment( + testingBrowser.BrowserDynamicTestingModule, + testingBrowser.platformBrowserDynamicTesting() +); + +// Then we find all the tests +const context = require.context('../', true, /\.spec\.ts$/); + +// And load the modules +context.keys().map(context); + +// Finally, start Karma to run the tests +__karma__.start(); diff --git a/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/test/karma.conf.js b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/test/karma.conf.js new file mode 100644 index 000000000000..c7f03d49a32a --- /dev/null +++ b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/test/karma.conf.js @@ -0,0 +1,26 @@ +// Karma configuration file, see link for more information +// https://karma-runner.github.io/0.13/config/configuration-file.html + +module.exports = function (config) { + config.set({ + basePath: '.', + frameworks: ['jasmine'], + files: [ + '../../wwwroot/dist/vendor.js', + './boot-tests.ts' + ], + preprocessors: { + './boot-tests.ts': ['webpack'] + }, + reporters: ['progress'], + port: 9876, + colors: true, + logLevel: config.LOG_INFO, + autoWatch: true, + browsers: ['Chrome'], + mime: { 'application/javascript': ['ts','tsx'] }, + singleRun: false, + webpack: require('../../webpack.config.js')().filter(config => config.target !== 'node'), // Test against client bundle, because tests run in a browser + webpackMiddleware: { stats: 'errors-only' } + }); +}; diff --git a/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Controllers/BlogsController.cs b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Controllers/BlogsController.cs new file mode 100644 index 000000000000..cab55b075c4c --- /dev/null +++ b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Controllers/BlogsController.cs @@ -0,0 +1,17 @@ +using Microsoft.AspNetCore.Mvc; +using SpaServicesSampleApp.Data; + +namespace SpaServicesSampleApp.Controllers +{ + [Route("api/[controller]")] + public class BlogsController : Controller + { + [HttpGet] + public IActionResult Get() + { + var blogs = SampleData.Blogs(); + + return Ok(blogs); + } + } +} \ No newline at end of file diff --git a/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Controllers/HomeController.cs b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Controllers/HomeController.cs new file mode 100644 index 000000000000..172ab09e3235 --- /dev/null +++ b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Controllers/HomeController.cs @@ -0,0 +1,11 @@ +using Microsoft.AspNetCore.Mvc; + +namespace SpaServicesSampleApp.Controllers +{ + public class HomeController : Controller + { + public IActionResult Index() => View(); + + public IActionResult Error() => View(); + } +} \ No newline at end of file diff --git a/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Controllers/PostsController.cs b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Controllers/PostsController.cs new file mode 100644 index 000000000000..a3088fbe5753 --- /dev/null +++ b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Controllers/PostsController.cs @@ -0,0 +1,18 @@ +using System.Linq; +using Microsoft.AspNetCore.Mvc; +using SpaServicesSampleApp.Data; + +namespace SpaServicesSampleApp.Controllers +{ + [Route("api/blogs/{blogId}/posts")] + public class PostsController : Controller + { + [HttpGet()] + public IActionResult Get(int blogId) + { + var posts = SampleData.Posts().Where(p => p.BlogId == blogId); + + return Ok(posts); + } + } +} diff --git a/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Controllers/SampleDataController.cs b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Controllers/SampleDataController.cs new file mode 100644 index 000000000000..d8e8b53ae349 --- /dev/null +++ b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Controllers/SampleDataController.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.AspNetCore.Mvc; + +namespace SpaServicesSampleApp.Controllers +{ + [Route("api/[controller]")] + public class SampleDataController : Controller + { + private static string[] Summaries = new[] + { + "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" + }; + + [HttpGet("[action]")] + public IEnumerable WeatherForecasts() + { + var rng = new Random(); + return Enumerable.Range(1, 5).Select(index => new WeatherForecast + { + DateFormatted = DateTime.Now.AddDays(index).ToString("d"), + TemperatureC = rng.Next(-20, 55), + Summary = Summaries[rng.Next(Summaries.Length)] + }); + } + + public class WeatherForecast + { + public string DateFormatted { get; set; } + public int TemperatureC { get; set; } + public string Summary { get; set; } + + public int TemperatureF + { + get + { + return 32 + (int)(TemperatureC / 0.5556); + } + } + } + } +} diff --git a/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Data/SampleData.cs b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Data/SampleData.cs new file mode 100644 index 000000000000..f1806288098c --- /dev/null +++ b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Data/SampleData.cs @@ -0,0 +1,28 @@ +using SpaServicesSampleApp.Models; +using System.Collections.Generic; + +namespace SpaServicesSampleApp.Data +{ + public static class SampleData + { + public static IEnumerable Blogs() + { + return new List() + { + new Blog{ BlogId = 1, Url = "http://blog.stevensanderson.com/", Title = "Steve Sanderson's Blog" }, + new Blog{ BlogId = 2, Url = "http://fiyazhasan.me", Title = "Classroom of Fizz" } + }; + } + + public static IEnumerable Posts() + { + return new List + { + new Post(){ PostId=1, Title="ASP.NET Core + Angular 2 template for Visual Studio", Author="Steve Sanderson", Link="http://blog.stevensanderson.com/2016/10/04/angular2-template-for-visual-studio/", BlogId=1}, + new Post(){ PostId=2, Title="Angular 2, React, and Knockout apps on ASP.NET Core", Author="Steve Sanderson", Link="http://blog.stevensanderson.com/2016/05/02/angular2-react-knockout-apps-on-aspnet-core/", BlogId=1}, + new Post(){ PostId=3, Title="Building Custom Formatters for .Net Core (Yaml Formatters)", Author="Fiyaz Hasan", Link="http://www.fiyazhasan.me/building-custom-formatters-for-net-core-yaml-formatters/", BlogId=2}, + new Post(){ PostId=4, Title="Preventing XSRF in AngularJS Apps with ASP.NET CORE Anti-Forgery Middleware", Author="Fiyaz Hasan", Link="http://www.fiyazhasan.me/angularjs-anti-forgery-with-asp-net-core/", BlogId=2} + }; + } + } +} diff --git a/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Models/Blog.cs b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Models/Blog.cs new file mode 100644 index 000000000000..19d54f7bcfcc --- /dev/null +++ b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Models/Blog.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; + +namespace SpaServicesSampleApp.Models +{ + public class Blog + { + public int BlogId { get; set; } + public string Url { get; set; } + public string Title { get; set; } + + public List Posts { get; set; } + } +} diff --git a/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Models/Post.cs b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Models/Post.cs new file mode 100644 index 000000000000..94588bcbb2bd --- /dev/null +++ b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Models/Post.cs @@ -0,0 +1,11 @@ +namespace SpaServicesSampleApp.Models +{ + public class Post + { + public int PostId { get; set; } + public string Title { get; set; } + public string Link { get; set; } + public string Author { get; set; } + public int BlogId { get; set; } + } +} diff --git a/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Program.cs b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Program.cs new file mode 100644 index 000000000000..13e846972e82 --- /dev/null +++ b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Program.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Hosting; + +namespace SpaServicesSampleApp +{ + public class Program + { + public static void Main(string[] args) + { + var host = new WebHostBuilder() + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseIISIntegration() + .UseStartup() + .Build(); + + host.Run(); + } + } +} diff --git a/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/SpaServicesSampleApp.csproj b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/SpaServicesSampleApp.csproj new file mode 100644 index 000000000000..f1d66a25bf33 --- /dev/null +++ b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/SpaServicesSampleApp.csproj @@ -0,0 +1,46 @@ + + + netcoreapp1.1 + true + false + portable-net45+win8 + + + + + + + + + + + + + + + + + + + package.json + + + + + + + + + + + + + + + + %(DistFiles.Identity) + PreserveNewest + + + + \ No newline at end of file diff --git a/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Startup.cs b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Startup.cs new file mode 100644 index 000000000000..db3db30e5db1 --- /dev/null +++ b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Startup.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.SpaServices.Webpack; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + +namespace SpaServicesSampleApp +{ + public class Startup + { + public Startup(IHostingEnvironment env) + { + var builder = new ConfigurationBuilder() + .SetBasePath(env.ContentRootPath) + .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) + .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true) + .AddEnvironmentVariables(); + Configuration = builder.Build(); + } + + public IConfigurationRoot Configuration { get; } + + // This method gets called by the runtime. Use this method to add services to the container. + public void ConfigureServices(IServiceCollection services) + { + // Add framework services. + services.AddMvc(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) + { + loggerFactory.AddConsole(Configuration.GetSection("Logging")); + loggerFactory.AddDebug(); + + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions { + HotModuleReplacement = true + }); + } + else + { + app.UseExceptionHandler("/Home/Error"); + } + + app.UseStaticFiles(); + + app.UseMvc(routes => + { + routes.MapRoute( + name: "default", + template: "{controller=Home}/{action=Index}/{id?}"); + + routes.MapSpaFallbackRoute( + name: "spa-fallback", + defaults: new { controller = "Home", action = "Index" }); + }); + } + } +} diff --git a/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Views/Home/Index.cshtml b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Views/Home/Index.cshtml new file mode 100644 index 000000000000..008eb8731f47 --- /dev/null +++ b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Views/Home/Index.cshtml @@ -0,0 +1,10 @@ +@{ + ViewData["Title"] = "Home Page"; +} + +Loading... + + +@section scripts { + +} \ No newline at end of file diff --git a/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Views/Shared/Error.cshtml b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Views/Shared/Error.cshtml new file mode 100644 index 000000000000..473b35d6caa8 --- /dev/null +++ b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Views/Shared/Error.cshtml @@ -0,0 +1,6 @@ +@{ + ViewData["Title"] = "Error"; +} + +

Error.

+

An error occurred while processing your request.

diff --git a/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Views/Shared/Layout.cshtml b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Views/Shared/Layout.cshtml new file mode 100644 index 000000000000..aa8501a83911 --- /dev/null +++ b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Views/Shared/Layout.cshtml @@ -0,0 +1,68 @@ + + + + + + @ViewData["Title"] - SpaServicesSampleApp + + + + + + + + + + + + +
+ @RenderBody() +
+
+

© 2016 - SpaServicesSampleApp

+
+
+ + + + + + + + + + + + + @RenderSection("scripts", required: false) + + diff --git a/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Views/Shared/_Layout.cshtml b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Views/Shared/_Layout.cshtml new file mode 100644 index 000000000000..db8d544244b4 --- /dev/null +++ b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Views/Shared/_Layout.cshtml @@ -0,0 +1,15 @@ + + + + + + @ViewData["Title"] - SpaServicesSampleApp + + + + + @RenderBody() + + @RenderSection("scripts", required: false) + + diff --git a/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Views/_ViewImports.cshtml b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Views/_ViewImports.cshtml new file mode 100644 index 000000000000..0e9cfbbc7e33 --- /dev/null +++ b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Views/_ViewImports.cshtml @@ -0,0 +1,3 @@ +@using SpaServicesSampleApp +@addTagHelper "*, Microsoft.AspNetCore.Mvc.TagHelpers" +@addTagHelper "*, Microsoft.AspNetCore.SpaServices" \ No newline at end of file diff --git a/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Views/_ViewStart.cshtml b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Views/_ViewStart.cshtml new file mode 100644 index 000000000000..820a2f6e02f1 --- /dev/null +++ b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Views/_ViewStart.cshtml @@ -0,0 +1,3 @@ +@{ + Layout = "_Layout"; +} diff --git a/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/appsettings.json b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/appsettings.json new file mode 100644 index 000000000000..723c096a87e9 --- /dev/null +++ b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "IncludeScopes": false, + "LogLevel": { + "Default": "Debug", + "System": "Information", + "Microsoft": "Information" + } + } +} diff --git a/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/bower.json b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/bower.json new file mode 100644 index 000000000000..e187afbd6867 --- /dev/null +++ b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/bower.json @@ -0,0 +1,10 @@ +{ + "name": "asp.net", + "private": true, + "dependencies": { + "bootstrap": "3.3.6", + "jquery": "2.2.0", + "jquery-validation": "1.14.0", + "jquery-validation-unobtrusive": "3.2.6" + } +} diff --git a/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/bundleconfig.json b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/bundleconfig.json new file mode 100644 index 000000000000..04754ba7135f --- /dev/null +++ b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/bundleconfig.json @@ -0,0 +1,24 @@ +// Configure bundling and minification for the project. +// More info at https://go.microsoft.com/fwlink/?LinkId=808241 +[ + { + "outputFileName": "wwwroot/css/site.min.css", + // An array of relative input file paths. Globbing patterns supported + "inputFiles": [ + "wwwroot/css/site.css" + ] + }, + { + "outputFileName": "wwwroot/js/site.min.js", + "inputFiles": [ + "wwwroot/js/site.js" + ], + // Optionally specify minification options + "minify": { + "enabled": true, + "renameLocals": true + }, + // Optinally generate .map file + "sourceMap": false + } +] diff --git a/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/global.json b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/global.json new file mode 100644 index 000000000000..f87d63416217 --- /dev/null +++ b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/global.json @@ -0,0 +1,3 @@ +{ + "sdk": { "version": "1.0.4" } +} diff --git a/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/package-lock.json b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/package-lock.json new file mode 100644 index 000000000000..d227c799e9b0 --- /dev/null +++ b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/package-lock.json @@ -0,0 +1,3588 @@ +{ + "name": "spaservicessampleapp", + "version": "0.0.0", + "lockfileVersion": 1, + "dependencies": { + "@angular/animations": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-4.1.2.tgz", + "integrity": "sha1-M3FZbnNrfSQOIAR30N2MWSk1ISQ=" + }, + "@angular/common": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-4.1.2.tgz", + "integrity": "sha1-j20ElsIEIQv+JV/rQ3pyJT8GmE0=" + }, + "@angular/compiler": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-4.1.2.tgz", + "integrity": "sha1-CVQjgRYv2N2WLYRVlJjOaaBQceo=" + }, + "@angular/core": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-4.1.2.tgz", + "integrity": "sha1-N7XAQLndNwA0ma6gQxnWmeOHBZY=" + }, + "@angular/forms": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-4.1.2.tgz", + "integrity": "sha1-gpg+nsXQgz564UvrjkfdNXyI9JA=" + }, + "@angular/http": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@angular/http/-/http-4.1.2.tgz", + "integrity": "sha1-/DeMMzDAQQ4fuKrCVGMppoh3duQ=" + }, + "@angular/platform-browser": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-4.1.2.tgz", + "integrity": "sha1-FuUKj3W01nXJ4pA+SZ4P5MahJaw=" + }, + "@angular/platform-browser-dynamic": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-4.1.2.tgz", + "integrity": "sha1-0kHGF5LHlL7GJ6tksxpmvqIqu58=" + }, + "@angular/platform-server": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@angular/platform-server/-/platform-server-4.1.2.tgz", + "integrity": "sha1-PvIgaln9LRONJnqtEBpQ09gG45w=" + }, + "@angular/router": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-4.1.2.tgz", + "integrity": "sha1-MbGxJv7C4cMvRVfvjrYFU2rcyyc=" + }, + "@types/chai": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-3.5.2.tgz", + "integrity": "sha1-wRzSgX06QBt7oPWkIPNcVhObHB4=", + "dev": true + }, + "@types/jasmine": { + "version": "2.5.47", + "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-2.5.47.tgz", + "integrity": "sha1-u7qbzw6V54kMb0pHOU6LrKqWDrY=", + "dev": true + }, + "@types/node": { + "version": "7.0.18", + "resolved": "https://registry.npmjs.org/@types/node/-/node-7.0.18.tgz", + "integrity": "sha1-zWfyfT3Az7dG8L3V4IbExdVb4XM=" + }, + "accepts": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.3.tgz", + "integrity": "sha1-w8p0NJOGSMPg2cHjKN1otiLChMo=", + "dev": true + }, + "acorn": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.0.3.tgz", + "integrity": "sha1-xGDfCEkUY/AozLguqzcwvwEIez0=" + }, + "acorn-dynamic-import": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-2.0.2.tgz", + "integrity": "sha1-x1K9IQvvZ5UBtsbLf8hPj0cVjMQ=", + "dependencies": { + "acorn": { + "version": "4.0.13", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", + "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=" + } + } + }, + "after": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz", + "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8=", + "dev": true + }, + "ajv": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", + "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=" + }, + "ajv-keywords": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-1.5.1.tgz", + "integrity": "sha1-MU3QpLM2j609/NxU7eYXG4htrzw=" + }, + "align-text": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", + "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=" + }, + "alphanum-sort": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz", + "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=" + }, + "amdefine": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=" + }, + "angular-router-loader": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/angular-router-loader/-/angular-router-loader-0.6.0.tgz", + "integrity": "sha1-bHyvfx6mrMbivGgs75GjQ+mn87U=", + "dev": true, + "dependencies": { + "loader-utils": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", + "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=", + "dev": true + } + } + }, + "angular2-template-loader": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/angular2-template-loader/-/angular2-template-loader-0.6.2.tgz", + "integrity": "sha1-wNROkP/w+sleiyPwQ6zaf9HFHXw=" + }, + "ansi-html": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz", + "integrity": "sha1-gTWEAhliqenm/QOflA0S9WynhZ4=" + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" + }, + "anymatch": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.0.tgz", + "integrity": "sha1-o+Uvo5FoyCX/V7AkgSbOWo/5VQc=" + }, + "argparse": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz", + "integrity": "sha1-c9g7wmP4bpf4zE9rrhsOkKfSLIY=" + }, + "arr-diff": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", + "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=" + }, + "arr-flatten": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.0.3.tgz", + "integrity": "sha1-onTthawIhJtr14R8RYB0XcUa37E=" + }, + "array-slice": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz", + "integrity": "sha1-3Tz7gO15c6dRF82sabC5nshhhvU=", + "dev": true + }, + "array-unique": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", + "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=" + }, + "arraybuffer.slice": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.6.tgz", + "integrity": "sha1-8zshWfBTKj8xB6JywMz70a0peco=", + "dev": true + }, + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=" + }, + "asn1.js": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.9.1.tgz", + "integrity": "sha1-SLokC0WpKA6UdImQull9IWYX/UA=" + }, + "aspnet-prerendering": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/aspnet-prerendering/-/aspnet-prerendering-2.0.5.tgz", + "integrity": "sha1-/ODCIhJVb3gBrojdDhsWl5rqNhE=" + }, + "aspnet-webpack": { + "version": "1.0.29", + "resolved": "https://registry.npmjs.org/aspnet-webpack/-/aspnet-webpack-1.0.29.tgz", + "integrity": "sha1-X+F9gJ3et4JpB62Y/yFW8cF+0Ik=" + }, + "assert": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz", + "integrity": "sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=" + }, + "assertion-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.0.2.tgz", + "integrity": "sha1-E8pRXYYgbaC6xm6DTdOX2HWBCUw=", + "dev": true + }, + "ast-types": { + "version": "0.9.6", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.9.6.tgz", + "integrity": "sha1-ECyenpAF0+fjgpvwxPok7oYu6bk=" + }, + "async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/async/-/async-2.4.1.tgz", + "integrity": "sha1-YqVrJ5yYoR0JhwlqAcw+6463u9c=" + }, + "async-each": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz", + "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=" + }, + "atob": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/atob/-/atob-1.1.3.tgz", + "integrity": "sha1-lfE2KbEsOlGl0hWr3OKqnzL4B3M=" + }, + "autoprefixer": { + "version": "6.7.7", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-6.7.7.tgz", + "integrity": "sha1-Hb0cg1ZY41zj+ZhAmdsAWFx4IBQ=" + }, + "awesome-typescript-loader": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/awesome-typescript-loader/-/awesome-typescript-loader-3.1.3.tgz", + "integrity": "sha1-frQd+xNsLqDgmb7wONxwxF+BAiM=", + "dependencies": { + "loader-utils": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", + "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=" + } + } + }, + "babel-code-frame": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.22.0.tgz", + "integrity": "sha1-AnYgvuVnqIwyVhV05/0IAdMxGOQ=" + }, + "backo2": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", + "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=", + "dev": true + }, + "balanced-match": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", + "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=" + }, + "base64-arraybuffer": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz", + "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg=", + "dev": true + }, + "base64-js": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.0.tgz", + "integrity": "sha1-o5mS1yNYSBGYK+XikLtqU9hnAPE=" + }, + "base64id": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-1.0.0.tgz", + "integrity": "sha1-R2iMuZu2gE8OBtPnY7HDLlfY5rY=", + "dev": true + }, + "better-assert": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz", + "integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=", + "dev": true + }, + "big.js": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.1.3.tgz", + "integrity": "sha1-TK2iGTZS6zyp7I5VyQFWacmAaXg=" + }, + "binary-extensions": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.8.0.tgz", + "integrity": "sha1-SOyNFt9Dd+rl+liEaCSAr02Vx3Q=" + }, + "blob": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.4.tgz", + "integrity": "sha1-vPEwUspURj8w+fx+lbmkdjCpSSE=", + "dev": true + }, + "bluebird": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.0.tgz", + "integrity": "sha1-eRQg1/VR7qKJdFOop3ZT+WYG1nw=", + "dev": true + }, + "bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha1-UzRK2xRhehP26N0s4okF0cC6MhU=" + }, + "body-parser": { + "version": "1.17.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.17.2.tgz", + "integrity": "sha1-+IkqvI+eYn1Crtr7yma/WrmRBO4=", + "dev": true, + "dependencies": { + "iconv-lite": { + "version": "0.4.15", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.15.tgz", + "integrity": "sha1-/iZaIYrGpXz+hUkn6dBMGYJe3es=", + "dev": true + } + } + }, + "bootstrap": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-3.3.7.tgz", + "integrity": "sha1-WjiTlFSfIzMIdaOxUGVldPip63E=" + }, + "brace-expansion": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", + "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", + "dependencies": { + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + } + } + }, + "braces": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", + "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=" + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" + }, + "browserify-aes": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.0.6.tgz", + "integrity": "sha1-Xncl297x/Vkw1OurSFZ85FHEigo=" + }, + "browserify-cipher": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.0.tgz", + "integrity": "sha1-mYgkSHS/XtTijalWZtzWasj8Njo=" + }, + "browserify-des": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.0.tgz", + "integrity": "sha1-2qJ3cXRwki7S/hhZQRihdUOXId0=" + }, + "browserify-rsa": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=" + }, + "browserify-sign": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", + "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=" + }, + "browserify-zlib": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.1.4.tgz", + "integrity": "sha1-uzX4pRn2AOD6a4SFJByXnQFB+y0=" + }, + "browserslist": { + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz", + "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=" + }, + "buffer": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", + "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=" + }, + "buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=" + }, + "builtin-modules": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=" + }, + "builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=" + }, + "bytes": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-2.4.0.tgz", + "integrity": "sha1-fZcZb51br39pNeJZhVSe3SpsIzk=", + "dev": true + }, + "callsite": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", + "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA=", + "dev": true + }, + "camel-case": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz", + "integrity": "sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=" + }, + "camelcase": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", + "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=" + }, + "caniuse-api": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-1.6.1.tgz", + "integrity": "sha1-tTTnxzTE+B7F++isoq0kNUuWLGw=" + }, + "caniuse-db": { + "version": "1.0.30000692", + "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000692.tgz", + "integrity": "sha1-Pampk1OtvOoeFCuZ9g7MYhbfR6U=" + }, + "center-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", + "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=" + }, + "chai": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-3.5.0.tgz", + "integrity": "sha1-TQJjewZ/6Vi9v906QOxW/vc3Mkc=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=" + }, + "chokidar": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz", + "integrity": "sha1-eY5ol3gVHIB2tLNg5e3SjNortGg=" + }, + "cipher-base": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.3.tgz", + "integrity": "sha1-7qvxlEGc6QDaMBjCB9IS8qbfCgc=" + }, + "clap": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/clap/-/clap-1.2.0.tgz", + "integrity": "sha1-WckP4+E3EEdG/xlGmiemNP9oyFc=" + }, + "clean-css": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.1.4.tgz", + "integrity": "sha1-7siBHbJ0V+AHjYypIfqBty+oK/Q=" + }, + "cliui": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", + "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=" + }, + "clone": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.2.tgz", + "integrity": "sha1-Jgt6meux7f4kdTgXX3gyQ8sZ0Uk=" + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" + }, + "coa": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/coa/-/coa-1.0.3.tgz", + "integrity": "sha1-G1Sl4dz3fJkEVdTe6pjFZEFtyJM=" + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" + }, + "color": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/color/-/color-0.11.4.tgz", + "integrity": "sha1-bXtcdPtl6EHNSHkq0e1eB7kE12Q=" + }, + "color-convert": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.0.tgz", + "integrity": "sha1-Gsz5fdc5uYO/mU1W/sj5WFNkG3o=" + }, + "color-name": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.2.tgz", + "integrity": "sha1-XIq3K2S9IhXWF66VWeuxSEdc+Y0=" + }, + "color-string": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-0.3.0.tgz", + "integrity": "sha1-J9RvtnAlxcL6JZk7+/V55HhBuZE=" + }, + "colormin": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/colormin/-/colormin-1.1.2.tgz", + "integrity": "sha1-6i90IKcrlogaOKrlnsEkpvcpgTM=" + }, + "colors": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", + "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=" + }, + "combine-lists": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/combine-lists/-/combine-lists-1.0.1.tgz", + "integrity": "sha1-RYwH4J4NkA/Ci3Cj/sLazR0st/Y=", + "dev": true + }, + "commander": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", + "integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=" + }, + "component-bind": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz", + "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E=", + "dev": true + }, + "component-emitter": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.1.2.tgz", + "integrity": "sha1-KWWU8nU9qmOZbSrwjRWpURbJrsM=", + "dev": true + }, + "component-inherit": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz", + "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM=", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "connect": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/connect/-/connect-3.6.2.tgz", + "integrity": "sha1-aU6NIGgb/kkCgsiriGvpjwn0L+c=" + }, + "console-browserify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", + "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=" + }, + "constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=" + }, + "content-type": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.2.tgz", + "integrity": "sha1-t9ETrueo3Se9IRM8TcJSnfFyHu0=", + "dev": true + }, + "cookie": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=", + "dev": true + }, + "core-js": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.4.1.tgz", + "integrity": "sha1-TekR5mew6ukSTjQlS1OupvxhjT4=", + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "create-ecdh": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.0.tgz", + "integrity": "sha1-iIxyNZbN92EvZJgjPuvXo1MBc30=" + }, + "create-hash": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.1.3.tgz", + "integrity": "sha1-YGBCrIuSYnUPSDyt2rD1gZFy2P0=" + }, + "create-hmac": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.6.tgz", + "integrity": "sha1-rLniIaThe9sHbpBlfEK5PjcmzwY=" + }, + "crypto-browserify": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.11.0.tgz", + "integrity": "sha1-NlKgkGq5sqfgw85mpAjpV6JIVSI=" + }, + "css": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/css/-/css-2.2.1.tgz", + "integrity": "sha1-c6TIHehdtmTU7mdPfUcIXjstVdw=", + "dependencies": { + "source-map": { + "version": "0.1.43", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz", + "integrity": "sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y=" + } + } + }, + "css-color-names": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", + "integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=" + }, + "css-loader": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-0.28.1.tgz", + "integrity": "sha1-IgMlWZ+PAEUtnOtMPKbIpmeYZC0=", + "dependencies": { + "loader-utils": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", + "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=" + } + } + }, + "css-selector-tokenizer": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.0.tgz", + "integrity": "sha1-5piEdK6MlTR3v15+/s/OzNnPTIY=" + }, + "cssesc": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-0.1.0.tgz", + "integrity": "sha1-yBSQPkViM3GgR3tAEJqq++6t27Q=" + }, + "cssnano": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-3.10.0.tgz", + "integrity": "sha1-Tzj2zqK5sX+gFJDyPx3GjqZcHDg=" + }, + "csso": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/csso/-/csso-2.3.2.tgz", + "integrity": "sha1-3dUsWHAz9J6Utx/FVWnyUuj/X4U=" + }, + "custom-event": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", + "integrity": "sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU=", + "dev": true + }, + "date-now": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", + "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=" + }, + "debug": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.7.tgz", + "integrity": "sha1-krrR9tBbu2u6Isyoi80OyJTChh4=" + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" + }, + "deep-eql": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-0.1.3.tgz", + "integrity": "sha1-71WKyrjeJSBs1xOQbXTlaTDrafI=", + "dev": true, + "dependencies": { + "type-detect": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-0.1.1.tgz", + "integrity": "sha1-C6XsKohWQORw6k6FBZcZANrFiCI=", + "dev": true + } + } + }, + "defined": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", + "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=" + }, + "depd": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.0.tgz", + "integrity": "sha1-4b2Cxqq2ztlluXuIsX7T5SjKGMM=", + "dev": true + }, + "des.js": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz", + "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=" + }, + "di": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", + "integrity": "sha1-gGZJMmzqp8qjMG112YXqJ0i6kTw=", + "dev": true + }, + "diffie-hellman": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.2.tgz", + "integrity": "sha1-tYNXOScM/ias9jIJn97SoH8gnl4=" + }, + "dom-serialize": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz", + "integrity": "sha1-ViromZ9Evl6jB29UGdzVnrQ6yVs=", + "dev": true + }, + "domain-browser": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.1.7.tgz", + "integrity": "sha1-hnqksJP6oF8d4IwG9NeyH9+GmLw=" + }, + "domain-context": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/domain-context/-/domain-context-0.5.1.tgz", + "integrity": "sha1-MhxmpBBVmHUHsjlqzF8E5T+5l8Q=" + }, + "domain-task": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/domain-task/-/domain-task-2.0.3.tgz", + "integrity": "sha1-BoIDIOnMBn5hmKdRrY5yzRpVLtA=" + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "electron-to-chromium": { + "version": "1.3.14", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.14.tgz", + "integrity": "sha1-ZK8Pnv08PGrNV9cfg7Scp+6cS0M=" + }, + "elliptic": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.0.tgz", + "integrity": "sha1-ysmvh2LIWDYYcAPI3+GT5eLq5d8=" + }, + "emojis-list": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", + "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=" + }, + "encodeurl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.1.tgz", + "integrity": "sha1-eePVhlU0aQn+bw9Fpd5oEDspTSA=" + }, + "encoding": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz", + "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=" + }, + "engine.io": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-1.8.3.tgz", + "integrity": "sha1-jef5eJXSDTm4X4ju7nd7K9QrE9Q=", + "dev": true, + "dependencies": { + "debug": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", + "integrity": "sha1-QMRT5n5uE8kB3ewxeviYbNqe/4w=", + "dev": true + }, + "ms": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", + "integrity": "sha1-riXPJRKziFodldfwN4aNhDESR2U=", + "dev": true + } + } + }, + "engine.io-client": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-1.8.3.tgz", + "integrity": "sha1-F5jtk0USRkU9TG9jXXogH+lA1as=", + "dev": true, + "dependencies": { + "component-emitter": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", + "dev": true + }, + "debug": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", + "integrity": "sha1-QMRT5n5uE8kB3ewxeviYbNqe/4w=", + "dev": true + }, + "ms": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", + "integrity": "sha1-riXPJRKziFodldfwN4aNhDESR2U=", + "dev": true + } + } + }, + "engine.io-parser": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-1.3.2.tgz", + "integrity": "sha1-k3sHnwAH0Ik+xW1GyyILjLQ1Igo=", + "dev": true + }, + "enhanced-resolve": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-3.1.0.tgz", + "integrity": "sha1-n0tib1dyRe3PSyrYPYbhf09CHew=", + "dependencies": { + "memory-fs": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", + "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=" + } + } + }, + "ent": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", + "integrity": "sha1-6WQhkyWiHQX0RGai9obtbOX13R0=", + "dev": true + }, + "errno": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.4.tgz", + "integrity": "sha1-uJbiOp5ei6M4cfyZar02NfyaHH0=" + }, + "error-ex": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz", + "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=" + }, + "es6-promise": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz", + "integrity": "sha1-oIzd6EzNvzTQJ6FFG8kdS80ophM=" + }, + "es6-shim": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/es6-shim/-/es6-shim-0.35.3.tgz", + "integrity": "sha1-m/tzY/7//4emzbbNk+QF7DxLbyY=" + }, + "es6-templates": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/es6-templates/-/es6-templates-0.2.3.tgz", + "integrity": "sha1-XLmsn7He1usSOTQrgdeSu7QHjuQ=" + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + }, + "esprima": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", + "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=" + }, + "esutils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=" + }, + "event-source-polyfill": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/event-source-polyfill/-/event-source-polyfill-0.0.9.tgz", + "integrity": "sha1-GMYgXRcKsJ24if/OqjPw5JPxSlA=" + }, + "eventemitter3": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-1.2.0.tgz", + "integrity": "sha1-HIaZHYFq0eUEdQ5zh0Ik7PO+xQg=", + "dev": true + }, + "events": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=" + }, + "evp_bytestokey": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.0.tgz", + "integrity": "sha1-SXtmrZ/vZc18CKYYCCS6FHa2blM=" + }, + "expand-braces": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/expand-braces/-/expand-braces-0.1.2.tgz", + "integrity": "sha1-SIsdHSRRyz06axks/AMPRMWFX+o=", + "dev": true, + "dependencies": { + "braces": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/braces/-/braces-0.1.5.tgz", + "integrity": "sha1-wIVxEIUpHYt1/ddOqw+FlygHEeY=", + "dev": true + }, + "expand-range": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-0.1.1.tgz", + "integrity": "sha1-TLjtoJk8pW+k9B/ELzy7TMrf8EQ=", + "dev": true + }, + "is-number": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-0.1.1.tgz", + "integrity": "sha1-aaevEWlj1HIG7JvZtIoUIW8eOAY=", + "dev": true + }, + "repeat-string": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-0.2.2.tgz", + "integrity": "sha1-x6jTI2BoNiBZp+RlH8aITosftK4=", + "dev": true + } + } + }, + "expand-brackets": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", + "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=" + }, + "expand-range": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", + "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=" + }, + "expose-loader": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/expose-loader/-/expose-loader-0.7.3.tgz", + "integrity": "sha1-NfvTZZeJ5PqoH1nei36fw55GbVE=" + }, + "extend": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", + "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=", + "dev": true + }, + "extglob": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", + "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=" + }, + "extract-text-webpack-plugin": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/extract-text-webpack-plugin/-/extract-text-webpack-plugin-2.1.0.tgz", + "integrity": "sha1-aTFbiF+Hbb+W04Gfap8cynrr8Vk=", + "dependencies": { + "loader-utils": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", + "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=" + } + } + }, + "fastparse": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.1.tgz", + "integrity": "sha1-0eJkOzipTXWDtHkGDmxK/8lAcfg=" + }, + "file-loader": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-0.11.1.tgz", + "integrity": "sha1-azKO4SNKcp5OR9Njdd1tNcDh24Q=", + "dependencies": { + "loader-utils": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", + "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=" + } + } + }, + "filename-regex": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", + "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=" + }, + "fill-range": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.3.tgz", + "integrity": "sha1-ULd9/X5Gm8dJJHCWNpn+eoSFpyM=" + }, + "finalhandler": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.0.3.tgz", + "integrity": "sha1-70fneVDpmXgOhgIqVg4yF+DQzIk=" + }, + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=" + }, + "flatten": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/flatten/-/flatten-1.0.2.tgz", + "integrity": "sha1-2uRqnXj74lKSJYzB54CkHZXAN4I=" + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=" + }, + "for-own": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", + "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=" + }, + "fs-access": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fs-access/-/fs-access-1.0.1.tgz", + "integrity": "sha1-1qh/JiJxzv6+wwxVNAf7mV2od3o=", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "fsevents": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.1.2.tgz", + "integrity": "sha512-Sn44E5wQW4bTHXvQmvSHwqbuiXtduD6Rrjm2ZtUEGbyrig+nUH3t/QD4M4/ZXViY556TBpRgZkHLDx3JxPwxiw==", + "optional": true, + "dependencies": { + "abbrev": { + "version": "1.1.0", + "bundled": true, + "optional": true + }, + "ajv": { + "version": "4.11.8", + "bundled": true, + "optional": true + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true + }, + "aproba": { + "version": "1.1.1", + "bundled": true, + "optional": true + }, + "are-we-there-yet": { + "version": "1.1.4", + "bundled": true, + "optional": true + }, + "asn1": { + "version": "0.2.3", + "bundled": true, + "optional": true + }, + "assert-plus": { + "version": "0.2.0", + "bundled": true, + "optional": true + }, + "asynckit": { + "version": "0.4.0", + "bundled": true, + "optional": true + }, + "aws-sign2": { + "version": "0.6.0", + "bundled": true, + "optional": true + }, + "aws4": { + "version": "1.6.0", + "bundled": true, + "optional": true + }, + "balanced-match": { + "version": "0.4.2", + "bundled": true + }, + "bcrypt-pbkdf": { + "version": "1.0.1", + "bundled": true, + "optional": true + }, + "block-stream": { + "version": "0.0.9", + "bundled": true + }, + "boom": { + "version": "2.10.1", + "bundled": true + }, + "brace-expansion": { + "version": "1.1.7", + "bundled": true + }, + "buffer-shims": { + "version": "1.0.0", + "bundled": true + }, + "caseless": { + "version": "0.12.0", + "bundled": true, + "optional": true + }, + "co": { + "version": "4.6.0", + "bundled": true, + "optional": true + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true + }, + "combined-stream": { + "version": "1.0.5", + "bundled": true + }, + "concat-map": { + "version": "0.0.1", + "bundled": true + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true + }, + "cryptiles": { + "version": "2.0.5", + "bundled": true, + "optional": true + }, + "dashdash": { + "version": "1.14.1", + "bundled": true, + "optional": true, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "bundled": true, + "optional": true + } + } + }, + "debug": { + "version": "2.6.8", + "bundled": true, + "optional": true + }, + "deep-extend": { + "version": "0.4.2", + "bundled": true, + "optional": true + }, + "delayed-stream": { + "version": "1.0.0", + "bundled": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true, + "optional": true + }, + "ecc-jsbn": { + "version": "0.1.1", + "bundled": true, + "optional": true + }, + "extend": { + "version": "3.0.1", + "bundled": true, + "optional": true + }, + "extsprintf": { + "version": "1.0.2", + "bundled": true + }, + "forever-agent": { + "version": "0.6.1", + "bundled": true, + "optional": true + }, + "form-data": { + "version": "2.1.4", + "bundled": true, + "optional": true + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true + }, + "fstream": { + "version": "1.0.11", + "bundled": true + }, + "fstream-ignore": { + "version": "1.0.5", + "bundled": true, + "optional": true + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "optional": true + }, + "getpass": { + "version": "0.1.7", + "bundled": true, + "optional": true, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "bundled": true, + "optional": true + } + } + }, + "glob": { + "version": "7.1.2", + "bundled": true + }, + "graceful-fs": { + "version": "4.1.11", + "bundled": true + }, + "har-schema": { + "version": "1.0.5", + "bundled": true, + "optional": true + }, + "har-validator": { + "version": "4.2.1", + "bundled": true, + "optional": true + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true, + "optional": true + }, + "hawk": { + "version": "3.1.3", + "bundled": true, + "optional": true + }, + "hoek": { + "version": "2.16.3", + "bundled": true + }, + "http-signature": { + "version": "1.1.1", + "bundled": true, + "optional": true + }, + "inflight": { + "version": "1.0.6", + "bundled": true + }, + "inherits": { + "version": "2.0.3", + "bundled": true + }, + "ini": { + "version": "1.3.4", + "bundled": true, + "optional": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true + }, + "is-typedarray": { + "version": "1.0.0", + "bundled": true, + "optional": true + }, + "isarray": { + "version": "1.0.0", + "bundled": true + }, + "isstream": { + "version": "0.1.2", + "bundled": true, + "optional": true + }, + "jodid25519": { + "version": "1.0.2", + "bundled": true, + "optional": true + }, + "jsbn": { + "version": "0.1.1", + "bundled": true, + "optional": true + }, + "json-schema": { + "version": "0.2.3", + "bundled": true, + "optional": true + }, + "json-stable-stringify": { + "version": "1.0.1", + "bundled": true, + "optional": true + }, + "json-stringify-safe": { + "version": "5.0.1", + "bundled": true, + "optional": true + }, + "jsonify": { + "version": "0.0.0", + "bundled": true, + "optional": true + }, + "jsprim": { + "version": "1.4.0", + "bundled": true, + "optional": true, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "bundled": true, + "optional": true + } + } + }, + "mime-db": { + "version": "1.27.0", + "bundled": true + }, + "mime-types": { + "version": "2.1.15", + "bundled": true + }, + "minimatch": { + "version": "3.0.4", + "bundled": true + }, + "minimist": { + "version": "0.0.8", + "bundled": true + }, + "mkdirp": { + "version": "0.5.1", + "bundled": true + }, + "ms": { + "version": "2.0.0", + "bundled": true, + "optional": true + }, + "node-pre-gyp": { + "version": "0.6.36", + "bundled": true, + "optional": true + }, + "nopt": { + "version": "4.0.1", + "bundled": true, + "optional": true + }, + "npmlog": { + "version": "4.1.0", + "bundled": true, + "optional": true + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true + }, + "oauth-sign": { + "version": "0.8.2", + "bundled": true, + "optional": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true, + "optional": true + }, + "once": { + "version": "1.4.0", + "bundled": true + }, + "os-homedir": { + "version": "1.0.2", + "bundled": true, + "optional": true + }, + "os-tmpdir": { + "version": "1.0.2", + "bundled": true, + "optional": true + }, + "osenv": { + "version": "0.1.4", + "bundled": true, + "optional": true + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true + }, + "performance-now": { + "version": "0.2.0", + "bundled": true, + "optional": true + }, + "process-nextick-args": { + "version": "1.0.7", + "bundled": true + }, + "punycode": { + "version": "1.4.1", + "bundled": true, + "optional": true + }, + "qs": { + "version": "6.4.0", + "bundled": true, + "optional": true + }, + "rc": { + "version": "1.2.1", + "bundled": true, + "optional": true, + "dependencies": { + "minimist": { + "version": "1.2.0", + "bundled": true, + "optional": true + } + } + }, + "readable-stream": { + "version": "2.2.9", + "bundled": true + }, + "request": { + "version": "2.81.0", + "bundled": true, + "optional": true + }, + "rimraf": { + "version": "2.6.1", + "bundled": true + }, + "safe-buffer": { + "version": "5.0.1", + "bundled": true + }, + "semver": { + "version": "5.3.0", + "bundled": true, + "optional": true + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true, + "optional": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true, + "optional": true + }, + "sntp": { + "version": "1.0.9", + "bundled": true, + "optional": true + }, + "sshpk": { + "version": "1.13.0", + "bundled": true, + "optional": true, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "bundled": true, + "optional": true + } + } + }, + "string_decoder": { + "version": "1.0.1", + "bundled": true + }, + "string-width": { + "version": "1.0.2", + "bundled": true + }, + "stringstream": { + "version": "0.0.5", + "bundled": true, + "optional": true + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true + }, + "strip-json-comments": { + "version": "2.0.1", + "bundled": true, + "optional": true + }, + "tar": { + "version": "2.2.1", + "bundled": true + }, + "tar-pack": { + "version": "3.4.0", + "bundled": true, + "optional": true + }, + "tough-cookie": { + "version": "2.3.2", + "bundled": true, + "optional": true + }, + "tunnel-agent": { + "version": "0.6.0", + "bundled": true, + "optional": true + }, + "tweetnacl": { + "version": "0.14.5", + "bundled": true, + "optional": true + }, + "uid-number": { + "version": "0.0.6", + "bundled": true, + "optional": true + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true + }, + "uuid": { + "version": "3.0.1", + "bundled": true, + "optional": true + }, + "verror": { + "version": "1.3.6", + "bundled": true, + "optional": true + }, + "wide-align": { + "version": "1.1.2", + "bundled": true, + "optional": true + }, + "wrappy": { + "version": "1.0.2", + "bundled": true + } + } + }, + "function-bind": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.0.tgz", + "integrity": "sha1-FhdnFMgBeY5Ojyz391KUZ7tKV3E=" + }, + "get-caller-file": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.2.tgz", + "integrity": "sha1-9wLmMSfn4jHBYKgMFVSstw1QR+U=" + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true + }, + "glob-base": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", + "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=" + }, + "glob-parent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", + "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=" + }, + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=" + }, + "graceful-readlink": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", + "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=" + }, + "has": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.1.tgz", + "integrity": "sha1-hGFzP1OLCDfJNh45qauelwTcLyg=" + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=" + }, + "has-binary": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/has-binary/-/has-binary-0.1.7.tgz", + "integrity": "sha1-aOYesWIQyVRaClzOBqhzkS/h5ow=", + "dev": true, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + } + } + }, + "has-cors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz", + "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=", + "dev": true + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=" + }, + "hash-base": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-2.0.2.tgz", + "integrity": "sha1-ZuodhW206KVHDK32/OI65SRO8uE=" + }, + "hash.js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.0.3.tgz", + "integrity": "sha1-EzL/ABVsCg/92CNgE9B7d6BFFXM=" + }, + "he": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", + "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=" + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=" + }, + "hosted-git-info": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.4.2.tgz", + "integrity": "sha1-AHa59GonBQbduq6lZJaJdGBhKmc=" + }, + "html-comment-regex": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/html-comment-regex/-/html-comment-regex-1.1.1.tgz", + "integrity": "sha1-ZouTd26q5V696POtRkswekljYl4=" + }, + "html-entities": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.2.1.tgz", + "integrity": "sha1-DfKTUfByEWNRXfueVUPl9u7VFi8=" + }, + "html-loader": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/html-loader/-/html-loader-0.4.5.tgz", + "integrity": "sha1-X7zYfNY6XEmn/OL+VvQl4Fcpxow=", + "dependencies": { + "loader-utils": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", + "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=" + } + } + }, + "html-minifier": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-3.5.2.tgz", + "integrity": "sha1-1zvD/0SJQkCIGM5gm/P7DqfvTrc=" + }, + "http-errors": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.1.tgz", + "integrity": "sha1-X4uO2YrKVFZWv1cplzh/kEpyIlc=", + "dev": true + }, + "http-proxy": { + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.16.2.tgz", + "integrity": "sha1-Bt/ykpUr9k2+hHH6nfcwZtTzd0I=", + "dev": true + }, + "https-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-0.0.1.tgz", + "integrity": "sha1-P5E2XKvmC3ftDruiS0VOPgnZWoI=" + }, + "iconv-lite": { + "version": "0.4.18", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.18.tgz", + "integrity": "sha512-sr1ZQph3UwHTR0XftSbK85OvBbxe/abLGzEnPENCQwmHf7sck8Oyu4ob3LgBxWWxRoM+QszeUyl7jbqapu2TqA==" + }, + "icss-replace-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz", + "integrity": "sha1-Bupvg2ead0njhs/h/oEq5dsiPe0=" + }, + "ieee754": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz", + "integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q=" + }, + "indexes-of": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz", + "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=" + }, + "indexof": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", + "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=" + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "interpret": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.0.3.tgz", + "integrity": "sha1-y8NcYu7uc/Gat7EKgBURQBr8D5A=" + }, + "invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=" + }, + "is-absolute-url": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-2.1.0.tgz", + "integrity": "sha1-UFMN+4T8yap9vnhS6Do3uTufKqY=" + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" + }, + "is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=" + }, + "is-buffer": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.5.tgz", + "integrity": "sha1-Hzsm72E7IUuIy8ojzGwB2Hlh7sw=" + }, + "is-builtin-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", + "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=" + }, + "is-dotfile": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", + "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=" + }, + "is-equal-shallow": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", + "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=" + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=" + }, + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=" + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=" + }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=" + }, + "is-number": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", + "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=" + }, + "is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=" + }, + "is-posix-bracket": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", + "integrity": "sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=" + }, + "is-primitive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", + "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=" + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" + }, + "is-svg": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-svg/-/is-svg-2.1.0.tgz", + "integrity": "sha1-z2EJDaDZ77yrhyLeum8DIgjbsOk=" + }, + "is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=" + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "isbinaryfile": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-3.0.2.tgz", + "integrity": "sha1-Sj6XTsDLqQBNP8bN5yCeppNopiE=", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=" + }, + "isomorphic-fetch": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz", + "integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=" + }, + "jasmine-core": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-2.6.1.tgz", + "integrity": "sha1-ZqYc3baZlY42E+3vNGyZb2MR/Ds=", + "dev": true + }, + "jquery": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.2.1.tgz", + "integrity": "sha1-XE2d5lKvbNCncBVKYxu6ErAVx4c=" + }, + "js-base64": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.1.9.tgz", + "integrity": "sha1-8OgK4DmkvWVLXygfyT8EqRSn/M4=" + }, + "js-tokens": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.1.tgz", + "integrity": "sha1-COnxMkhKLEWjCQfp3E1VZ7fxFNc=" + }, + "js-yaml": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.7.0.tgz", + "integrity": "sha1-XJZ93YN6m/3KXy3oQlOr6KHAO4A=" + }, + "jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=" + }, + "json-loader": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/json-loader/-/json-loader-0.5.4.tgz", + "integrity": "sha1-i6oTZaYy9Yo8RtIBdfxgAsluN94=" + }, + "json-stable-stringify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", + "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=" + }, + "json3": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz", + "integrity": "sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE=", + "dev": true + }, + "json5": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=" + }, + "jsonify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", + "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=" + }, + "karma": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/karma/-/karma-1.7.0.tgz", + "integrity": "sha1-b3oaQGRG+i4YfslTmGmPTO5HYmk=", + "dev": true, + "dependencies": { + "lodash": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", + "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=", + "dev": true + } + } + }, + "karma-chai": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/karma-chai/-/karma-chai-0.1.0.tgz", + "integrity": "sha1-vuWtQEAFF4Ea40u5RfdikJEIt5o=", + "dev": true + }, + "karma-chrome-launcher": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-2.1.1.tgz", + "integrity": "sha1-IWh5xorATY1RQOmWGboEtZr9Rs8=", + "dev": true + }, + "karma-cli": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/karma-cli/-/karma-cli-1.0.1.tgz", + "integrity": "sha1-rmw8WKMTodALRRZMRVubhs4X+WA=", + "dev": true + }, + "karma-jasmine": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-1.1.0.tgz", + "integrity": "sha1-IuTAa/mhguUpTR9wXjczgRuBCs8=", + "dev": true + }, + "karma-webpack": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/karma-webpack/-/karma-webpack-2.0.3.tgz", + "integrity": "sha1-Oc6/XKJYATmyf5rmm3iBa5yC+uY=", + "dev": true, + "dependencies": { + "async": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", + "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=", + "dev": true + }, + "lodash": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", + "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=", + "dev": true + }, + "source-map": { + "version": "0.1.43", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz", + "integrity": "sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y=", + "dev": true + } + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=" + }, + "lazy-cache": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", + "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=" + }, + "lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=" + }, + "load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=" + }, + "loader-runner": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.3.0.tgz", + "integrity": "sha1-9IKuqC1UPgeSFwDVpG7yb9rGuKI=" + }, + "loader-utils": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz", + "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=" + }, + "lodash": { + "version": "4.17.4", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", + "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=" + }, + "lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=" + }, + "lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=" + }, + "lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=" + }, + "log4js": { + "version": "0.6.38", + "resolved": "https://registry.npmjs.org/log4js/-/log4js-0.6.38.tgz", + "integrity": "sha1-LElBFmldb7JUgJQ9P8hy5mKlIv0=", + "dev": true, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "dev": true + }, + "semver": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/semver/-/semver-4.3.6.tgz", + "integrity": "sha1-MAvG4OhjdPe6YQaLWx7NV/xlMto=", + "dev": true + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + } + } + }, + "longest": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", + "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=" + }, + "lower-case": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", + "integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw=" + }, + "lru-cache": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.2.4.tgz", + "integrity": "sha1-bGWGGb7PFAMdDQtZSxYELOTcBj0=", + "dev": true + }, + "macaddress": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/macaddress/-/macaddress-0.2.8.tgz", + "integrity": "sha1-WQTcU3w57G2+/q6QIycTX6hRHxI=" + }, + "math-expression-evaluator": { + "version": "1.2.17", + "resolved": "https://registry.npmjs.org/math-expression-evaluator/-/math-expression-evaluator-1.2.17.tgz", + "integrity": "sha1-3oGf282E3M2PrlnGrreWFbnSZqw=" + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "dev": true + }, + "memory-fs": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.3.0.tgz", + "integrity": "sha1-e8xrYp46Q+hx1+Kaymrop/FcuyA=" + }, + "micromatch": { + "version": "2.3.11", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", + "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=" + }, + "miller-rabin": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.0.tgz", + "integrity": "sha1-SmL7HUKTPAVYOYL0xxb2+55sbT0=" + }, + "mime": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.3.6.tgz", + "integrity": "sha1-WR2E02U6awtKO5343lqoEI5y5eA=" + }, + "mime-db": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.27.0.tgz", + "integrity": "sha1-gg9XIpa70g7CXtVeW13oaeVDbrE=", + "dev": true + }, + "mime-types": { + "version": "2.1.15", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.15.tgz", + "integrity": "sha1-pOv1BkCUVpI3uM9wBGd20J/JKu0=", + "dev": true + }, + "minimalistic-assert": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz", + "integrity": "sha1-cCvi3aazf0g2vLP121ZkG2Sh09M=" + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==" + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=" + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "nan": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.6.2.tgz", + "integrity": "sha1-5P805slf37WuzAjeZZb0NgWn20U=", + "optional": true + }, + "ncname": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ncname/-/ncname-1.0.0.tgz", + "integrity": "sha1-W1etGLHKCShk72Kwse2BlPODtxw=" + }, + "negotiator": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", + "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=", + "dev": true + }, + "no-case": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.1.tgz", + "integrity": "sha1-euuhxzpSGEJlVUt9wDuvcg34AIE=" + }, + "node-fetch": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.1.tgz", + "integrity": "sha512-j8XsFGCLw79vWXkZtMSmmLaOk9z5SQ9bV/tkbZVCqvgwzrjAGq66igobLofHtF63NvMTp2WjytpsNTGKa+XRIQ==" + }, + "node-libs-browser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.0.0.tgz", + "integrity": "sha1-o6WeyXAkmFtG6Vg3lkb5bEthZkY=", + "dependencies": { + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + } + } + }, + "normalize-package-data": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.3.8.tgz", + "integrity": "sha1-2Bntoqne29H/pWPqQHHZNngilbs=" + }, + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=" + }, + "normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=" + }, + "normalize-url": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-1.9.1.tgz", + "integrity": "sha1-LMDWazHqIwNkWENuNiDYWVTGbDw=" + }, + "null-check": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/null-check/-/null-check-1.0.0.tgz", + "integrity": "sha1-l33/1xdgErnsMNKjnbXPcqBDnt0=", + "dev": true + }, + "num2fraction": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", + "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=" + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "object-component": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz", + "integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE=", + "dev": true + }, + "object.omit": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", + "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=" + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=" + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true + }, + "optimist": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", + "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", + "dev": true + }, + "options": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/options/-/options-0.0.6.tgz", + "integrity": "sha1-7CLTEoBrtT5zF3Pnza788cZDEo8=", + "dev": true + }, + "os-browserify": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.2.1.tgz", + "integrity": "sha1-Y/xMzuXS13Y9Jrv4YBB45sLgBE8=" + }, + "os-locale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=" + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true + }, + "pako": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", + "integrity": "sha1-8/dSL073gjSNqBYbrZ7P1Rv4OnU=" + }, + "param-case": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz", + "integrity": "sha1-35T9jPZTHs915r75oIWPvHK+Ikc=" + }, + "parse-asn1": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.0.tgz", + "integrity": "sha1-N8T5t+06tlx0gXtfJICTf7+XxxI=" + }, + "parse-glob": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", + "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=" + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=" + }, + "parse5": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-3.0.2.tgz", + "integrity": "sha1-Be/1fw70V3+xRKefi5qWemzERRA=", + "dependencies": { + "@types/node": { + "version": "6.0.78", + "resolved": "https://registry.npmjs.org/@types/node/-/node-6.0.78.tgz", + "integrity": "sha512-+vD6E8ixntRzzZukoF3uP1iV+ZjVN3koTcaeK+BEoc/kSfGbLDIGC7RmCaUgVpUfN6cWvfczFRERCyKM9mkvXg==" + } + } + }, + "parsejson": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/parsejson/-/parsejson-0.0.3.tgz", + "integrity": "sha1-q343WfIJ7OmUN5c/fQ8fZK4OZKs=", + "dev": true + }, + "parseqs": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz", + "integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=", + "dev": true + }, + "parseuri": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz", + "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=", + "dev": true + }, + "parseurl": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.1.tgz", + "integrity": "sha1-yKuMkiO6NIiKpkopeyiFO+wY2lY=" + }, + "path-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", + "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=" + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=" + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "path-parse": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", + "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=", + "dev": true + }, + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=" + }, + "pbkdf2": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.12.tgz", + "integrity": "sha1-vjZ4XFBn6kjYBv+SMojF91C2uKI=" + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=" + }, + "postcss": { + "version": "5.2.17", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", + "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", + "dependencies": { + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=" + } + } + }, + "postcss-calc": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-5.3.1.tgz", + "integrity": "sha1-d7rnypKK2FcW4v2kLyYb98HWW14=" + }, + "postcss-colormin": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-2.2.2.tgz", + "integrity": "sha1-ZjFBfV8OkJo9fsJrJMio0eT5bks=" + }, + "postcss-convert-values": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-2.6.1.tgz", + "integrity": "sha1-u9hZPFwf0uPRwyK7kl3K6Nrk1i0=" + }, + "postcss-discard-comments": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-2.0.4.tgz", + "integrity": "sha1-vv6J+v1bPazlzM5Rt2uBUUvgDj0=" + }, + "postcss-discard-duplicates": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-2.1.0.tgz", + "integrity": "sha1-uavye4isGIFYpesSq8riAmO5GTI=" + }, + "postcss-discard-empty": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-2.1.0.tgz", + "integrity": "sha1-0rS9nVztXr2Nyt52QMfXzX9PkrU=" + }, + "postcss-discard-overridden": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-0.1.1.tgz", + "integrity": "sha1-ix6vVU9ob7KIzYdMVWZ7CqNmjVg=" + }, + "postcss-discard-unused": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/postcss-discard-unused/-/postcss-discard-unused-2.2.3.tgz", + "integrity": "sha1-vOMLLMWR/8Y0Mitfs0ZLbZNPRDM=" + }, + "postcss-filter-plugins": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/postcss-filter-plugins/-/postcss-filter-plugins-2.0.2.tgz", + "integrity": "sha1-bYWGJTTXNaxCDkqFgG4fXUKG2Ew=" + }, + "postcss-merge-idents": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/postcss-merge-idents/-/postcss-merge-idents-2.1.7.tgz", + "integrity": "sha1-TFUwMTwI4dWzu/PSu8dH4njuonA=" + }, + "postcss-merge-longhand": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-2.0.2.tgz", + "integrity": "sha1-I9kM0Sewp3mUkVMyc5A0oaTz1lg=" + }, + "postcss-merge-rules": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-2.1.2.tgz", + "integrity": "sha1-0d9d+qexrMO+VT8OnhDofGG19yE=" + }, + "postcss-message-helpers": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-message-helpers/-/postcss-message-helpers-2.0.0.tgz", + "integrity": "sha1-pPL0+rbk/gAvCu0ABHjN9S+bpg4=" + }, + "postcss-minify-font-values": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-1.0.5.tgz", + "integrity": "sha1-S1jttWZB66fIR0qzUmyv17vey2k=" + }, + "postcss-minify-gradients": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-1.0.5.tgz", + "integrity": "sha1-Xb2hE3NwP4PPtKPqOIHY11/15uE=" + }, + "postcss-minify-params": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-1.2.2.tgz", + "integrity": "sha1-rSzgcTc7lDs9kwo/pZo1jCjW8fM=" + }, + "postcss-minify-selectors": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-2.1.1.tgz", + "integrity": "sha1-ssapjAByz5G5MtGkllCBFDEXNb8=" + }, + "postcss-modules-extract-imports": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.1.0.tgz", + "integrity": "sha1-thTJcgvmgW6u41+zpfqh26agXds=", + "dependencies": { + "postcss": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.2.tgz", + "integrity": "sha1-XE/qWJ8Kw7AMqnWxy8OihBlbfl0=" + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=" + } + } + }, + "postcss-modules-local-by-default": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.2.0.tgz", + "integrity": "sha1-99gMOYxaOT+nlkRmvRlQCn1hwGk=", + "dependencies": { + "postcss": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.2.tgz", + "integrity": "sha1-XE/qWJ8Kw7AMqnWxy8OihBlbfl0=" + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=" + } + } + }, + "postcss-modules-scope": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-1.1.0.tgz", + "integrity": "sha1-1upkmUx5+XtipytCb75gVqGUu5A=", + "dependencies": { + "postcss": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.2.tgz", + "integrity": "sha1-XE/qWJ8Kw7AMqnWxy8OihBlbfl0=" + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=" + } + } + }, + "postcss-modules-values": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-1.3.0.tgz", + "integrity": "sha1-7P+p1+GSUYOJ9CrQ6D9yrsRW6iA=", + "dependencies": { + "postcss": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.2.tgz", + "integrity": "sha1-XE/qWJ8Kw7AMqnWxy8OihBlbfl0=" + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=" + } + } + }, + "postcss-normalize-charset": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-1.1.1.tgz", + "integrity": "sha1-757nEhLX/nWceO0WL2HtYrXLk/E=" + }, + "postcss-normalize-url": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-3.0.8.tgz", + "integrity": "sha1-EI90s/L82viRov+j6kWSJ5/HgiI=" + }, + "postcss-ordered-values": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-2.2.3.tgz", + "integrity": "sha1-7sbCpntsQSqNsgQud/6NpD+VwR0=" + }, + "postcss-reduce-idents": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/postcss-reduce-idents/-/postcss-reduce-idents-2.4.0.tgz", + "integrity": "sha1-wsbSDMlYKE9qv75j92Cb9AkFmtM=" + }, + "postcss-reduce-initial": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-1.0.1.tgz", + "integrity": "sha1-aPgGlfBF0IJjqHmtJA343WT2ROo=" + }, + "postcss-reduce-transforms": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-1.0.4.tgz", + "integrity": "sha1-/3b02CEkN7McKYpC0uFEQCV3GuE=" + }, + "postcss-selector-parser": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-2.2.3.tgz", + "integrity": "sha1-+UN3iGBsPJrO4W/+jYsWKX8nu5A=" + }, + "postcss-svgo": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-2.1.6.tgz", + "integrity": "sha1-tt8YqmE7Zm4TPwittSGcJoSsEI0=" + }, + "postcss-unique-selectors": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-2.0.2.tgz", + "integrity": "sha1-mB1X0p3csz57Hf4f1DuGSfkzyh0=" + }, + "postcss-value-parser": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz", + "integrity": "sha1-h/OPnxj3dKSrTIojL1xc6IcqnRU=" + }, + "postcss-zindex": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/postcss-zindex/-/postcss-zindex-2.2.0.tgz", + "integrity": "sha1-0hCd3AVbka9n/EyzsCWUZjnSryI=" + }, + "preboot": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/preboot/-/preboot-4.5.2.tgz", + "integrity": "sha1-yzSSCZWMK0jX90E3s0mbu+VHKl4=" + }, + "prepend-http": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", + "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=" + }, + "preserve": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", + "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=" + }, + "private": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/private/-/private-0.1.7.tgz", + "integrity": "sha1-aM5eih7woju1cMwoU3tTMqumPvE=" + }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=" + }, + "process-nextick-args": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=" + }, + "prr": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/prr/-/prr-0.0.0.tgz", + "integrity": "sha1-GoS4WQgyVQFBGFPQCB7j+obikmo=" + }, + "public-encrypt": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.0.tgz", + "integrity": "sha1-OfaZ86RlYN1eusvKaTyvfGXBjMY=" + }, + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + }, + "q": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.0.tgz", + "integrity": "sha1-3QG6ydBtMObyGa7LglPunr3DCPE=" + }, + "qjobs": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.1.5.tgz", + "integrity": "sha1-ZZ3p8s+NzCehSBJ28gU3cnI4LnM=", + "dev": true + }, + "qs": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz", + "integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM=", + "dev": true + }, + "query-string": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz", + "integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=" + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" + }, + "querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=" + }, + "randomatic": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-1.1.7.tgz", + "integrity": "sha512-D5JUjPyJbaJDkuAazpVnSfVkLlpeO3wDlPROTMLGKG1zMFNFRgrciKo1ltz/AzNTkqE0HzDx655QOL51N06how==", + "dependencies": { + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=" + } + } + }, + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=" + } + } + }, + "randombytes": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.5.tgz", + "integrity": "sha512-8T7Zn1AhMsQ/HI1SjcCfT/t4ii3eAqco3yOcSzS4mozsOz69lHLsoMXmF9nZgnFanYscnSlUSgs8uZyKzpE6kg==" + }, + "range-parser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", + "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=" + }, + "raw-body": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.2.0.tgz", + "integrity": "sha1-mUl2z2pQlqQRYoQEkvC9xdbn+5Y=", + "dev": true, + "dependencies": { + "iconv-lite": { + "version": "0.4.15", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.15.tgz", + "integrity": "sha1-/iZaIYrGpXz+hUkn6dBMGYJe3es=", + "dev": true + } + } + }, + "raw-loader": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-0.5.1.tgz", + "integrity": "sha1-DD0L6u2KAclm2Xh793goElKpeao=" + }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=" + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=" + }, + "readable-stream": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.0.tgz", + "integrity": "sha512-c7KMXGd4b48nN3OJ1U9qOsn6pXNzf6kLd3kdZCkg2sxAcoiufInqF0XckwEnlrcwuaYwonlNK8GQUIOC/WC7sg==" + }, + "readdirp": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.1.0.tgz", + "integrity": "sha1-TtCtBg3zBzMAxIRANz9y0cxkLXg=" + }, + "recast": { + "version": "0.11.23", + "resolved": "https://registry.npmjs.org/recast/-/recast-0.11.23.tgz", + "integrity": "sha1-RR/TAEqx5N+bTktmN2sqIZEkYtM=", + "dependencies": { + "esprima": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", + "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=" + } + } + }, + "reduce-css-calc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz", + "integrity": "sha1-dHyRTgSWFKTJz7umKYca0dKSdxY=" + }, + "reduce-function-call": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/reduce-function-call/-/reduce-function-call-1.0.2.tgz", + "integrity": "sha1-WiAL+S4ON3UXUv5FsKszD9S2vpk=" + }, + "reflect-metadata": { + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.10.tgz", + "integrity": "sha1-tPg3BEFqytiZiMmxVjXUfgO5NEo=" + }, + "regenerate": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.3.2.tgz", + "integrity": "sha1-0ZQcZ7rUN+G+dkM63Vs4X5WxkmA=" + }, + "regex-cache": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.3.tgz", + "integrity": "sha1-mxpsNdTQ3871cRrmUejp09cRQUU=" + }, + "regexpu-core": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-1.0.0.tgz", + "integrity": "sha1-hqdj9Y7k18L2sQLkdkBQ3n7ZDGs=" + }, + "regjsgen": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", + "integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=" + }, + "regjsparser": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", + "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=" + }, + "relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=" + }, + "remove-trailing-separator": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.0.2.tgz", + "integrity": "sha1-abBi2XhyetFNxrVrpKt3L9jXBRE=" + }, + "repeat-element": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz", + "integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=" + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" + }, + "require-from-string": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-1.2.1.tgz", + "integrity": "sha1-UpyczvJzgK3+yaL5ZbZJu+5jZBg=" + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=" + }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", + "dev": true + }, + "resolve": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.3.3.tgz", + "integrity": "sha1-ZVkHw0aahoDcLeOidaj91paR8OU=", + "dev": true + }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=" + }, + "right-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", + "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=" + }, + "rimraf": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz", + "integrity": "sha1-wjOOxkPfeht/5cVPqG9XQopV8z0=", + "dev": true + }, + "ripemd160": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.1.tgz", + "integrity": "sha1-D0WEKVxTo2KK9+bXmsohzlfRxuc=" + }, + "rxjs": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.4.0.tgz", + "integrity": "sha1-p9sUqxV/nXqsalbmVeejhg05vyY=" + }, + "safe-buffer": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.0.tgz", + "integrity": "sha512-aSLEDudu6OoRr/2rU609gRmnYboRLxgDG1z9o2Q0os7236FwvcqIOO8r8U5JUEwivZOhDaKlFO4SbPTJYyBEyQ==" + }, + "sax": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.2.tgz", + "integrity": "sha1-/YYxojvHgmvvXYcb24c3jJVkeCg=" + }, + "semver": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", + "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=" + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + }, + "set-immediate-shim": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", + "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=" + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" + }, + "setprototypeof": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", + "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=", + "dev": true + }, + "sha.js": { + "version": "2.4.8", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.8.tgz", + "integrity": "sha1-NwaMLEdra69ALRSknGf1l5IfY08=" + }, + "socket.io": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-1.7.3.tgz", + "integrity": "sha1-uK+cq6AJSeVo42nxMn6pvp6iRhs=", + "dev": true, + "dependencies": { + "debug": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", + "integrity": "sha1-QMRT5n5uE8kB3ewxeviYbNqe/4w=", + "dev": true + }, + "ms": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", + "integrity": "sha1-riXPJRKziFodldfwN4aNhDESR2U=", + "dev": true + }, + "object-assign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.0.tgz", + "integrity": "sha1-ejs9DpgGPUP0wD8uiubNUahog6A=", + "dev": true + } + } + }, + "socket.io-adapter": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-0.5.0.tgz", + "integrity": "sha1-y21LuL7IHhB4uZZ3+c7QBGBmu4s=", + "dev": true, + "dependencies": { + "debug": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", + "integrity": "sha1-QMRT5n5uE8kB3ewxeviYbNqe/4w=", + "dev": true + }, + "ms": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", + "integrity": "sha1-riXPJRKziFodldfwN4aNhDESR2U=", + "dev": true + } + } + }, + "socket.io-client": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-1.7.3.tgz", + "integrity": "sha1-sw6GqhDV7zVGYBwJzeR2Xjgdo3c=", + "dev": true, + "dependencies": { + "component-emitter": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", + "dev": true + }, + "debug": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", + "integrity": "sha1-QMRT5n5uE8kB3ewxeviYbNqe/4w=", + "dev": true + }, + "ms": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz", + "integrity": "sha1-riXPJRKziFodldfwN4aNhDESR2U=", + "dev": true + } + } + }, + "socket.io-parser": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-2.3.1.tgz", + "integrity": "sha1-3VMgJRA85Clpcya+/WQAX8/ltKA=", + "dev": true, + "dependencies": { + "debug": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", + "dev": true + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "ms": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", + "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=", + "dev": true + } + } + }, + "sort-keys": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", + "integrity": "sha1-RBttTTRnmPG05J6JIK37oOVD+a0=" + }, + "source-list-map": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-0.1.8.tgz", + "integrity": "sha1-xVCyq1Qn9rPyH1r+rYjE9Vh7IQY=" + }, + "source-map": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "integrity": "sha1-dc449SvwczxafwwRjYEzSiu19BI=" + }, + "source-map-resolve": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.3.1.tgz", + "integrity": "sha1-YQ9hIqRFuN1RU1oqcbeD38Ekh2E=" + }, + "source-map-support": { + "version": "0.4.15", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.15.tgz", + "integrity": "sha1-AyAt9lwG0r2MfsI2KhkwVv7407E=" + }, + "source-map-url": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.3.0.tgz", + "integrity": "sha1-fsrxO1e80J2opAxdJp2zN5nUqvk=" + }, + "spdx-correct": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-1.0.2.tgz", + "integrity": "sha1-SzBz2TP/UfORLwOsVRlJikFQ20A=" + }, + "spdx-expression-parse": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz", + "integrity": "sha1-m98vIOH0DtRH++JzJmGR/O1RYmw=" + }, + "spdx-license-ids": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz", + "integrity": "sha1-yd96NCRZSt5r0RkA1ZZpbcBrrFc=" + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" + }, + "statuses": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", + "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=" + }, + "stream-browserify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz", + "integrity": "sha1-ZiZu5fm9uZQKTkUUyvtDu3Hlyds=" + }, + "stream-http": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.7.2.tgz", + "integrity": "sha512-c0yTD2rbQzXtSsFSVhtpvY/vS6u066PcXOX9kBB3mSO76RiUQzL340uJkGBWnlBg4/HZzqiUXtaVA7wcRcJgEw==" + }, + "strict-uri-encode": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", + "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=" + }, + "string_decoder": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.2.tgz", + "integrity": "sha1-sp4fThEl+pehA4K4pTNze3SR4Xk=", + "dependencies": { + "safe-buffer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.0.1.tgz", + "integrity": "sha1-0mPKVGls2KMGtcplUekt5XkY++c=" + } + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=" + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=" + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=" + }, + "style-loader": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-0.17.0.tgz", + "integrity": "sha1-6CVLzNt690vVgnTjYQe01atN8xA=", + "dependencies": { + "loader-utils": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", + "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=" + } + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" + }, + "svgo": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-0.7.2.tgz", + "integrity": "sha1-n1dyQTlSE1xv779Ar+ak+qiLS7U=" + }, + "symbol-observable": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.4.tgz", + "integrity": "sha1-Kb9hXUqnEhvdiYsi1LP5vE4qoD0=" + }, + "tapable": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-0.2.6.tgz", + "integrity": "sha1-IGvo4YiGC1FEJTdebxrom/sB/Y0=" + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" + }, + "timers-browserify": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.2.tgz", + "integrity": "sha1-q0iDz1l9zVCvIRNJoA+8pWrIa4Y=" + }, + "tmp": { + "version": "0.0.31", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.31.tgz", + "integrity": "sha1-jzirlDjhcxXl29izZX6L+yd65Kc=", + "dev": true + }, + "to-array": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz", + "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA=", + "dev": true + }, + "to-arraybuffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", + "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=" + }, + "to-string-loader": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/to-string-loader/-/to-string-loader-1.1.5.tgz", + "integrity": "sha1-e3qheJG3u0lHp6Eb+wO1/enG5pU=" + }, + "tty-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", + "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=" + }, + "type-detect": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-1.0.0.tgz", + "integrity": "sha1-diIXzAbbJY7EiQihKY6LlRIejqI=", + "dev": true + }, + "type-is": { + "version": "1.6.15", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.15.tgz", + "integrity": "sha1-yrEPtJCeRByChC6v4a1kbIGARBA=", + "dev": true + }, + "typescript": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.3.2.tgz", + "integrity": "sha1-8PBF4Zb2mnLwayX9O9OdAcPOmYQ=" + }, + "uglify-js": { + "version": "3.0.18", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.0.18.tgz", + "integrity": "sha512-0M/KeXO8bPYtlqnwIYpO4R6om1mrScMzPuWn2UPfUYOaowIhQmmFpL9Q5tlD18ulKLRKD12GQ0IiYDKJS/si1w==" + }, + "uglify-to-browserify": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", + "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", + "optional": true + }, + "ultron": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz", + "integrity": "sha1-rOEWq1V80Zc4ak6I9GhTeMiy5Po=", + "dev": true + }, + "uniq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", + "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=" + }, + "uniqid": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/uniqid/-/uniqid-4.1.1.tgz", + "integrity": "sha1-iSIN32t1GuUrX3JISGNShZa7hME=" + }, + "uniqs": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz", + "integrity": "sha1-/+3ks2slKQaW5uFl1KWe25mOawI=" + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + }, + "upper-case": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", + "integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=" + }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=" + }, + "url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "dependencies": { + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" + } + } + }, + "url-loader": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-0.5.8.tgz", + "integrity": "sha1-uRg7GAHg+EdxhnNnMEC8ncHHFcU=", + "dependencies": { + "loader-utils": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", + "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=" + } + } + }, + "useragent": { + "version": "2.1.13", + "resolved": "https://registry.npmjs.org/useragent/-/useragent-2.1.13.tgz", + "integrity": "sha1-u6Q+iqJNXOuDwpN0c+EC4h33TBA=", + "dev": true + }, + "util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "dependencies": { + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=" + } + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "utils-merge": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.0.tgz", + "integrity": "sha1-ApT7kiu5N1FTVBxPcJYjHyh8ivg=" + }, + "validate-npm-package-license": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz", + "integrity": "sha1-KAS6vnEq0zeUWaz74kdGqywwP7w=" + }, + "vendors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.1.tgz", + "integrity": "sha1-N61zyO5Bf7PVgOeFMSMH0nSEfyI=" + }, + "vm-browserify": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", + "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=" + }, + "void-elements": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", + "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=", + "dev": true + }, + "watchpack": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.3.1.tgz", + "integrity": "sha1-fYaTkHsozmAT5/NhCqKhrPB9rYc=" + }, + "webpack": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-2.5.1.tgz", + "integrity": "sha1-YXQvDPivVVuHRgqc2Lui8ePuL84=", + "dependencies": { + "memory-fs": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", + "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=" + }, + "source-list-map": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-1.1.2.tgz", + "integrity": "sha1-mIkBnRAkzOVc3AaUmDN+9hhqEaE=" + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=" + }, + "uglify-js": { + "version": "2.8.29", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", + "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", + "dependencies": { + "yargs": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", + "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=" + } + } + }, + "webpack-sources": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-0.2.3.tgz", + "integrity": "sha1-F8Yr+vE8cH+dAsR54Nzd6DgGl/s=" + } + } + }, + "webpack-dev-middleware": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-1.10.2.tgz", + "integrity": "sha1-LiUs4d+wINvaHMs33ybzCrAU29E=", + "dependencies": { + "memory-fs": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", + "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=" + } + } + }, + "webpack-hot-middleware": { + "version": "2.18.0", + "resolved": "https://registry.npmjs.org/webpack-hot-middleware/-/webpack-hot-middleware-2.18.0.tgz", + "integrity": "sha1-oWu1Nbg6aslKeKxevOTzBZ6CdNM=" + }, + "webpack-merge": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-4.1.0.tgz", + "integrity": "sha1-atciI7PguDflMeRZfBmfkJNhUR4=" + }, + "webpack-node-externals": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/webpack-node-externals/-/webpack-node-externals-1.6.0.tgz", + "integrity": "sha1-Iyxi7GCSsQBjWj0p2DwXRxKN+b0=" + }, + "webpack-sources": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-0.1.5.tgz", + "integrity": "sha1-qh86vw8NdNtxEcQOUAuE+WZkB1A=" + }, + "whatwg-fetch": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.3.tgz", + "integrity": "sha1-nITsLc9oGH/wC8ZOEnS0QhduHIQ=" + }, + "whet.extend": { + "version": "0.9.9", + "resolved": "https://registry.npmjs.org/whet.extend/-/whet.extend-0.9.9.tgz", + "integrity": "sha1-+HfVv2SMl+WqVC+twW1qJZucEaE=" + }, + "which": { + "version": "1.2.14", + "resolved": "https://registry.npmjs.org/which/-/which-1.2.14.tgz", + "integrity": "sha1-mofEN48D6CfOyvGs31bHNsAcFOU=", + "dev": true + }, + "which-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", + "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=" + }, + "window-size": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", + "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=" + }, + "wordwrap": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", + "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=" + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=" + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "ws": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-1.1.2.tgz", + "integrity": "sha1-iiRPoFJAHgjJiGz0SoUYnh/UBn8=", + "dev": true + }, + "wtf-8": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wtf-8/-/wtf-8-1.0.0.tgz", + "integrity": "sha1-OS2LotDxw00e4tYw8V0O+2jhBIo=", + "dev": true + }, + "xhr2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/xhr2/-/xhr2-0.1.4.tgz", + "integrity": "sha1-f4dliEdxbbUCYyOBL4GMras4el8=" + }, + "xml-char-classes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/xml-char-classes/-/xml-char-classes-1.0.0.tgz", + "integrity": "sha1-ZGV4SKIP/F31g6Qq2KJ3tFErvE0=" + }, + "xmlhttprequest-ssl": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.3.tgz", + "integrity": "sha1-GFqIjATspGw+QHDZn3tJ3jUomS0=", + "dev": true + }, + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" + }, + "y18n": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=" + }, + "yargs": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-6.6.0.tgz", + "integrity": "sha1-eC7CHvQDNF+DCoCMo9UTr1YGUgg=", + "dependencies": { + "camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=" + }, + "cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=" + } + } + }, + "yargs-parser": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-4.2.1.tgz", + "integrity": "sha1-KczqwNxPA8bIe0qfIX3RjJ90hxw=", + "dependencies": { + "camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=" + } + } + }, + "yeast": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz", + "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=", + "dev": true + }, + "zone.js": { + "version": "0.8.10", + "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.8.10.tgz", + "integrity": "sha1-bRtpZJLAKc2+gI5Z6Hu9lJG5iqg=" + } + } +} diff --git a/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/package.json b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/package.json new file mode 100644 index 000000000000..ea6e09053394 --- /dev/null +++ b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/package.json @@ -0,0 +1,64 @@ +{ + "name": "spaservicessampleapp", + "version": "0.0.0", + "scripts": { + "build": "npm run build:vendor && npm run build:custom", + "build:custom": "webpack", + "build:vendor": "webpack --config webpack.config.vendor.js", + "test": "karma start ClientApp/test/karma.conf.js" + }, + "dependencies": { + "@angular/animations": "4.1.2", + "@angular/common": "4.1.2", + "@angular/compiler": "4.1.2", + "@angular/core": "4.1.2", + "@angular/forms": "4.1.2", + "@angular/http": "4.1.2", + "@angular/platform-browser": "4.1.2", + "@angular/platform-browser-dynamic": "4.1.2", + "@angular/platform-server": "4.1.2", + "@angular/router": "4.1.2", + "@types/node": "7.0.18", + "angular2-template-loader": "0.6.2", + "aspnet-prerendering": "^2.0.5", + "aspnet-webpack": "^1.0.29", + "awesome-typescript-loader": "3.1.3", + "bootstrap": "3.3.7", + "css": "2.2.1", + "css-loader": "0.28.1", + "es6-shim": "0.35.3", + "event-source-polyfill": "0.0.9", + "expose-loader": "0.7.3", + "extract-text-webpack-plugin": "2.1.0", + "file-loader": "0.11.1", + "html-loader": "0.4.5", + "isomorphic-fetch": "2.2.1", + "jquery": "3.2.1", + "json-loader": "0.5.4", + "preboot": "4.5.2", + "raw-loader": "0.5.1", + "reflect-metadata": "0.1.10", + "rxjs": "5.4.0", + "style-loader": "0.17.0", + "to-string-loader": "1.1.5", + "typescript": "2.3.2", + "url-loader": "0.5.8", + "webpack": "2.5.1", + "webpack-hot-middleware": "2.18.0", + "webpack-merge": "4.1.0", + "zone.js": "0.8.10" + }, + "devDependencies": { + "@types/chai": "3.5.2", + "@types/jasmine": "2.5.47", + "angular-router-loader": "^0.6.0", + "chai": "3.5.0", + "jasmine-core": "2.6.1", + "karma": "1.7.0", + "karma-chai": "0.1.0", + "karma-chrome-launcher": "2.1.1", + "karma-cli": "1.0.1", + "karma-jasmine": "1.1.0", + "karma-webpack": "2.0.3" + } +} diff --git a/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/tsconfig.json b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/tsconfig.json new file mode 100644 index 000000000000..94b22fcec7e5 --- /dev/null +++ b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "moduleResolution": "node", + "target": "es5", + "sourceMap": true, + "experimentalDecorators": true, + "emitDecoratorMetadata": true, + "skipDefaultLibCheck": true, + "lib": [ "es6", "dom" ], + "types": [ "node" ] + }, + "exclude": [ "bin", "node_modules" ], + "atom": { "rewriteTsconfig": false } +} diff --git a/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/web.config b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/web.config new file mode 100644 index 000000000000..a8d6672758d6 --- /dev/null +++ b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/web.config @@ -0,0 +1,14 @@ + + + + + + + + + + + + diff --git a/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/webpack.config.js b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/webpack.config.js new file mode 100644 index 000000000000..3cf9455e6fbb --- /dev/null +++ b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/webpack.config.js @@ -0,0 +1,71 @@ +const path = require('path'); +const webpack = require('webpack'); +const merge = require('webpack-merge'); +const CheckerPlugin = require('awesome-typescript-loader').CheckerPlugin; + +module.exports = (env) => { + // Configuration in common to both client-side and server-side bundles + const isDevBuild = !(env && env.prod); + const sharedConfig = { + stats: { modules: false }, + context: __dirname, + resolve: { extensions: [ '.js', '.ts' ] }, + output: { + filename: '[name].js', + publicPath: '/dist/' // Webpack dev middleware, if enabled, handles requests for this URL prefix + }, + module: { + rules: [ + { test: /\.ts$/, include: /ClientApp/, use: ['awesome-typescript-loader?silent=true', 'angular2-template-loader', 'angular-router-loader'] }, + { test: /\.html$/, use: 'html-loader?minimize=false' }, + { test: /\.css$/, use: [ 'to-string-loader', isDevBuild ? 'css-loader' : 'css-loader?minimize' ] }, + { test: /\.(png|jpg|jpeg|gif|svg)$/, use: 'url-loader?limit=25000' } + ] + }, + plugins: [new CheckerPlugin()] + }; + + // Configuration for client-side bundle suitable for running in browsers + const clientBundleOutputDir = './wwwroot/dist'; + const clientBundleConfig = merge(sharedConfig, { + entry: { 'main-client': './ClientApp/boot-client.ts' }, + output: { path: path.join(__dirname, clientBundleOutputDir) }, + plugins: [ + new webpack.DllReferencePlugin({ + context: __dirname, + manifest: require('./wwwroot/dist/vendor-manifest.json') + }) + ].concat(isDevBuild ? [ + // Plugins that apply in development builds only + new webpack.SourceMapDevToolPlugin({ + filename: '[file].map', // Remove this line if you prefer inline source maps + moduleFilenameTemplate: path.relative(clientBundleOutputDir, '[resourcePath]') // Point sourcemap entries to the original file locations on disk + }) + ] : [ + // Plugins that apply in production builds only + new webpack.optimize.UglifyJsPlugin() + ]) + }); + + // Configuration for server-side (prerendering) bundle suitable for running in Node + const serverBundleConfig = merge(sharedConfig, { + resolve: { mainFields: ['main'] }, + entry: { 'main-server': './ClientApp/boot-server.ts' }, + plugins: [ + new webpack.DllReferencePlugin({ + context: __dirname, + manifest: require('./ClientApp/dist/vendor-manifest.json'), + sourceType: 'commonjs2', + name: './vendor' + }) + ], + output: { + libraryTarget: 'commonjs', + path: path.join(__dirname, './ClientApp/dist') + }, + target: 'node', + devtool: 'inline-source-map' + }); + + return [clientBundleConfig, serverBundleConfig]; +}; diff --git a/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/webpack.config.vendor.js b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/webpack.config.vendor.js new file mode 100644 index 000000000000..7b0c3017cf10 --- /dev/null +++ b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/webpack.config.vendor.js @@ -0,0 +1,88 @@ +const path = require('path'); +const webpack = require('webpack'); +const ExtractTextPlugin = require('extract-text-webpack-plugin'); +const merge = require('webpack-merge'); + +module.exports = (env) => { + const extractCSS = new ExtractTextPlugin('vendor.css'); + const isDevBuild = !(env && env.prod); + const sharedConfig = { + stats: { modules: false }, + resolve: { extensions: [ '.js' ] }, + module: { + rules: [ + { test: /\.(png|woff|woff2|eot|ttf|svg)(\?|$)/, use: 'url-loader?limit=100000' } + ] + }, + entry: { + vendor: [ + '@angular/animations', + '@angular/common', + '@angular/compiler', + '@angular/core', + '@angular/forms', + '@angular/http', + '@angular/platform-browser', + '@angular/platform-browser-dynamic', + '@angular/router', + 'bootstrap', + 'bootstrap/dist/css/bootstrap.css', + 'es6-shim', + 'es6-promise', + 'event-source-polyfill', + 'jquery', + 'zone.js', + ] + }, + output: { + publicPath: '/dist/', + filename: '[name].js', + library: '[name]_[hash]' + }, + plugins: [ + new webpack.ProvidePlugin({ $: 'jquery', jQuery: 'jquery' }), // Maps these identifiers to the jQuery package (because Bootstrap expects it to be a global variable) + new webpack.ContextReplacementPlugin(/\@angular\b.*\b(bundles|linker)/, path.join(__dirname, './ClientApp')), // Workaround for https://github.com/angular/angular/issues/11580 + new webpack.ContextReplacementPlugin(/angular(\\|\/)core(\\|\/)@angular/, path.join(__dirname, './ClientApp')), // Workaround for https://github.com/angular/angular/issues/14898 + new webpack.IgnorePlugin(/^vertx$/) // Workaround for https://github.com/stefanpenner/es6-promise/issues/100 + ] + }; + + const clientBundleConfig = merge(sharedConfig, { + output: { path: path.join(__dirname, 'wwwroot', 'dist') }, + module: { + rules: [ + { test: /\.css(\?|$)/, use: extractCSS.extract({ use: isDevBuild ? 'css-loader' : 'css-loader?minimize' }) } + ] + }, + plugins: [ + extractCSS, + new webpack.DllPlugin({ + path: path.join(__dirname, 'wwwroot', 'dist', '[name]-manifest.json'), + name: '[name]_[hash]' + }) + ].concat(isDevBuild ? [] : [ + new webpack.optimize.UglifyJsPlugin() + ]) + }); + + const serverBundleConfig = merge(sharedConfig, { + target: 'node', + resolve: { mainFields: ['main'] }, + output: { + path: path.join(__dirname, 'ClientApp', 'dist'), + libraryTarget: 'commonjs2', + }, + module: { + rules: [ { test: /\.css(\?|$)/, use: ['to-string-loader', isDevBuild ? 'css-loader' : 'css-loader?minimize' ] } ] + }, + entry: { vendor: ['aspnet-prerendering'] }, + plugins: [ + new webpack.DllPlugin({ + path: path.join(__dirname, 'ClientApp', 'dist', '[name]-manifest.json'), + name: '[name]_[hash]' + }) + ] + }); + + return [clientBundleConfig, serverBundleConfig]; +} From f5c1ff803db4417ae56e29c46f4a57f535266e81 Mon Sep 17 00:00:00 2001 From: Scott Addie Date: Tue, 20 Jun 2017 16:27:32 -0500 Subject: [PATCH 02/23] Add Tag Helpers text and code --- aspnetcore/client-side/spa-services.md | 62 +++++++------------ .../ClientApp/boot-server.ts | 46 +++++++++++++- .../Views/Home/Index.cshtml | 10 ++- 3 files changed, 75 insertions(+), 43 deletions(-) diff --git a/aspnetcore/client-side/spa-services.md b/aspnetcore/client-side/spa-services.md index 78977f6762a9..13d8670ab90c 100644 --- a/aspnetcore/client-side/spa-services.md +++ b/aspnetcore/client-side/spa-services.md @@ -45,60 +45,40 @@ With the required packages installed, the Tag Helpers are made discoverable via These Tag Helpers abstract away the intricacies of communicating directly with low-level APIs by leveraging an HTML-like syntax within the Razor view: -[!code-csharp[Main](../client-side/spa-services/sample/SpaServicesSampleApp/Views/Home/Index.cshtml?highlight=5)] +[!code-html[Main](../client-side/spa-services/sample/SpaServicesSampleApp/Views/Home/Index.cshtml?range=5)] #### The asp-prerender-module Tag Helper -The `asp-prerender-module` Tag Helper executes `ClientApp/dist/main-server.js` on the server via Node.js. To clarify, `main-server.js` file is an artifact of the [Webpack](http://webpack.github.io/) build process' TypeScript-to-JavaScript transpilation task. Webpack defines an entry point alias of `main-server`; and, traversal of the dependency graph for this alias begins at the `ClientApp/boot-server.ts` file: +The `asp-prerender-module` Tag Helper, used in the previous example, executes `ClientApp/dist/main-server.js` on the server via Node.js. To clarify, `main-server.js` file is an artifact of the [Webpack](http://webpack.github.io/) build process' TypeScript-to-JavaScript transpilation task. Webpack defines an entry point alias of `main-server`; and, traversal of the dependency graph for this alias begins at the `ClientApp/boot-server.ts` file: [!code-javascript[Main](../client-side/spa-services/sample/SpaServicesSampleApp/webpack.config.js?range=53)] The `ClientApp/boot-server.ts` file utilizes the `createServerRenderer` function and `RenderResult` type of the `aspnet-prerendering` npm package to configure server rendering via Node.js. The HTML markup destined for server-side rendering is passed to a `resolve` function call, which is wrapped in a JavaScript `Promise` object of type `RenderResult`. The `Promise` object is significant in that it asynchronously supplies the HTML markup to the page for injection in the placeholder element. -[!code-javascript[Main](../client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/boot-server.ts?range=6,10-)] +[!code-javascript[Main](../client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/boot-server.ts?range=6,10-34,79-)] #### The asp-prerender-data Tag Helper Sometimes contextual information must be passed as arguments from the Razor view to the server-side JavaScript. To satisfy this requirement, the `asp-prerender-data` Tag Helper is used in conjunction with the aforementioned `asp-prerender-module` Tag Helper. For example, the following markup passes user data to the `main-server` module: -```html -
-``` - -The received data is serialized using the built-in JSON serializer and is stored in the `params.data` object. The following is an example of using the data from the `param.data` object to construct some simple markup: -[again, best to import a snippet from a working sample] - -```javascript -var prerendering = require('aspnet-prerendering'); - -module.exports = prerendering.createServerRenderer(function (params) { - return new Promise(function (resolve, reject) { - var result = '

Good Morning, ' + params.data.userName + '!

'; - resolve({ html: result }); - }); -}); -``` - -*PascalCase* notation is used for property names in the `asp-prerender-data` Tag Helper; however, in JavaScript they are used in **camelCase**. The default JSON serialization configuration is responsible for this difference. - -Data can be passed back to the view. ~While returning the `Promise` object that resolves markup, use the `globals` property to send data back to the view:~ Maybe something like, In the preceding code, the `Promise` object bla bla bla. The following code uses the `globals` property to send data to the view: - -```javascript -resolve({ - html: result, - globals: { - postList: [ - 'Introduction to ASP.NET Core', - 'Making apps with Angular and ASP.NET Core' - ] - } -}); -``` - -Each of the properties set inside the `globals` object will create individual, globally-accessible JavaScript variables with the same property names and associated values. +[!code-html[Main](../client-side/spa-services/sample/SpaServicesSampleApp/Views/Home/Index.cshtml?range=9-12)] + +The received `UserName` argument is serialized using the built-in JSON serializer and is stored in the `params.data` object. The data is used to construct a personalized greeting within an `h1` element: + +[!code-javascript[Main](../client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/boot-server.ts?range=6,10-21,38-52,79-)] + +Property names passed in the `asp-prerender-data` Tag Helper are represented with *PascalCase* notation. Contrast that to JavaScript, where the same property names are represented with *camelCase*. The default JSON serialization configuration is responsible for this difference. + +Data can be passed from the server to the view by hydrating the `globals` property passed to the `resolve` function: + +[!code-javascript[Main](../client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/boot-server.ts?range=6,10-21,57-77,79-)] + +The `postList` array defined inside the `globals` object is attached to the browser's global `window` object. This capability eliminates duplication of effort, particularly as it pertains to loading the same data once on the server and again on the client. + + + + +<> ## Webpack middleware diff --git a/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/boot-server.ts b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/boot-server.ts index 61e7472c304a..634331793990 100644 --- a/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/boot-server.ts +++ b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/boot-server.ts @@ -18,7 +18,7 @@ export default createServerRenderer(params => { const appRef = moduleRef.injector.get(ApplicationRef); const state = moduleRef.injector.get(PlatformState); const zone = moduleRef.injector.get(NgZone); - + /* return new Promise((resolve, reject) => { zone.onError.subscribe(errorInfo => reject(errorInfo)); appRef.isStable.first(isStable => isStable).subscribe(() => { @@ -32,5 +32,49 @@ export default createServerRenderer(params => { }); }); }); + */ + // Example of accessing arguments passed from the Tag Helper + /* + return new Promise((resolve, reject) => { + const result = `

Hello, ${params.data.userName}

`; + + zone.onError.subscribe(errorInfo => reject(errorInfo)); + appRef.isStable.first(isStable => isStable).subscribe(() => { + // Because 'onStable' fires before 'onError', we have to delay slightly before + // completing the request in case there's an error to report + setImmediate(() => { + resolve({ + html: result + }); + moduleRef.destroy(); + }); + }); + }); + */ + + // Example of attaching property to browser's "window" object + + return new Promise((resolve, reject) => { + const result = `

Hello, ${params.data.userName}

`; + + zone.onError.subscribe(errorInfo => reject(errorInfo)); + appRef.isStable.first(isStable => isStable).subscribe(() => { + // Because 'onStable' fires before 'onError', we have to delay slightly before + // completing the request in case there's an error to report + setImmediate(() => { + resolve({ + html: result, + globals: { + postList: [ + 'Introduction to ASP.NET Core', + 'Making apps with Angular and ASP.NET Core' + ] + } + }); + moduleRef.destroy(); + }); + }); + }); + }); }); \ No newline at end of file diff --git a/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Views/Home/Index.cshtml b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Views/Home/Index.cshtml index 008eb8731f47..23fef498058c 100644 --- a/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Views/Home/Index.cshtml +++ b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Views/Home/Index.cshtml @@ -2,7 +2,15 @@ ViewData["Title"] = "Home Page"; } -Loading... +@*Loading...*@ + +@* Example of asp-prerender-data Tag Helper passing data to server-side JavaScript *@ + +Loading... + @section scripts { From d14aa67aafa591e24acf570275c0148423a8b049 Mon Sep 17 00:00:00 2001 From: Scott Addie Date: Tue, 20 Jun 2017 16:42:47 -0500 Subject: [PATCH 03/23] Add screenshot of global postList variable --- aspnetcore/client-side/spa-services.md | 5 +---- .../spa-services/_static/global_variable.png | Bin 0 -> 20139 bytes .../_static/global_variable_original.png | Bin 0 -> 23957 bytes 3 files changed, 1 insertion(+), 4 deletions(-) create mode 100644 aspnetcore/client-side/spa-services/_static/global_variable.png create mode 100644 aspnetcore/client-side/spa-services/_static/global_variable_original.png diff --git a/aspnetcore/client-side/spa-services.md b/aspnetcore/client-side/spa-services.md index 13d8670ab90c..c96e4b140abc 100644 --- a/aspnetcore/client-side/spa-services.md +++ b/aspnetcore/client-side/spa-services.md @@ -75,10 +75,7 @@ Data can be passed from the server to the view by hydrating the `globals` proper The `postList` array defined inside the `globals` object is attached to the browser's global `window` object. This capability eliminates duplication of effort, particularly as it pertains to loading the same data once on the server and again on the client. - - - -<> +![global postList variable attached to window object](spa-services/_static/global_variable.png) ## Webpack middleware diff --git a/aspnetcore/client-side/spa-services/_static/global_variable.png b/aspnetcore/client-side/spa-services/_static/global_variable.png new file mode 100644 index 0000000000000000000000000000000000000000..18f68f29b49d07ae823fd90d39cc7a9f8d5341a4 GIT binary patch literal 20139 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 DYO{}Z literal 0 HcmV?d00001 From 04d665c919a82deb7a5687197f4e9934a7b8e536 Mon Sep 17 00:00:00 2001 From: Scott Addie Date: Tue, 20 Jun 2017 16:47:04 -0500 Subject: [PATCH 04/23] Restore Tag Helpers code sample to original state --- .../sample/SpaServicesSampleApp/ClientApp/boot-server.ts | 8 ++++---- .../sample/SpaServicesSampleApp/Views/Home/Index.cshtml | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/boot-server.ts b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/boot-server.ts index 634331793990..144ee7b0ea54 100644 --- a/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/boot-server.ts +++ b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/boot-server.ts @@ -18,7 +18,7 @@ export default createServerRenderer(params => { const appRef = moduleRef.injector.get(ApplicationRef); const state = moduleRef.injector.get(PlatformState); const zone = moduleRef.injector.get(NgZone); - /* + return new Promise((resolve, reject) => { zone.onError.subscribe(errorInfo => reject(errorInfo)); appRef.isStable.first(isStable => isStable).subscribe(() => { @@ -32,7 +32,7 @@ export default createServerRenderer(params => { }); }); }); - */ + // Example of accessing arguments passed from the Tag Helper /* return new Promise((resolve, reject) => { @@ -53,7 +53,7 @@ export default createServerRenderer(params => { */ // Example of attaching property to browser's "window" object - + /* return new Promise((resolve, reject) => { const result = `

Hello, ${params.data.userName}

`; @@ -75,6 +75,6 @@ export default createServerRenderer(params => { }); }); }); - + */ }); }); \ No newline at end of file diff --git a/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Views/Home/Index.cshtml b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Views/Home/Index.cshtml index 23fef498058c..2b7be1ccf65b 100644 --- a/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Views/Home/Index.cshtml +++ b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Views/Home/Index.cshtml @@ -2,15 +2,15 @@ ViewData["Title"] = "Home Page"; } -@*Loading...*@ +Loading... @* Example of asp-prerender-data Tag Helper passing data to server-side JavaScript *@ - +@* Loading... - +*@ @section scripts { From 04df38b892c5c5cf4d037c774b400454994dba01 Mon Sep 17 00:00:00 2001 From: Scott Addie Date: Tue, 20 Jun 2017 16:50:02 -0500 Subject: [PATCH 05/23] Adjust Tag Helpers heading styles --- aspnetcore/client-side/spa-services.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/aspnetcore/client-side/spa-services.md b/aspnetcore/client-side/spa-services.md index c96e4b140abc..caf16d0c148f 100644 --- a/aspnetcore/client-side/spa-services.md +++ b/aspnetcore/client-side/spa-services.md @@ -33,7 +33,7 @@ SpaServices is not required to develop SPAs with ASP.NET Core. Since SpaServices A universal (also known as isomorphic) application is a JavaScript application capable of running both on the server and the client. Angular, React, and other popular frameworks provide a universal platform for this application development style. The idea is to first render the framework components on the server-side via Node.js and then delegate further execution to the client. SpaServices' Tag Helpers make the implementation of server-side prerendering simple by invoking the JavaScript functions on the server for you. -#### Tag Helpers +### Tag Helpers SpaServices provides a suite of ASP.NET Core [Tag Helpers](xref:mvc/views/tag-helpers/intro) to support the prerendering process. Using them requires installation of the following mutually inclusive prerequisites: 1. [Microsoft.AspNetCore.SpaServices](http://www.nuget.org/packages/Microsoft.AspNetCore.SpaServices/) NuGet package @@ -47,7 +47,7 @@ These Tag Helpers abstract away the intricacies of communicating directly with l [!code-html[Main](../client-side/spa-services/sample/SpaServicesSampleApp/Views/Home/Index.cshtml?range=5)] -#### The asp-prerender-module Tag Helper +### The asp-prerender-module Tag Helper The `asp-prerender-module` Tag Helper, used in the previous example, executes `ClientApp/dist/main-server.js` on the server via Node.js. To clarify, `main-server.js` file is an artifact of the [Webpack](http://webpack.github.io/) build process' TypeScript-to-JavaScript transpilation task. Webpack defines an entry point alias of `main-server`; and, traversal of the dependency graph for this alias begins at the `ClientApp/boot-server.ts` file: @@ -57,7 +57,7 @@ The `ClientApp/boot-server.ts` file utilizes the `createServerRenderer` function [!code-javascript[Main](../client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/boot-server.ts?range=6,10-34,79-)] -#### The asp-prerender-data Tag Helper +### The asp-prerender-data Tag Helper Sometimes contextual information must be passed as arguments from the Razor view to the server-side JavaScript. To satisfy this requirement, the `asp-prerender-data` Tag Helper is used in conjunction with the aforementioned `asp-prerender-module` Tag Helper. For example, the following markup passes user data to the `main-server` module: From 4fcb40f23b09f78805df3e6927078314fd8f25e6 Mon Sep 17 00:00:00 2001 From: Scott Addie Date: Wed, 21 Jun 2017 11:43:50 -0500 Subject: [PATCH 06/23] Add Webpack Dev Middleware code and text --- aspnetcore/client-side/spa-services.md | 31 +++++++++++++++---- .../sample/SpaServicesSampleApp/Startup.cs | 5 +++ 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/aspnetcore/client-side/spa-services.md b/aspnetcore/client-side/spa-services.md index caf16d0c148f..f66fb8c24f6f 100644 --- a/aspnetcore/client-side/spa-services.md +++ b/aspnetcore/client-side/spa-services.md @@ -43,7 +43,7 @@ With the required packages installed, the Tag Helpers are made discoverable via [!code-csharp[Main](../client-side/spa-services/sample/SpaServicesSampleApp/Views/_ViewImports.cshtml?highlight=3)] -These Tag Helpers abstract away the intricacies of communicating directly with low-level APIs by leveraging an HTML-like syntax within the Razor view: +These Tag Helpers abstract away the intricacies of communicating directly with low-level APIs by leveraging an HTML-like syntax inside the Razor view: [!code-html[Main](../client-side/spa-services/sample/SpaServicesSampleApp/Views/Home/Index.cshtml?range=5)] @@ -73,22 +73,41 @@ Data can be passed from the server to the view by hydrating the `globals` proper [!code-javascript[Main](../client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/boot-server.ts?range=6,10-21,57-77,79-)] -The `postList` array defined inside the `globals` object is attached to the browser's global `window` object. This capability eliminates duplication of effort, particularly as it pertains to loading the same data once on the server and again on the client. +The `postList` array defined inside the `globals` object is attached to the browser's global `window` object. This variable hoisting to global scope eliminates duplication of effort, particularly as it pertains to loading the same data once on the server and again on the client. ![global postList variable attached to window object](spa-services/_static/global_variable.png) -## Webpack middleware +## Webpack Dev Middleware -### MapSpaFallbackRoute +[Webpack Dev Middleware](https://webpack.github.io/docs/webpack-dev-middleware.html) introduces a streamlined development workflow whereby Webpack builds resources on demand. The middleware automatically compiles and serves client-side resources when a page is reloaded in the browser. The alternate, less efficient approach is to manually run the following npm script when a third-party dependency or your custom code changes: -### UseWebpackDevMiddleware +[!code-json[Main](../client-side/spa-services/sample/SpaServicesSampleApp/package.json?range=5)] + +Using Webpack Dev Middleware requires installation of the following mutually inclusive prerequisites: +1. [Microsoft.AspNetCore.SpaServices](http://www.nuget.org/packages/Microsoft.AspNetCore.SpaServices/) NuGet package +1. [aspnet-webpack](https://www.npmjs.com/package/aspnet-webpack) npm package + +Webpack Dev Middleware is registered into the HTTP request pipeline via the following code in the `Startup.cs` file's `Configure` method: + +[!code-csharp[Main](../client-side/spa-services/sample/SpaServicesSampleApp/Startup.cs?range=41-42,44-47,53-54)] + +With regard to `UseWebpackDevMiddleware`, there are a couple important points: +1. It must be called before `UseStaticFiles` +1. It should be registered for use only when running the application in development mode ## Hot Module Replacement -## Routing helpers +## Routing helper + +In most SPAs using ASP.NET Core, you'll want client-side routing in addition to server-side routing. The SPA and MVC routing systems can work independently without interference. There is, however, one edge case posing challenges: identifying 404s. + +Consider the scenario where an extensionless route of `/some/page` is used. Assume the request doesn't pattern match a server-side route, but it pattern matches a client-side route. Now consider an incoming request for `/images/user-512.png`, which undoubtedly expects to find an image file on the server. As such, if that requested resource path doesn't match any server-side route or static file, it's unlikely that your client-side application would handle it — you probably want to return a 404 HTTP status code. +To distinguish between these cases, a C# extension method named `MapSpaFallbackRoute` is used in the `Startup` class' `Configure` method: +[!code-csharp[Main](../client-side/spa-services/sample/SpaServicesSampleApp/Startup.cs?name=mvc-routing-table&highlight=7-9)] +Order matters when configuring routes. Consequently, diff --git a/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Startup.cs b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Startup.cs index db3db30e5db1..6f000a672235 100644 --- a/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Startup.cs +++ b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Startup.cs @@ -38,6 +38,7 @@ public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerF loggerFactory.AddConsole(Configuration.GetSection("Logging")); loggerFactory.AddDebug(); + #region webpack-middleware-registration if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); @@ -50,8 +51,11 @@ public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerF app.UseExceptionHandler("/Home/Error"); } + // Call UseWebpackDevMiddleware before UseStaticFiles app.UseStaticFiles(); + #endregion + #region mvc-routing-table app.UseMvc(routes => { routes.MapRoute( @@ -62,6 +66,7 @@ public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerF name: "spa-fallback", defaults: new { controller = "Home", action = "Index" }); }); + #endregion } } } From b54230f78c81443e907f455e5f1a3898f7052a80 Mon Sep 17 00:00:00 2001 From: Scott Addie Date: Wed, 21 Jun 2017 13:16:15 -0500 Subject: [PATCH 07/23] Reorganize headings and start to HMR content --- aspnetcore/client-side/spa-services.md | 34 +++++++++++++++++++++----- 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/aspnetcore/client-side/spa-services.md b/aspnetcore/client-side/spa-services.md index f66fb8c24f6f..89ef100b82e0 100644 --- a/aspnetcore/client-side/spa-services.md +++ b/aspnetcore/client-side/spa-services.md @@ -35,11 +35,17 @@ A universal (also known as isomorphic) application is a JavaScript application c ### Tag Helpers -SpaServices provides a suite of ASP.NET Core [Tag Helpers](xref:mvc/views/tag-helpers/intro) to support the prerendering process. Using them requires installation of the following mutually inclusive prerequisites: +SpaServices provides a suite of ASP.NET Core [Tag Helpers](xref:mvc/views/tag-helpers/intro) to support the prerendering process. + +#### Prerequisites + +Using them requires installation of the following mutually inclusive prerequisites: 1. [Microsoft.AspNetCore.SpaServices](http://www.nuget.org/packages/Microsoft.AspNetCore.SpaServices/) NuGet package 1. [aspnet-prerendering](https://www.npmjs.com/package/aspnet-prerendering) npm package -With the required packages installed, the Tag Helpers are made discoverable via registration in the project's `_ViewImports.cshtml` file: +#### Configuration + +The Tag Helpers are made discoverable via registration in the project's `_ViewImports.cshtml` file: [!code-csharp[Main](../client-side/spa-services/sample/SpaServicesSampleApp/Views/_ViewImports.cshtml?highlight=3)] @@ -83,21 +89,37 @@ The `postList` array defined inside the `globals` object is attached to the brow [!code-json[Main](../client-side/spa-services/sample/SpaServicesSampleApp/package.json?range=5)] +### Prerequisites + Using Webpack Dev Middleware requires installation of the following mutually inclusive prerequisites: 1. [Microsoft.AspNetCore.SpaServices](http://www.nuget.org/packages/Microsoft.AspNetCore.SpaServices/) NuGet package 1. [aspnet-webpack](https://www.npmjs.com/package/aspnet-webpack) npm package +### Configuration + Webpack Dev Middleware is registered into the HTTP request pipeline via the following code in the `Startup.cs` file's `Configure` method: -[!code-csharp[Main](../client-side/spa-services/sample/SpaServicesSampleApp/Startup.cs?range=41-42,44-47,53-54)] +[!code-csharp[Main](../client-side/spa-services/sample/SpaServicesSampleApp/Startup.cs?name=webpack-middleware-registration&highlight=4-6)] -With regard to `UseWebpackDevMiddleware`, there are a couple important points: -1. It must be called before `UseStaticFiles` +With regard to `UseWebpackDevMiddleware`, there are a few critical points: +1. It must be called before the `UseStaticFiles` extension method 1. It should be registered for use only when running the application in development mode +Finally, the `webpack.config.js` file's `output.publicPath` property tells the middleware to watch the `wwwroot/dist` folder for changes: + +[!code-javascript[Main](../client-side/spa-services/sample/SpaServicesSampleApp/webpack.config.js?range=6,13-16)] + ## Hot Module Replacement -## Routing helper +Webpack's [Hot Module Replacement](https://webpack.github.io/docs/hot-module-replacement-with-webpack.html) (HMR) feature introduces all the same benefits as Webpack Dev Middleware; and, it streamlines the development workflow even further by automatically updating page content after compiling the changes. Don't confuse this with a refresh of the browser, as that would interfere with the current in-memory state and debugging session of the SPA. Changes are simply pushed to the browser. + +### Prerequisites + +### Configuration + + + +## Routing helpers In most SPAs using ASP.NET Core, you'll want client-side routing in addition to server-side routing. The SPA and MVC routing systems can work independently without interference. There is, however, one edge case posing challenges: identifying 404s. From 0585a5a23cd7407f2c52cafd0f18457db0a5bb45 Mon Sep 17 00:00:00 2001 From: Scott Addie Date: Wed, 21 Jun 2017 15:13:33 -0500 Subject: [PATCH 08/23] HMR changes --- aspnetcore/client-side/spa-services.md | 44 ++++++++++++------- .../sample/SpaServicesSampleApp/Startup.cs | 4 +- 2 files changed, 29 insertions(+), 19 deletions(-) diff --git a/aspnetcore/client-side/spa-services.md b/aspnetcore/client-side/spa-services.md index 89ef100b82e0..2e352800c5ac 100644 --- a/aspnetcore/client-side/spa-services.md +++ b/aspnetcore/client-side/spa-services.md @@ -27,23 +27,19 @@ A SPA is a popular type of web application due to its inherent rich user experie ## What is SpaServices? -SpaServices is not required to develop SPAs with ASP.NET Core. Since SpaServices is a nonopinionated, client framework-agnostic library, it doesn't lock you into a particular client framework, library, or coding style. It provides useful infrastructure such as server-side prerendering, Webpack middleware, Hot Module Replacement, and routing helpers. +SpaServices is not required to develop SPAs with ASP.NET Core. Because SpaServices is a nonopinionated, client framework-agnostic library, it doesn't lock you into a particular client framework, library, or coding style. It provides useful infrastructure such as server-side prerendering, Webpack middleware, Hot Module Replacement, and routing helpers. -### Server-side prerendering +## Server-side prerendering -A universal (also known as isomorphic) application is a JavaScript application capable of running both on the server and the client. Angular, React, and other popular frameworks provide a universal platform for this application development style. The idea is to first render the framework components on the server-side via Node.js and then delegate further execution to the client. SpaServices' Tag Helpers make the implementation of server-side prerendering simple by invoking the JavaScript functions on the server for you. +A universal (also known as isomorphic) application is a JavaScript application capable of running both on the server and the client. Angular, React, and other popular frameworks provide a universal platform for this application development style. The idea is to first render the framework components on the server-side via Node.js and then delegate further execution to the client. SpaServices' ASP.NET Core [Tag Helpers](xref:mvc/views/tag-helpers/intro) make the implementation of server-side prerendering simple by invoking the JavaScript functions on the server for you. -### Tag Helpers - -SpaServices provides a suite of ASP.NET Core [Tag Helpers](xref:mvc/views/tag-helpers/intro) to support the prerendering process. - -#### Prerequisites +### Prerequisites -Using them requires installation of the following mutually inclusive prerequisites: +Install the following mutually inclusive prerequisites: 1. [Microsoft.AspNetCore.SpaServices](http://www.nuget.org/packages/Microsoft.AspNetCore.SpaServices/) NuGet package 1. [aspnet-prerendering](https://www.npmjs.com/package/aspnet-prerendering) npm package -#### Configuration +### Configuration The Tag Helpers are made discoverable via registration in the project's `_ViewImports.cshtml` file: @@ -91,7 +87,7 @@ The `postList` array defined inside the `globals` object is attached to the brow ### Prerequisites -Using Webpack Dev Middleware requires installation of the following mutually inclusive prerequisites: +Install the following mutually inclusive prerequisites: 1. [Microsoft.AspNetCore.SpaServices](http://www.nuget.org/packages/Microsoft.AspNetCore.SpaServices/) NuGet package 1. [aspnet-webpack](https://www.npmjs.com/package/aspnet-webpack) npm package @@ -99,7 +95,7 @@ Using Webpack Dev Middleware requires installation of the following mutually inc Webpack Dev Middleware is registered into the HTTP request pipeline via the following code in the `Startup.cs` file's `Configure` method: -[!code-csharp[Main](../client-side/spa-services/sample/SpaServicesSampleApp/Startup.cs?name=webpack-middleware-registration&highlight=4-6)] +[!code-csharp[Main](../client-side/spa-services/sample/SpaServicesSampleApp/Startup.cs?name=webpack-middleware-registration&highlight=4)] With regard to `UseWebpackDevMiddleware`, there are a few critical points: 1. It must be called before the `UseStaticFiles` extension method @@ -111,13 +107,31 @@ Finally, the `webpack.config.js` file's `output.publicPath` property tells the m ## Hot Module Replacement -Webpack's [Hot Module Replacement](https://webpack.github.io/docs/hot-module-replacement-with-webpack.html) (HMR) feature introduces all the same benefits as Webpack Dev Middleware; and, it streamlines the development workflow even further by automatically updating page content after compiling the changes. Don't confuse this with a refresh of the browser, as that would interfere with the current in-memory state and debugging session of the SPA. Changes are simply pushed to the browser. +Think of Webpack's [Hot Module Replacement](https://webpack.github.io/docs/hot-module-replacement-with-webpack.html) (HMR) feature as an evolution of Webpack Dev Middleware. HMR introduces all the same benefits; and, it streamlines the development workflow even further by automatically updating page content after compiling the changes. Don't confuse this with a refresh of the browser, which would interfere with the current in-memory state and debugging session of the SPA. Changes are simply pushed to the browser. ### Prerequisites +Install the following mutually inclusive prerequisites: +1. [Microsoft.AspNetCore.SpaServices](http://www.nuget.org/packages/Microsoft.AspNetCore.SpaServices/) NuGet package +1. [webpack-hot-middleware](https://www.npmjs.com/package/webpack-hot-middleware) npm package + ### Configuration +The HMR component must be registered into MVC's HTTP request pipeline. An overload of the `UseWebpackDevMiddleware` extension method must be used in the `Startup` class' `Configure` method: +```csharp +app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions { + HotModuleReplacement = true +}); +``` + +As was true with Webpack Dev Middleware, there are a few critical points when using `UseWebpackDevMiddleware`: +1. It must be called before the `UseStaticFiles` extension method +1. It should be registered for use only when running the application in development mode + +Finally, the `webpack.config.js` file should define a `plugins` array, even if it's left empty: + +[!code-javascript[Main](../client-side/spa-services/sample/SpaServicesSampleApp/webpack.config.js?range=6,25)] ## Routing helpers @@ -129,9 +143,7 @@ To distinguish between these cases, a C# extension method named `MapSpaFallbackR [!code-csharp[Main](../client-side/spa-services/sample/SpaServicesSampleApp/Startup.cs?name=mvc-routing-table&highlight=7-9)] -Order matters when configuring routes. Consequently, - - +Routes are evaluated in the order in which they're configured. Consequently, the server routing table will be consulted for pattern matching first. ## Prerequisites diff --git a/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Startup.cs b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Startup.cs index 6f000a672235..c9d8f1a66a5a 100644 --- a/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Startup.cs +++ b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Startup.cs @@ -42,9 +42,7 @@ public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerF if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); - app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions { - HotModuleReplacement = true - }); + app.UseWebpackDevMiddleware(); } else { From a7cb2552077da5adb2605f8f1a723e093bd476c0 Mon Sep 17 00:00:00 2001 From: Scott Addie Date: Thu, 22 Jun 2017 10:49:16 -0500 Subject: [PATCH 09/23] Additional tweaks to content --- aspnetcore/client-side/spa-services.md | 34 ++++++++++++++++++-------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/aspnetcore/client-side/spa-services.md b/aspnetcore/client-side/spa-services.md index 2e352800c5ac..a1dd9802951d 100644 --- a/aspnetcore/client-side/spa-services.md +++ b/aspnetcore/client-side/spa-services.md @@ -23,25 +23,31 @@ In this article, you will learn about the value proposition of [SpaServices](htt ## Using SpaServices with ASP.NET Core -A SPA is a popular type of web application due to its inherent rich user experience; however, integrating client-side SPA frameworks or libraries, such as [Angular](https://angular.io/) or [React](https://facebook.github.io/react/), with server-side frameworks like ASP.NET Core can be daunting. The `Microsoft.AspNetCore.SpaServices` NuGet package, or SpaServices for short, was developed to reduce friction in the integration process. It enables seamless operation between the disparate client and server technology stacks. +A SPA is a very popular breed of web application due to its inherent rich user experience. Alas, integrating client-side SPA frameworks or libraries, such as [Angular](https://angular.io/) or [React](https://facebook.github.io/react/), with server-side frameworks like ASP.NET Core can be daunting. The `Microsoft.AspNetCore.SpaServices` NuGet package, or SpaServices for short, was developed to reduce friction in the integration process. It enables seamless operation between the disparate client and server technology stacks. ## What is SpaServices? -SpaServices is not required to develop SPAs with ASP.NET Core. Because SpaServices is a nonopinionated, client framework-agnostic library, it doesn't lock you into a particular client framework, library, or coding style. It provides useful infrastructure such as server-side prerendering, Webpack middleware, Hot Module Replacement, and routing helpers. +SpaServices was created as a component of the larger [JavaScriptServices](https://github.com/aspnet/JavaScriptServices) project, whose goal is to make ASP.NET Core developers' preferred server-side platform for building SPAs. With that being said, SpaServices is not required to develop SPAs with ASP.NET Core. Because SpaServices is a nonopinionated, client framework-agnostic library, it doesn't lock you into a particular client framework, library, or coding style. It provides useful infrastructure such as server-side prerendering, Webpack Dev Middleware, Hot Module Replacement, and routing helpers. ## Server-side prerendering -A universal (also known as isomorphic) application is a JavaScript application capable of running both on the server and the client. Angular, React, and other popular frameworks provide a universal platform for this application development style. The idea is to first render the framework components on the server-side via Node.js and then delegate further execution to the client. SpaServices' ASP.NET Core [Tag Helpers](xref:mvc/views/tag-helpers/intro) make the implementation of server-side prerendering simple by invoking the JavaScript functions on the server for you. +A universal (also known as isomorphic) application is a JavaScript application capable of running both on the server and the client. Angular, React, and other popular frameworks provide a universal platform for this application development style. The idea is to first render the framework components on the server via Node.js and then delegate further execution to the client. + +SpaServices' ASP.NET Core [Tag Helpers](xref:mvc/views/tag-helpers/intro) make the implementation of server-side prerendering simple by invoking the JavaScript functions on the server for you. ### Prerequisites Install the following mutually inclusive prerequisites: 1. [Microsoft.AspNetCore.SpaServices](http://www.nuget.org/packages/Microsoft.AspNetCore.SpaServices/) NuGet package -1. [aspnet-prerendering](https://www.npmjs.com/package/aspnet-prerendering) npm package +1. [aspnet-prerendering](https://www.npmjs.com/package/aspnet-prerendering) npm package: + + ```console + npm i -S aspnet-prerendering + ``` ### Configuration -The Tag Helpers are made discoverable via registration in the project's `_ViewImports.cshtml` file: +The Tag Helpers are made discoverable via namespace registration in the project's `_ViewImports.cshtml` file: [!code-csharp[Main](../client-side/spa-services/sample/SpaServicesSampleApp/Views/_ViewImports.cshtml?highlight=3)] @@ -49,7 +55,7 @@ These Tag Helpers abstract away the intricacies of communicating directly with l [!code-html[Main](../client-side/spa-services/sample/SpaServicesSampleApp/Views/Home/Index.cshtml?range=5)] -### The asp-prerender-module Tag Helper +### The `asp-prerender-module` Tag Helper The `asp-prerender-module` Tag Helper, used in the previous example, executes `ClientApp/dist/main-server.js` on the server via Node.js. To clarify, `main-server.js` file is an artifact of the [Webpack](http://webpack.github.io/) build process' TypeScript-to-JavaScript transpilation task. Webpack defines an entry point alias of `main-server`; and, traversal of the dependency graph for this alias begins at the `ClientApp/boot-server.ts` file: @@ -59,7 +65,7 @@ The `ClientApp/boot-server.ts` file utilizes the `createServerRenderer` function [!code-javascript[Main](../client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/boot-server.ts?range=6,10-34,79-)] -### The asp-prerender-data Tag Helper +### The `asp-prerender-data` Tag Helper Sometimes contextual information must be passed as arguments from the Razor view to the server-side JavaScript. To satisfy this requirement, the `asp-prerender-data` Tag Helper is used in conjunction with the aforementioned `asp-prerender-module` Tag Helper. For example, the following markup passes user data to the `main-server` module: @@ -81,7 +87,7 @@ The `postList` array defined inside the `globals` object is attached to the brow ## Webpack Dev Middleware -[Webpack Dev Middleware](https://webpack.github.io/docs/webpack-dev-middleware.html) introduces a streamlined development workflow whereby Webpack builds resources on demand. The middleware automatically compiles and serves client-side resources when a page is reloaded in the browser. The alternate, less efficient approach is to manually run the following npm script when a third-party dependency or your custom code changes: +[Webpack Dev Middleware](https://webpack.github.io/docs/webpack-dev-middleware.html) introduces a streamlined development workflow whereby Webpack builds resources on demand. The middleware automatically compiles and serves client-side resources when a page is reloaded in the browser. The alternate, less efficient approach is to manually run the project's Webpack build script when a third-party dependency or your custom code changes. An example of said build script is: [!code-json[Main](../client-side/spa-services/sample/SpaServicesSampleApp/package.json?range=5)] @@ -89,7 +95,11 @@ The `postList` array defined inside the `globals` object is attached to the brow Install the following mutually inclusive prerequisites: 1. [Microsoft.AspNetCore.SpaServices](http://www.nuget.org/packages/Microsoft.AspNetCore.SpaServices/) NuGet package -1. [aspnet-webpack](https://www.npmjs.com/package/aspnet-webpack) npm package +1. [aspnet-webpack](https://www.npmjs.com/package/aspnet-webpack) npm package: + + ```console + npm i -D aspnet-webpack + ``` ### Configuration @@ -113,7 +123,11 @@ Think of Webpack's [Hot Module Replacement](https://webpack.github.io/docs/hot-m Install the following mutually inclusive prerequisites: 1. [Microsoft.AspNetCore.SpaServices](http://www.nuget.org/packages/Microsoft.AspNetCore.SpaServices/) NuGet package -1. [webpack-hot-middleware](https://www.npmjs.com/package/webpack-hot-middleware) npm package +1. [webpack-hot-middleware](https://www.npmjs.com/package/webpack-hot-middleware) npm package: + + ```console + npm i -D webpack-hot-middleware + ``` ### Configuration From 0fe0da0e053c21d0c0586cd4be42853e144b5b39 Mon Sep 17 00:00:00 2001 From: Scott Addie Date: Thu, 22 Jun 2017 12:51:40 -0500 Subject: [PATCH 10/23] Add HMR screenshot and finish SpaServices prereqs section --- aspnetcore/client-side/spa-services.md | 45 +++++++++++------- .../spa-services/_static/hmr_connected.png | Bin 0 -> 16476 bytes .../_static/hmr_connected_original.png | Bin 0 -> 27722 bytes 3 files changed, 28 insertions(+), 17 deletions(-) create mode 100644 aspnetcore/client-side/spa-services/_static/hmr_connected.png create mode 100644 aspnetcore/client-side/spa-services/_static/hmr_connected_original.png diff --git a/aspnetcore/client-side/spa-services.md b/aspnetcore/client-side/spa-services.md index a1dd9802951d..5ab62a0e8b0e 100644 --- a/aspnetcore/client-side/spa-services.md +++ b/aspnetcore/client-side/spa-services.md @@ -33,7 +33,7 @@ SpaServices was created as a component of the larger [JavaScriptServices](https: A universal (also known as isomorphic) application is a JavaScript application capable of running both on the server and the client. Angular, React, and other popular frameworks provide a universal platform for this application development style. The idea is to first render the framework components on the server via Node.js and then delegate further execution to the client. -SpaServices' ASP.NET Core [Tag Helpers](xref:mvc/views/tag-helpers/intro) make the implementation of server-side prerendering simple by invoking the JavaScript functions on the server for you. +SpaServices' ASP.NET Core [Tag Helpers](xref:mvc/views/tag-helpers/intro) simplify the implementation of server-side prerendering by invoking the JavaScript functions on the server for you. ### Prerequisites @@ -47,7 +47,7 @@ Install the following mutually inclusive prerequisites: ### Configuration -The Tag Helpers are made discoverable via namespace registration in the project's `_ViewImports.cshtml` file: +The Tag Helpers are made discoverable via namespace registration in the project's *_ViewImports.cshtml* file: [!code-csharp[Main](../client-side/spa-services/sample/SpaServicesSampleApp/Views/_ViewImports.cshtml?highlight=3)] @@ -57,11 +57,11 @@ These Tag Helpers abstract away the intricacies of communicating directly with l ### The `asp-prerender-module` Tag Helper -The `asp-prerender-module` Tag Helper, used in the previous example, executes `ClientApp/dist/main-server.js` on the server via Node.js. To clarify, `main-server.js` file is an artifact of the [Webpack](http://webpack.github.io/) build process' TypeScript-to-JavaScript transpilation task. Webpack defines an entry point alias of `main-server`; and, traversal of the dependency graph for this alias begins at the `ClientApp/boot-server.ts` file: +The `asp-prerender-module` Tag Helper, used in the previous example, executes *ClientApp/dist/main-server.js* on the server via Node.js. To clarify, *main-server.js* file is an artifact of the [Webpack](http://webpack.github.io/) build process' TypeScript-to-JavaScript transpilation task. Webpack defines an entry point alias of `main-server`; and, traversal of the dependency graph for this alias begins at the *ClientApp/boot-server.ts* file: [!code-javascript[Main](../client-side/spa-services/sample/SpaServicesSampleApp/webpack.config.js?range=53)] -The `ClientApp/boot-server.ts` file utilizes the `createServerRenderer` function and `RenderResult` type of the `aspnet-prerendering` npm package to configure server rendering via Node.js. The HTML markup destined for server-side rendering is passed to a `resolve` function call, which is wrapped in a JavaScript `Promise` object of type `RenderResult`. The `Promise` object is significant in that it asynchronously supplies the HTML markup to the page for injection in the placeholder element. +The *ClientApp/boot-server.ts* file utilizes the `createServerRenderer` function and `RenderResult` type of the `aspnet-prerendering` npm package to configure server rendering via Node.js. The HTML markup destined for server-side rendering is passed to a `resolve` function call, which is wrapped in a JavaScript `Promise` object of type `RenderResult`. The `Promise` object's significance is that it asynchronously supplies the HTML markup to the page for injection in the DOM's placeholder element. [!code-javascript[Main](../client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/boot-server.ts?range=6,10-34,79-)] @@ -75,7 +75,8 @@ The received `UserName` argument is serialized using the built-in JSON serialize [!code-javascript[Main](../client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/boot-server.ts?range=6,10-21,38-52,79-)] -Property names passed in the `asp-prerender-data` Tag Helper are represented with *PascalCase* notation. Contrast that to JavaScript, where the same property names are represented with *camelCase*. The default JSON serialization configuration is responsible for this difference. +> [!NOTE] +> Property names passed in Tag Helpers are represented with *PascalCase* notation. Contrast that to JavaScript, where the same property names are represented with *camelCase*. The default JSON serialization configuration is responsible for this difference. Data can be passed from the server to the view by hydrating the `globals` property passed to the `resolve` function: @@ -87,7 +88,7 @@ The `postList` array defined inside the `globals` object is attached to the brow ## Webpack Dev Middleware -[Webpack Dev Middleware](https://webpack.github.io/docs/webpack-dev-middleware.html) introduces a streamlined development workflow whereby Webpack builds resources on demand. The middleware automatically compiles and serves client-side resources when a page is reloaded in the browser. The alternate, less efficient approach is to manually run the project's Webpack build script when a third-party dependency or your custom code changes. An example of said build script is: +[Webpack Dev Middleware](https://webpack.github.io/docs/webpack-dev-middleware.html) introduces a streamlined development workflow whereby Webpack builds resources on demand. The middleware automatically compiles and serves client-side resources when a page is reloaded in the browser. The alternate, less efficient approach is to manually invoke Webpack via the project's npm build script when a third-party dependency or the custom code changes. An example of said build script is: [!code-json[Main](../client-side/spa-services/sample/SpaServicesSampleApp/package.json?range=5)] @@ -103,7 +104,7 @@ Install the following mutually inclusive prerequisites: ### Configuration -Webpack Dev Middleware is registered into the HTTP request pipeline via the following code in the `Startup.cs` file's `Configure` method: +Webpack Dev Middleware is registered into the HTTP request pipeline via the following code in the *Startup.cs* file's `Configure` method: [!code-csharp[Main](../client-side/spa-services/sample/SpaServicesSampleApp/Startup.cs?name=webpack-middleware-registration&highlight=4)] @@ -111,7 +112,7 @@ With regard to `UseWebpackDevMiddleware`, there are a few critical points: 1. It must be called before the `UseStaticFiles` extension method 1. It should be registered for use only when running the application in development mode -Finally, the `webpack.config.js` file's `output.publicPath` property tells the middleware to watch the `wwwroot/dist` folder for changes: +Finally, the *webpack.config.js* file's `output.publicPath` property tells the middleware to watch the `wwwroot/dist` folder for changes: [!code-javascript[Main](../client-side/spa-services/sample/SpaServicesSampleApp/webpack.config.js?range=6,13-16)] @@ -143,23 +144,35 @@ As was true with Webpack Dev Middleware, there are a few critical points when us 1. It must be called before the `UseStaticFiles` extension method 1. It should be registered for use only when running the application in development mode -Finally, the `webpack.config.js` file should define a `plugins` array, even if it's left empty: +Finally, the *webpack.config.js* file must define a `plugins` array, even if it's left empty: [!code-javascript[Main](../client-side/spa-services/sample/SpaServicesSampleApp/webpack.config.js?range=6,25)] +After loading the application in the browser, the developer tools' Console tab provides confirmation of HMR activation: + +![Hot Module Replacement connected message](spa-services/_static/hmr_connected.png) + ## Routing helpers In most SPAs using ASP.NET Core, you'll want client-side routing in addition to server-side routing. The SPA and MVC routing systems can work independently without interference. There is, however, one edge case posing challenges: identifying 404s. -Consider the scenario where an extensionless route of `/some/page` is used. Assume the request doesn't pattern match a server-side route, but it pattern matches a client-side route. Now consider an incoming request for `/images/user-512.png`, which undoubtedly expects to find an image file on the server. As such, if that requested resource path doesn't match any server-side route or static file, it's unlikely that your client-side application would handle it — you probably want to return a 404 HTTP status code. +Consider the scenario in which an extensionless route of `/some/page` is used. Assume the request doesn't pattern-match a server-side route, but its pattern does match a client-side route. Now consider an incoming request for `/images/user-512.png`, which undoubtedly expects to find an image file on the server. As such, if that requested resource path doesn't match any server-side route or static file, it's unlikely that the client-side application would handle it — you probably want to return a 404 HTTP status code. To distinguish between these cases, a C# extension method named `MapSpaFallbackRoute` is used in the `Startup` class' `Configure` method: [!code-csharp[Main](../client-side/spa-services/sample/SpaServicesSampleApp/Startup.cs?name=mvc-routing-table&highlight=7-9)] -Routes are evaluated in the order in which they're configured. Consequently, the server routing table will be consulted for pattern matching first. +> [!TIP] +> Routes are evaluated in the order in which they're configured. Consequently, the `default` route in the preceding code example will be consulted first for pattern matching. -## Prerequisites +## Prerequisites for using SpaServices + +To work with SpaServices, install the following: +1. [Node.js](https://nodejs.org/), version 6 or later + * To test this is installed and can be found, run `node -v` on a command line. + * Note: If you're deploying to an Azure web site, you don't need to do anything here — Node is already installed and available in the server environments. +1. .NET Core, version 1.0 RC4 or later + * If you're on Windows, you can install Visual Studio 2017, which includes it. ## Creating a new project @@ -171,9 +184,7 @@ Routes are evaluated in the order in which they're configured. Consequently, the ### Web API -### Angular - -## Developing your project +## Developing the project ### Create the Backend (Controller / REST) @@ -181,7 +192,7 @@ Routes are evaluated in the order in which they're configured. Consequently, the ### Add Lazy Loading -## Debugging your application +## Debugging the application ### Visual Studio Code @@ -195,7 +206,7 @@ Routes are evaluated in the order in which they're configured. Consequently, the ### Use Tree Shaking with Webpack -## Deploying your application +## Deploying the application ### Azure diff --git a/aspnetcore/client-side/spa-services/_static/hmr_connected.png b/aspnetcore/client-side/spa-services/_static/hmr_connected.png new file mode 100644 index 0000000000000000000000000000000000000000..346b80e6888e582b158c3d853f2a579f2be96a78 GIT binary patch literal 16476 zcmeIZg;QKj5HFep2=1=I-Q8hX+})i3i@OI%7Fh_vf)ivHmtX;cL$Kgsae@Y4Jb}gS z^4(YW)_woMt9rMqPMtG7r{?sTneOTC`Axj8whAs571oOvFL2dV74=`dcm;p);^lh` zlxI%kY3ac8?WLc-io%Q9Uo`vA1ypB%7U0E;hGgt}JG2)sP+sV28Yu$D#ETLDQ?WhXeiEM zA}!)BLG3BPrAMctEu^g^?c^*1G*TAzQ>JlLW_MB*1L?9k8%PGJar$YAgc@-M8HYEs=DVZCno0ynrnScx&{hZX4Kn7}#R>mQ!ih-8uLFR^jPDako&W`TB zEM2u)92CpSf7lz zW9;bCU4<*WB@)fl5Ivl$BAB99U zKtv0`mMPJG&Cy0psh)qMnEEo+e?mM4^1X&~0;ltQ$I62KW~%(Ha)Ur1(TVA?pL4Sk zqjJ+eW@ct4e<{l;uP@6NF|+G!yo>Zqv?Ohzqj~^{>o$XH}5QvkLvqR*; z(ZwC|2=Vv$?B?R&@$vCl9sVDpaMjIu){__h`q~Ch|No{J23P1T&%J`_t!n1?;ssv+ zf7i<)uZp+NOiF(xQ-1?5C;uQjU&j~Tb}pX&yq=E!^uoOSyaJ+%E_D|#UPv{mDFO_G zt&j5~-_baS?*%cv>9cp=+SnD2u_rhVsgR)CGmA&V@j2mQ4VpmvE@j* znM3iHq<4Pv<<%D^t(WDj8w!8ifnzzIn)B*@6AGhrB8k6BWJ)jvV=^^pv}gps=ml

fNq%^)UFYFAWB<%pfS)ge#^nDP$HP?lQHf+Bx)z|P@ zO(t$49M&QOzZ>P|4Zaxd$aTute%H&FvE7CT;4@5-m=s1m=GBLbYvSP%BWZ%_H)+~U z;hDSe_VEHA4T7fE8uQ@^M8fEMDVg!)wcnx8-bDQ}y-nIBa57KSz0iBsFDs5xyIuptDVOF`*5bp+7~fI zO=VxYk;+@|*4z#i3dkF`NoE%(=7ytVopEP-Pk73Rh9F^$*(P`So#10)Lbd+=8*3yvSK z2GSv497}8txkh1OAim3#s?@JuSVPX|t+n-1vfOwd1kM17I3*W*$ya(W(r{qZsUujw z_YAwfuXA6Ri)!iE$m=gnDI)=aj9;&44ft*{ zce1?m7jt?gC`}2te|`BJZ!k|(Q<73#vF|y;`H|O|8+DOt{(D;!zsJddROc#%EAG>hd#~zgVao-s~3ZxcTbBt$vF9UufjXL zpuRJ6rBFS*uB9X?xm0qoHZ*dMMMKWH!&8K^;T(p<$~#%6?`q5CTsedkt;qxpS>t`& z)O*`-N!~JlK$2ZIkeb&X=Oe>e66@ylJItSu;shS15z_L)PdV5Jan&hIB>ug6dbL;* zP*t1s>3TXsH)U>;7^ogWb3!XcL-cr=gV48E;{M5BnYXAK6FVtwVo|7a={<4VwF-sf zn)Tr^L{=%aN`6qE?S7W+VBLWndFMQ(q4(5?!vrLehhGlmL`{<$NR*LcQXSX?!AJ9 z-|3-cF(eZHFWE>)dBj0{;~k((@8tJ}V*s3E`etbRAUAcr+);S1J*_$9*lwD%?pe}& zm?f1}xlkzySYKdN4=gd={8CLu)i=z@Ddwbt$xFVreTxaQNQNuY%Hr!qWd&`pj_T}I z39=;5)N;V|oqc(t!C_C2!uzGNkUw)qE5o+lK&|%9zF!{3>pq$qMXiZAJ-oLe zPS_&oCOA|(??+wx*L>qB}#%vJ?fJatOn0zNy~9 z`;!lvT0cBXnP}0sI5|uX;f~KXEShr;Yd?s8)vQR#4py?{)*sNXl6Z{;YZpOSz%8jk z>rPt6oa!Jene(kUx>2H+CJf(BLcZ8$zQh1N47|C(8=yP8!CSSa979j!sUslZ-uIM= zbvCBg3)@eJGTmVdHH@po!3eIMyl2)v$Vt}Wpgnbj?_R!w6s*Thl8|ciPX?>^ zhPWxzQ~{;gZxLo9o|}qPdIBi*n7W2{xL@3LF`O5+Zf;$o6%t2{jLN)==XU@PNsc&LU

76=e^H{THa(TP|rIyizUds>q6 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 literal 0 HcmV?d00001 diff --git a/aspnetcore/client-side/spa-services/_static/hmr_connected_original.png b/aspnetcore/client-side/spa-services/_static/hmr_connected_original.png new file mode 100644 index 0000000000000000000000000000000000000000..684cc868ca921dcf09b366dfd34dc7896f37d49e GIT binary patch literal 27722 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 literal 0 HcmV?d00001 From 671530c30bcc26502d42dd475305b1575055a873 Mon Sep 17 00:00:00 2001 From: Scott Addie Date: Thu, 22 Jun 2017 13:45:12 -0500 Subject: [PATCH 11/23] Start to project creation content for .NET Core CLI --- aspnetcore/client-side/spa-services.md | 51 ++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/aspnetcore/client-side/spa-services.md b/aspnetcore/client-side/spa-services.md index 5ab62a0e8b0e..cce89d24bcb8 100644 --- a/aspnetcore/client-side/spa-services.md +++ b/aspnetcore/client-side/spa-services.md @@ -176,8 +176,59 @@ To work with SpaServices, install the following: ## Creating a new project +JavaScriptServices provides pre-configured application templates. SpaServices is used in these templates, in conjunction with different frameworks and libraries such as Angular, Aurelia, Knockout, React, and Vue. + +These templates can be installed using the .NET Core CLI by running the following command: + +```console +dotnet new --install Microsoft.AspNetCore.SpaTemplates::* +``` + +Upon successful installation, a list of available SPA templates is provided: + +| Templates | Short Name | Language | Tags | +|:------------------------------------------|:-----------|:---------|:------------| +| MVC ASP.NET Core with Angular | angular | [C#] | Web/MVC/SPA | +| MVC ASP.NET Core with Aurelia | aurelia | [C#] | Web/MVC/SPA | +| MVC ASP.NET Core with Knockout.js | knockout | [C#] | Web/MVC/SPA | +| MVC ASP.NET Core with React.js | react | [C#] | Web/MVC/SPA | +| MVC ASP.NET Core with React.js and Redux | reactredux | [C#] | Web/MVC/SPA | +| MVC ASP.NET Core with Vue.js | vue | [C#] | Web/MVC/SPA | + ### Using the .NET Core CLI +To create a new project using one of the SPA templates, include the **Short Name** of the template in the `dotnet new` command. The following command creates an Angular application with ASP.NET Core MVC configured for the server-side: + +```console +dotnet new angular +``` + +Restore the required NuGet and npm packages by running: + +```console +dotnet restore && npm i +``` + +Set an environment variable instructing ASP.NET Core to run in **development** mode: + +* For Windows: + * **PowerShell**: execute `$Env:ASPNETCORE_ENVIRONMENT = "Development"` + * **cmd.exe**: execute `setx ASPNETCORE_ENVIRONMENT "Development"`, and then restart your command prompt for the change to take effect +* For Mac or Linux, execute `export ASPNETCORE_ENVIRONMENT=Development` + +Run the application: + +```console +dotnet run +``` + +The application will start in development mode under localhost. Navigating to `http://localhost:5000` in your browser will display the landing page. + +### Using Visual Studio 2017 + +To create a new project using Visual Studio 2017, navigate to *File* > *New* > *Project...*. + + ### Using npm / Yarn ### What does it produce From 35fc7632c154ac799d635127722d169069ca8664 Mon Sep 17 00:00:00 2001 From: Scott Addie Date: Thu, 22 Jun 2017 15:20:27 -0500 Subject: [PATCH 12/23] Add Karma / Jasmine testing content --- aspnetcore/client-side/spa-services.md | 37 +++++++++++++------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/aspnetcore/client-side/spa-services.md b/aspnetcore/client-side/spa-services.md index cce89d24bcb8..f1b5741d06d1 100644 --- a/aspnetcore/client-side/spa-services.md +++ b/aspnetcore/client-side/spa-services.md @@ -112,7 +112,7 @@ With regard to `UseWebpackDevMiddleware`, there are a few critical points: 1. It must be called before the `UseStaticFiles` extension method 1. It should be registered for use only when running the application in development mode -Finally, the *webpack.config.js* file's `output.publicPath` property tells the middleware to watch the `wwwroot/dist` folder for changes: +Finally, the *webpack.config.js* file's `output.publicPath` property tells the middleware to watch the `dist` folder for changes: [!code-javascript[Main](../client-side/spa-services/sample/SpaServicesSampleApp/webpack.config.js?range=6,13-16)] @@ -178,7 +178,7 @@ To work with SpaServices, install the following: JavaScriptServices provides pre-configured application templates. SpaServices is used in these templates, in conjunction with different frameworks and libraries such as Angular, Aurelia, Knockout, React, and Vue. -These templates can be installed using the .NET Core CLI by running the following command: +These templates can be installed via the .NET Core CLI by running the following command: ```console dotnet new --install Microsoft.AspNetCore.SpaTemplates::* @@ -195,14 +195,14 @@ Upon successful installation, a list of available SPA templates is provided: | MVC ASP.NET Core with React.js and Redux | reactredux | [C#] | Web/MVC/SPA | | MVC ASP.NET Core with Vue.js | vue | [C#] | Web/MVC/SPA | -### Using the .NET Core CLI - To create a new project using one of the SPA templates, include the **Short Name** of the template in the `dotnet new` command. The following command creates an Angular application with ASP.NET Core MVC configured for the server-side: ```console dotnet new angular ``` +### Running with .NET Core CLI + Restore the required NuGet and npm packages by running: ```console @@ -213,7 +213,7 @@ Set an environment variable instructing ASP.NET Core to run in **development** m * For Windows: * **PowerShell**: execute `$Env:ASPNETCORE_ENVIRONMENT = "Development"` - * **cmd.exe**: execute `setx ASPNETCORE_ENVIRONMENT "Development"`, and then restart your command prompt for the change to take effect + * **cmd.exe**: execute `setx ASPNETCORE_ENVIRONMENT "Development"`, and then restart the command shell for the change to take effect * For Mac or Linux, execute `export ASPNETCORE_ENVIRONMENT=Development` Run the application: @@ -222,26 +222,29 @@ Run the application: dotnet run ``` -The application will start in development mode under localhost. Navigating to `http://localhost:5000` in your browser will display the landing page. +The application will start in development mode on localhost. Navigating to `http://localhost:5000` in the browser will display the landing page. -### Using Visual Studio 2017 +### Running with Visual Studio 2017 -To create a new project using Visual Studio 2017, navigate to *File* > *New* > *Project...*. +Open the *.csproj* file generated by the `dotnet new` command. The required NuGet and npm packages will be restored automatically upon project open. This restoration process may take up to a few minutes; and, the application is ready to run when it completes. Click the green run button or press `Ctrl` + `F5`, and the browser opens to the application's landing page. +## Testing the application -### Using npm / Yarn +SpaServices templates are pre-configured to run client-side tests using [Karma](https://karma-runner.github.io/1.0/index.html) and [Jasmine](https://jasmine.github.io/). Jasmine is a popular unit testing framework for JavaScript, whereas Karma is a test runner for those tests. Karma is configured to work with the Webpack Dev Middleware such that you don’t have to stop and run the test every time changes are made. Where it's the code running against the test case or the test case itself, the test will run automatically. -### What does it produce +Using the Angular application as an example, there are two Jasmine test cases already provided for the `CounterComponent` in the *counter.component.spec.ts* file: -### Web API +[!code-javascript[Main](../client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/components/counter/counter.component.spec.ts?range=15-28)] -## Developing the project +Open the command prompt at the root of your application, and run the following command: -### Create the Backend (Controller / REST) +```console +npm test +``` -### Create Angular Module & Components +The script launches the Karma test runner, which reads the settings defined in the *karma.conf.js* file. Amongst other settings, the *karma.conf.js* identifies the test files to be executed via its `files` array: -### Add Lazy Loading +[!code-javascript[Main](../client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/test/karma.conf.js?range=4-5,8-11)] ## Debugging the application @@ -253,10 +256,6 @@ To create a new project using Visual Studio 2017, navigate to *File* > *New* > * ## Distribution -### Use Ahead of Time Compilation - -### Use Tree Shaking with Webpack - ## Deploying the application ### Azure From b351dc8aad76b9db10e3e65f6d4a5dced0db1a09 Mon Sep 17 00:00:00 2001 From: Scott Addie Date: Thu, 22 Jun 2017 15:40:58 -0500 Subject: [PATCH 13/23] More minor tweaks --- aspnetcore/client-side/spa-services.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/aspnetcore/client-side/spa-services.md b/aspnetcore/client-side/spa-services.md index f1b5741d06d1..11f6018e885b 100644 --- a/aspnetcore/client-side/spa-services.md +++ b/aspnetcore/client-side/spa-services.md @@ -29,6 +29,15 @@ A SPA is a very popular breed of web application due to its inherent rich user e SpaServices was created as a component of the larger [JavaScriptServices](https://github.com/aspnet/JavaScriptServices) project, whose goal is to make ASP.NET Core developers' preferred server-side platform for building SPAs. With that being said, SpaServices is not required to develop SPAs with ASP.NET Core. Because SpaServices is a nonopinionated, client framework-agnostic library, it doesn't lock you into a particular client framework, library, or coding style. It provides useful infrastructure such as server-side prerendering, Webpack Dev Middleware, Hot Module Replacement, and routing helpers. +## Prerequisites for using SpaServices + +To work with SpaServices, install the following: +1. [Node.js](https://nodejs.org/) (version 6 or later) + * To verify this is installed and can be found, run `node -v` on a command line. + * Note: If you're deploying to an Azure web site, you don't need to do anything here — Node is already installed and available in the server environments. +1. [.NET Core SDK](https://www.microsoft.com/net/download/core) 1.0 RC4 (or later) + * If you're on Windows, you can install Visual Studio 2017, which includes the .NET Core SDK. + ## Server-side prerendering A universal (also known as isomorphic) application is a JavaScript application capable of running both on the server and the client. Angular, React, and other popular frameworks provide a universal platform for this application development style. The idea is to first render the framework components on the server via Node.js and then delegate further execution to the client. @@ -165,15 +174,6 @@ To distinguish between these cases, a C# extension method named `MapSpaFallbackR > [!TIP] > Routes are evaluated in the order in which they're configured. Consequently, the `default` route in the preceding code example will be consulted first for pattern matching. -## Prerequisites for using SpaServices - -To work with SpaServices, install the following: -1. [Node.js](https://nodejs.org/), version 6 or later - * To test this is installed and can be found, run `node -v` on a command line. - * Note: If you're deploying to an Azure web site, you don't need to do anything here — Node is already installed and available in the server environments. -1. .NET Core, version 1.0 RC4 or later - * If you're on Windows, you can install Visual Studio 2017, which includes it. - ## Creating a new project JavaScriptServices provides pre-configured application templates. SpaServices is used in these templates, in conjunction with different frameworks and libraries such as Angular, Aurelia, Knockout, React, and Vue. @@ -203,7 +203,7 @@ dotnet new angular ### Running with .NET Core CLI -Restore the required NuGet and npm packages by running: +Restore the required NuGet and npm packages by running the following command at the project root: ```console dotnet restore && npm i @@ -236,7 +236,7 @@ Using the Angular application as an example, there are two Jasmine test cases al [!code-javascript[Main](../client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/components/counter/counter.component.spec.ts?range=15-28)] -Open the command prompt at the root of your application, and run the following command: +Open the command prompt at the project root, and run the following command: ```console npm test From 82aab44fe7be4c78319d528f6f5093829eebe176 Mon Sep 17 00:00:00 2001 From: Scott Addie Date: Fri, 23 Jun 2017 10:43:59 -0500 Subject: [PATCH 14/23] Refine existing content --- aspnetcore/client-side/spa-services.md | 88 ++++++++++++++---- .../_static/tag_helper_intellisense.png | Bin 0 -> 21049 bytes .../tag_helper_intellisense_original.png | Bin 0 -> 16315 bytes .../sample/SpaServicesSampleApp/Startup.cs | 7 +- .../Views/Home/Index.cshtml | 3 + 5 files changed, 73 insertions(+), 25 deletions(-) create mode 100644 aspnetcore/client-side/spa-services/_static/tag_helper_intellisense.png create mode 100644 aspnetcore/client-side/spa-services/_static/tag_helper_intellisense_original.png diff --git a/aspnetcore/client-side/spa-services.md b/aspnetcore/client-side/spa-services.md index 11f6018e885b..f8eb0f538a6a 100644 --- a/aspnetcore/client-side/spa-services.md +++ b/aspnetcore/client-side/spa-services.md @@ -5,7 +5,7 @@ description: Learn about the benefits of using SpaServices to build a SPA with A keywords: ASP.NET Core, Angular, SPA, JavaScriptServices, SpaServices ms.author: scaddie manager: wpickett -ms.date: 6/19/2017 +ms.date: 6/23/2017 ms.topic: article ms.assetid: 4b30576b-2718-4c39-9253-a59966747893 ms.technology: aspnet @@ -21,23 +21,46 @@ In this article, you will learn about the value proposition of [SpaServices](htt [View or download sample code](https://github.com/aspnet/Docs/tree/master/aspnetcore/client-side/spa-services/sample) + + ## Using SpaServices with ASP.NET Core A SPA is a very popular breed of web application due to its inherent rich user experience. Alas, integrating client-side SPA frameworks or libraries, such as [Angular](https://angular.io/) or [React](https://facebook.github.io/react/), with server-side frameworks like ASP.NET Core can be daunting. The `Microsoft.AspNetCore.SpaServices` NuGet package, or SpaServices for short, was developed to reduce friction in the integration process. It enables seamless operation between the disparate client and server technology stacks. + + ## What is SpaServices? -SpaServices was created as a component of the larger [JavaScriptServices](https://github.com/aspnet/JavaScriptServices) project, whose goal is to make ASP.NET Core developers' preferred server-side platform for building SPAs. With that being said, SpaServices is not required to develop SPAs with ASP.NET Core. Because SpaServices is a nonopinionated, client framework-agnostic library, it doesn't lock you into a particular client framework, library, or coding style. It provides useful infrastructure such as server-side prerendering, Webpack Dev Middleware, Hot Module Replacement, and routing helpers. +SpaServices was created as a component of the larger [JavaScriptServices](https://github.com/aspnet/JavaScriptServices) project, whose goal is to position ASP.NET Core as developers' preferred server-side platform for building SPAs. With that said, SpaServices is not required to develop SPAs with ASP.NET Core. Because SpaServices is a nonopinionated, client framework-agnostic library, it doesn't lock you into a particular client framework, library, or coding style. + +SpaServices provides useful infrastructure such as: +* [Server-side prerendering](#server-prerendering) +* [Webpack Dev Middleware](#webpack-dev-middleware) +* [Hot Module Replacement](#hot-module-replacement) +* [Routing helpers](#routing-helpers) + +Collectively, these infrastructure components enhance both the development workflow and the runtime experience. Moreover, the components may be adopted in an à la carte fashion. + + ## Prerequisites for using SpaServices To work with SpaServices, install the following: -1. [Node.js](https://nodejs.org/) (version 6 or later) - * To verify this is installed and can be found, run `node -v` on a command line. - * Note: If you're deploying to an Azure web site, you don't need to do anything here — Node is already installed and available in the server environments. +1. [Node.js](https://nodejs.org/) (version 6 or later) with npm + * To verify these component are installed and can be found, run the following from the command line: + + ```console + node -v && npm -v + ``` + + > [!NOTE] + > If you're deploying to an Azure web site, you don't need to do anything here — Node.js is already installed and available in the server environments. + 1. [.NET Core SDK](https://www.microsoft.com/net/download/core) 1.0 RC4 (or later) * If you're on Windows, you can install Visual Studio 2017, which includes the .NET Core SDK. + + ## Server-side prerendering A universal (also known as isomorphic) application is a JavaScript application capable of running both on the server and the client. Angular, React, and other popular frameworks provide a universal platform for this application development style. The idea is to first render the framework components on the server via Node.js and then delegate further execution to the client. @@ -46,7 +69,7 @@ SpaServices' ASP.NET Core [Tag Helpers](xref:mvc/views/tag-helpers/intro) simpli ### Prerequisites -Install the following mutually inclusive prerequisites: +Install the following prerequisites: 1. [Microsoft.AspNetCore.SpaServices](http://www.nuget.org/packages/Microsoft.AspNetCore.SpaServices/) NuGet package 1. [aspnet-prerendering](https://www.npmjs.com/package/aspnet-prerendering) npm package: @@ -64,13 +87,18 @@ These Tag Helpers abstract away the intricacies of communicating directly with l [!code-html[Main](../client-side/spa-services/sample/SpaServicesSampleApp/Views/Home/Index.cshtml?range=5)] +> [!TIP] +> Microsoft's **[Razor Language Services](https://marketplace.visualstudio.com/items?itemName=ms-madsk.RazorLanguageServices)** extension improves Visual Studio 2017's Tag Helpers development experience by adding context-aware IntelliSense and syntax highlighting: +> +> ![Tag Helpers intellisense](../client-side/spa-services/_static/tag_helper_intellisense.png) + ### The `asp-prerender-module` Tag Helper -The `asp-prerender-module` Tag Helper, used in the previous example, executes *ClientApp/dist/main-server.js* on the server via Node.js. To clarify, *main-server.js* file is an artifact of the [Webpack](http://webpack.github.io/) build process' TypeScript-to-JavaScript transpilation task. Webpack defines an entry point alias of `main-server`; and, traversal of the dependency graph for this alias begins at the *ClientApp/boot-server.ts* file: +The `asp-prerender-module` Tag Helper, used in the preceding code example, executes *ClientApp/dist/main-server.js* on the server via Node.js. For clarity's sake, *main-server.js* file is an artifact of the [Webpack](http://webpack.github.io/) build process' TypeScript-to-JavaScript transpilation task. Webpack defines an entry point alias of `main-server`; and, traversal of the dependency graph for this alias begins at the *ClientApp/boot-server.ts* file: [!code-javascript[Main](../client-side/spa-services/sample/SpaServicesSampleApp/webpack.config.js?range=53)] -The *ClientApp/boot-server.ts* file utilizes the `createServerRenderer` function and `RenderResult` type of the `aspnet-prerendering` npm package to configure server rendering via Node.js. The HTML markup destined for server-side rendering is passed to a `resolve` function call, which is wrapped in a JavaScript `Promise` object of type `RenderResult`. The `Promise` object's significance is that it asynchronously supplies the HTML markup to the page for injection in the DOM's placeholder element. +In the following Angular example, the *ClientApp/boot-server.ts* file utilizes the `createServerRenderer` function and `RenderResult` type of the `aspnet-prerendering` npm package to configure server rendering via Node.js. The HTML markup destined for server-side rendering is passed to a `resolve` function call, which is wrapped in a JavaScript `Promise` object of type `RenderResult`. The `Promise` object's significance is that it asynchronously supplies the HTML markup to the page for injection in the DOM's placeholder element. [!code-javascript[Main](../client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/boot-server.ts?range=6,10-34,79-)] @@ -80,14 +108,14 @@ Sometimes contextual information must be passed as arguments from the Razor view [!code-html[Main](../client-side/spa-services/sample/SpaServicesSampleApp/Views/Home/Index.cshtml?range=9-12)] -The received `UserName` argument is serialized using the built-in JSON serializer and is stored in the `params.data` object. The data is used to construct a personalized greeting within an `h1` element: +The received `UserName` argument is serialized using the built-in JSON serializer and is stored in the `params.data` object. In the following Angular example, the data is used to construct a personalized greeting within an `h1` element: [!code-javascript[Main](../client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/boot-server.ts?range=6,10-21,38-52,79-)] > [!NOTE] -> Property names passed in Tag Helpers are represented with *PascalCase* notation. Contrast that to JavaScript, where the same property names are represented with *camelCase*. The default JSON serialization configuration is responsible for this difference. +> Property names passed in Tag Helpers are represented with **PascalCase** notation. Contrast that to JavaScript, where the same property names are represented with **camelCase**. The default JSON serialization configuration is responsible for this difference. -Data can be passed from the server to the view by hydrating the `globals` property passed to the `resolve` function: +To expand upon the preceding code example, data can be passed from the server to the view by hydrating the `globals` property provided to the `resolve` function: [!code-javascript[Main](../client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/boot-server.ts?range=6,10-21,57-77,79-)] @@ -95,15 +123,17 @@ The `postList` array defined inside the `globals` object is attached to the brow ![global postList variable attached to window object](spa-services/_static/global_variable.png) + + ## Webpack Dev Middleware -[Webpack Dev Middleware](https://webpack.github.io/docs/webpack-dev-middleware.html) introduces a streamlined development workflow whereby Webpack builds resources on demand. The middleware automatically compiles and serves client-side resources when a page is reloaded in the browser. The alternate, less efficient approach is to manually invoke Webpack via the project's npm build script when a third-party dependency or the custom code changes. An example of said build script is: +[Webpack Dev Middleware](https://webpack.github.io/docs/webpack-dev-middleware.html) introduces a streamlined development workflow whereby Webpack builds resources on demand. The middleware automatically compiles and serves client-side resources when a page is reloaded in the browser. The alternate, less efficient approach is to manually invoke Webpack via the project's npm build script when a third-party dependency or the custom code changes. An example of said build script in the `package.json` file is: [!code-json[Main](../client-side/spa-services/sample/SpaServicesSampleApp/package.json?range=5)] ### Prerequisites -Install the following mutually inclusive prerequisites: +Install the following prerequisites: 1. [Microsoft.AspNetCore.SpaServices](http://www.nuget.org/packages/Microsoft.AspNetCore.SpaServices/) NuGet package 1. [aspnet-webpack](https://www.npmjs.com/package/aspnet-webpack) npm package: @@ -117,21 +147,23 @@ Webpack Dev Middleware is registered into the HTTP request pipeline via the foll [!code-csharp[Main](../client-side/spa-services/sample/SpaServicesSampleApp/Startup.cs?name=webpack-middleware-registration&highlight=4)] -With regard to `UseWebpackDevMiddleware`, there are a few critical points: -1. It must be called before the `UseStaticFiles` extension method +With regard to the `UseWebpackDevMiddleware` extension method, some critical details are: +1. It must be called before [registering static file hosting](xref:fundamentals/static-files) via the `UseStaticFiles` extension method 1. It should be registered for use only when running the application in development mode Finally, the *webpack.config.js* file's `output.publicPath` property tells the middleware to watch the `dist` folder for changes: [!code-javascript[Main](../client-side/spa-services/sample/SpaServicesSampleApp/webpack.config.js?range=6,13-16)] + + ## Hot Module Replacement -Think of Webpack's [Hot Module Replacement](https://webpack.github.io/docs/hot-module-replacement-with-webpack.html) (HMR) feature as an evolution of Webpack Dev Middleware. HMR introduces all the same benefits; and, it streamlines the development workflow even further by automatically updating page content after compiling the changes. Don't confuse this with a refresh of the browser, which would interfere with the current in-memory state and debugging session of the SPA. Changes are simply pushed to the browser. +Think of Webpack's [Hot Module Replacement](https://webpack.github.io/docs/hot-module-replacement-with-webpack.html) (HMR) feature as an evolution of [Webpack Dev Middleware](#webpack-dev-middleware). HMR introduces all the same benefits; but, it further streamlines the development workflow by automatically updating page content after compiling the changes. Don't confuse this with a refresh of the browser, which would interfere with the current in-memory state and debugging session of the SPA. There is a live link between the Webpack Dev Middleware service and the browser, which means changes are simply pushed to the browser. ### Prerequisites -Install the following mutually inclusive prerequisites: +Install the following prerequisites: 1. [Microsoft.AspNetCore.SpaServices](http://www.nuget.org/packages/Microsoft.AspNetCore.SpaServices/) NuGet package 1. [webpack-hot-middleware](https://www.npmjs.com/package/webpack-hot-middleware) npm package: @@ -161,19 +193,35 @@ After loading the application in the browser, the developer tools' Console tab p ![Hot Module Replacement connected message](spa-services/_static/hmr_connected.png) + + ## Routing helpers -In most SPAs using ASP.NET Core, you'll want client-side routing in addition to server-side routing. The SPA and MVC routing systems can work independently without interference. There is, however, one edge case posing challenges: identifying 404s. +In most ASP.NET Core-based SPAs, you'll want client-side routing in addition to server-side routing. The SPA and MVC routing systems can work independently without interference. There is, however, one edge case posing challenges: identifying 404 HTTP responses. Consider the scenario in which an extensionless route of `/some/page` is used. Assume the request doesn't pattern-match a server-side route, but its pattern does match a client-side route. Now consider an incoming request for `/images/user-512.png`, which undoubtedly expects to find an image file on the server. As such, if that requested resource path doesn't match any server-side route or static file, it's unlikely that the client-side application would handle it — you probably want to return a 404 HTTP status code. -To distinguish between these cases, a C# extension method named `MapSpaFallbackRoute` is used in the `Startup` class' `Configure` method: +### Prerequisites + +Install the following prerequisites: +1. [Microsoft.AspNetCore.SpaServices](http://www.nuget.org/packages/Microsoft.AspNetCore.SpaServices/) NuGet package +1. The client-side routing npm package. Using Angular as an example: + + ```console + npm i -S @angular/router + ``` + +### Configuration + +An extension method named `MapSpaFallbackRoute` is used in the `Startup` class' `Configure` method: [!code-csharp[Main](../client-side/spa-services/sample/SpaServicesSampleApp/Startup.cs?name=mvc-routing-table&highlight=7-9)] > [!TIP] > Routes are evaluated in the order in which they're configured. Consequently, the `default` route in the preceding code example will be consulted first for pattern matching. + + ## Creating a new project JavaScriptServices provides pre-configured application templates. SpaServices is used in these templates, in conjunction with different frameworks and libraries such as Angular, Aurelia, Knockout, React, and Vue. @@ -228,6 +276,8 @@ The application will start in development mode on localhost. Navigating to `http Open the *.csproj* file generated by the `dotnet new` command. The required NuGet and npm packages will be restored automatically upon project open. This restoration process may take up to a few minutes; and, the application is ready to run when it completes. Click the green run button or press `Ctrl` + `F5`, and the browser opens to the application's landing page. + + ## Testing the application SpaServices templates are pre-configured to run client-side tests using [Karma](https://karma-runner.github.io/1.0/index.html) and [Jasmine](https://jasmine.github.io/). Jasmine is a popular unit testing framework for JavaScript, whereas Karma is a test runner for those tests. Karma is configured to work with the Webpack Dev Middleware such that you don’t have to stop and run the test every time changes are made. Where it's the code running against the test case or the test case itself, the test will run automatically. diff --git a/aspnetcore/client-side/spa-services/_static/tag_helper_intellisense.png b/aspnetcore/client-side/spa-services/_static/tag_helper_intellisense.png new file mode 100644 index 0000000000000000000000000000000000000000..9f3d842dce687b47e4e1c15cf2c25548e29e9b98 GIT binary patch literal 21049 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 Uc=QeC8Cooj=RlQmr4K>>7lsJLoading... *@ + @section scripts { From 37f1da253e54c08a810e94e3c49d68332035fb26 Mon Sep 17 00:00:00 2001 From: Scott Addie Date: Fri, 23 Jun 2017 10:44:40 -0500 Subject: [PATCH 15/23] Remove unused Tag Helper from Index view --- .../sample/SpaServicesSampleApp/Views/Home/Index.cshtml | 3 --- 1 file changed, 3 deletions(-) diff --git a/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Views/Home/Index.cshtml b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Views/Home/Index.cshtml index 1f745cf3048c..2b7be1ccf65b 100644 --- a/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Views/Home/Index.cshtml +++ b/aspnetcore/client-side/spa-services/sample/SpaServicesSampleApp/Views/Home/Index.cshtml @@ -12,9 +12,6 @@ }'>Loading... *@ - @section scripts { From dbfd8c597459b82199c18d53d06be6f0f9ef349e Mon Sep 17 00:00:00 2001 From: Scott Addie Date: Fri, 23 Jun 2017 11:57:56 -0500 Subject: [PATCH 16/23] Update TOC --- aspnetcore/client-side/index.md | 3 ++- aspnetcore/client-side/spa-services.md | 35 ++++++++++++-------------- aspnetcore/client-side/toc.md | 5 ++-- 3 files changed, 21 insertions(+), 22 deletions(-) diff --git a/aspnetcore/client-side/index.md b/aspnetcore/client-side/index.md index 0dba8b931a64..d6b805445131 100644 --- a/aspnetcore/client-side/index.md +++ b/aspnetcore/client-side/index.md @@ -18,7 +18,8 @@ ms.prod: asp.net-core - [Manage client-side packages with Bower](bower.md) - [Building beautiful, responsive sites with Bootstrap](bootstrap.md) - [Knockout.js MVVM Framework](knockout.md) -- [Using AngularJS for Single Page Applications (SPAs)](angular.md) +- [Using AngularJS for Single Page Apps (SPAs)](angular.md) +- [Using SpaServices for Single Page Apps (SPAs)](spa-services.md) - [Styling applications with Less, Sass, and Font Awesome](less-sass-fa.md) - [Bundling and minification](bundling-and-minification.md) - [TypeScript](https://www.typescriptlang.org/docs/handbook/asp-net-core.html) diff --git a/aspnetcore/client-side/spa-services.md b/aspnetcore/client-side/spa-services.md index f8eb0f538a6a..c2162795f0f3 100644 --- a/aspnetcore/client-side/spa-services.md +++ b/aspnetcore/client-side/spa-services.md @@ -1,5 +1,5 @@ --- -title: Using SpaServices for Creating Universal Applications | Microsoft Docs +title: Using SpaServices for Creating Single Page Applications | Microsoft Docs author: scottaddie description: Learn about the benefits of using SpaServices to build a SPA with ASP.NET Core keywords: ASP.NET Core, Angular, SPA, JavaScriptServices, SpaServices @@ -13,7 +13,7 @@ ms.prod: asp.net-core uid: client-side/spa-services ms.custom: H1Hack27Feb2017 --- -# Using SpaServices for Creating Universal Applications with ASP.NET Core +# Using SpaServices for Creating Single Page Applications with ASP.NET Core By [Scott Addie](https://github.com/scottaddie) @@ -249,6 +249,16 @@ To create a new project using one of the SPA templates, include the **Short Name dotnet new angular ``` + + +### Set the runtime configuration mode + +Be aware that two primary runtime configuration modes exist: +1. **Development**: includes source maps for easy debugging and doesn't optimize client-side code for performance +1. **Production**: excludes source maps and optimizes the client-side code via bundling & minification + +ASP.NET Core uses an environment variable named `ASPNETCORE_ENVIRONMENT` to store the configuration mode. See instructions at **[Setting the environment](xref:fundamentals/environments#setting-the-environment)** for more information. + ### Running with .NET Core CLI Restore the required NuGet and npm packages by running the following command at the project root: @@ -257,30 +267,23 @@ Restore the required NuGet and npm packages by running the following command at dotnet restore && npm i ``` -Set an environment variable instructing ASP.NET Core to run in **development** mode: - -* For Windows: - * **PowerShell**: execute `$Env:ASPNETCORE_ENVIRONMENT = "Development"` - * **cmd.exe**: execute `setx ASPNETCORE_ENVIRONMENT "Development"`, and then restart the command shell for the change to take effect -* For Mac or Linux, execute `export ASPNETCORE_ENVIRONMENT=Development` - -Run the application: +Build and run the application: ```console dotnet run ``` -The application will start in development mode on localhost. Navigating to `http://localhost:5000` in the browser will display the landing page. +The application will start on localhost according to the [runtime configuration mode](#runtime-config-mode). Navigating to `http://localhost:5000` in the browser will display the landing page. ### Running with Visual Studio 2017 -Open the *.csproj* file generated by the `dotnet new` command. The required NuGet and npm packages will be restored automatically upon project open. This restoration process may take up to a few minutes; and, the application is ready to run when it completes. Click the green run button or press `Ctrl` + `F5`, and the browser opens to the application's landing page. +Open the *.csproj* file generated by the `dotnet new` command. The required NuGet and npm packages will be restored automatically upon project open. This restoration process may take up to a few minutes; and, the application is ready to run when it completes. Click the green run button or press `Ctrl` + `F5`, and the browser opens to the application's landing page. The application will be running on localhost according to the [runtime configuration mode](#runtime-config-mode). ## Testing the application -SpaServices templates are pre-configured to run client-side tests using [Karma](https://karma-runner.github.io/1.0/index.html) and [Jasmine](https://jasmine.github.io/). Jasmine is a popular unit testing framework for JavaScript, whereas Karma is a test runner for those tests. Karma is configured to work with the Webpack Dev Middleware such that you don’t have to stop and run the test every time changes are made. Where it's the code running against the test case or the test case itself, the test will run automatically. +SpaServices templates are pre-configured to run client-side tests using [Karma](https://karma-runner.github.io/1.0/index.html) and [Jasmine](https://jasmine.github.io/). Jasmine is a popular unit testing framework for JavaScript, whereas Karma is a test runner for those tests. Karma is configured to work with the [Webpack Dev Middleware](#webpack-dev-middleware) such that you don’t have to stop and run the test every time changes are made. Whether it's the code running against the test case or the test case itself, the test will run automatically. Using the Angular application as an example, there are two Jasmine test cases already provided for the `CounterComponent` in the *counter.component.spec.ts* file: @@ -304,12 +307,6 @@ The script launches the Karma test runner, which reads the settings defined in t ### Chrome -## Distribution - -## Deploying the application - -### Azure - ## Additional resources * [Angular Docs](https://angular.io/docs) \ No newline at end of file diff --git a/aspnetcore/client-side/toc.md b/aspnetcore/client-side/toc.md index a76ccec67450..79566466b7e9 100644 --- a/aspnetcore/client-side/toc.md +++ b/aspnetcore/client-side/toc.md @@ -3,9 +3,10 @@ # [Manage client-side packages with Bower](bower.md) # [Building beautiful, responsive sites with Bootstrap](bootstrap.md) # [Knockout.js MVVM Framework](knockout.md) -# [Using Angular for Single Page Applications (SPAs)](angular.md) +# [Using Angular for Single Page Apps (SPAs)](angular.md) +# [Using SpaServices for Single Page Apps (SPAs)](spa-services.md) # [Styling applications with Less, Sass, and Font Awesome](less-sass-fa.md) # [Bundling and minification](bundling-and-minification.md) # [Building Projects with Yeoman](yeoman.md) -# [Using Browser Link](using-browserlink.md) +# [Using Browser Link](using-browserlink.md) \ No newline at end of file From 8cb4185e1f72149968d81ecd8d6be994f26a4afb Mon Sep 17 00:00:00 2001 From: Scott Addie Date: Fri, 23 Jun 2017 12:57:38 -0500 Subject: [PATCH 17/23] Start publishing section of content --- aspnetcore/client-side/spa-services.md | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/aspnetcore/client-side/spa-services.md b/aspnetcore/client-side/spa-services.md index c2162795f0f3..0ea429e1a25e 100644 --- a/aspnetcore/client-side/spa-services.md +++ b/aspnetcore/client-side/spa-services.md @@ -254,8 +254,12 @@ dotnet new angular ### Set the runtime configuration mode Be aware that two primary runtime configuration modes exist: -1. **Development**: includes source maps for easy debugging and doesn't optimize client-side code for performance -1. **Production**: excludes source maps and optimizes the client-side code via bundling & minification +1. **Development**: + * includes source maps to ease debugging + * doesn't optimize the client-side code for performance +1. **Production**: + * excludes source maps + * optimizes the client-side code via bundling & minification ASP.NET Core uses an environment variable named `ASPNETCORE_ENVIRONMENT` to store the configuration mode. See instructions at **[Setting the environment](xref:fundamentals/environments#setting-the-environment)** for more information. @@ -285,7 +289,7 @@ Open the *.csproj* file generated by the `dotnet new` command. The required NuGe SpaServices templates are pre-configured to run client-side tests using [Karma](https://karma-runner.github.io/1.0/index.html) and [Jasmine](https://jasmine.github.io/). Jasmine is a popular unit testing framework for JavaScript, whereas Karma is a test runner for those tests. Karma is configured to work with the [Webpack Dev Middleware](#webpack-dev-middleware) such that you don’t have to stop and run the test every time changes are made. Whether it's the code running against the test case or the test case itself, the test will run automatically. -Using the Angular application as an example, there are two Jasmine test cases already provided for the `CounterComponent` in the *counter.component.spec.ts* file: +Using the Angular application as an example, two Jasmine test cases are already provided for the `CounterComponent` in the *counter.component.spec.ts* file: [!code-javascript[Main](../client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/components/counter/counter.component.spec.ts?range=15-28)] @@ -299,13 +303,19 @@ The script launches the Karma test runner, which reads the settings defined in t [!code-javascript[Main](../client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/test/karma.conf.js?range=4-5,8-11)] -## Debugging the application + -### Visual Studio Code +## Publishing the application -### Visual Studio +Without SpaServices, packaging up the appropriate client-side assets with the published ASP.NET Core assets can be cumbersome. SpaServices includes a custom MSBuild target named `RunWebpack`: -### Chrome +[!code-xml[Main](../client-side/spa-services/sample/SpaServicesSampleApp/SpaServicesSampleApp.csproj?range=31-45)] + +The target has the following responsibilities: +1. Restore the npm packages +1. Create a production-grade build of the third-party, client-side code +1. Create a production-grade build of the custom client-side code +1. Copy the Webpack-generated assets to the publish folder ## Additional resources From f8770d1b76a1332df4641325fc438f265ece0340 Mon Sep 17 00:00:00 2001 From: Scott Addie Date: Fri, 23 Jun 2017 14:06:22 -0500 Subject: [PATCH 18/23] Finish writing publishing section of content --- aspnetcore/client-side/spa-services.md | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/aspnetcore/client-side/spa-services.md b/aspnetcore/client-side/spa-services.md index 0ea429e1a25e..5bcb79b5f982 100644 --- a/aspnetcore/client-side/spa-services.md +++ b/aspnetcore/client-side/spa-services.md @@ -307,16 +307,22 @@ The script launches the Karma test runner, which reads the settings defined in t ## Publishing the application -Without SpaServices, packaging up the appropriate client-side assets with the published ASP.NET Core assets can be cumbersome. SpaServices includes a custom MSBuild target named `RunWebpack`: +Combining the generated client-side assets and the published ASP.NET Core artifacts into a ready-to-deploy package can be cumbersome. Thankfully, SpaServices orchestrates that entire publication process with a custom MSBuild target named `RunWebpack`: [!code-xml[Main](../client-side/spa-services/sample/SpaServicesSampleApp/SpaServicesSampleApp.csproj?range=31-45)] -The target has the following responsibilities: +The MSBuild target has the following responsibilities: 1. Restore the npm packages -1. Create a production-grade build of the third-party, client-side code -1. Create a production-grade build of the custom client-side code +1. Create a production-grade build of the third-party, client-side assets +1. Create a production-grade build of the custom client-side assets 1. Copy the Webpack-generated assets to the publish folder +The MSBuild target is invoked when running: + +```console +dotnet publish -c Release +``` + ## Additional resources * [Angular Docs](https://angular.io/docs) \ No newline at end of file From e3746b03a1b27d2c6c0399b919d71d043d6723af Mon Sep 17 00:00:00 2001 From: Scott Addie Date: Fri, 23 Jun 2017 14:13:37 -0500 Subject: [PATCH 19/23] Remove original, unused images --- .../_static/global_variable_original.png | Bin 23957 -> 0 bytes .../_static/hmr_connected_original.png | Bin 27722 -> 0 bytes .../tag_helper_intellisense_original.png | Bin 16315 -> 0 bytes 3 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 aspnetcore/client-side/spa-services/_static/global_variable_original.png delete mode 100644 aspnetcore/client-side/spa-services/_static/hmr_connected_original.png delete mode 100644 aspnetcore/client-side/spa-services/_static/tag_helper_intellisense_original.png diff --git a/aspnetcore/client-side/spa-services/_static/global_variable_original.png b/aspnetcore/client-side/spa-services/_static/global_variable_original.png deleted file mode 100644 index 354671c12e20470f4edefbe8d73d942b7cb9018f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 23957 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 DYO{}Z diff --git a/aspnetcore/client-side/spa-services/_static/hmr_connected_original.png b/aspnetcore/client-side/spa-services/_static/hmr_connected_original.png deleted file mode 100644 index 684cc868ca921dcf09b366dfd34dc7896f37d49e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 27722 zcmd?QWmKC{*EUE6XiI@oS}0y5c%ZmLu_7r@T!NJ15Zt{j#ft?9#fumBgrLO*UYcUT6c2G%F%u8bDeANJN%QX0^u{tXE-=Ggi4BX8aOz(KpdQh zZBHKE-+4}pmvH~{z*R#*2B&O*dgK1$v6ZxnG!9N>48gT2E)LEkoKGLMIj?L_~y=N)_j^1rClA&TC&< zT60=jFKTvm8lVF!pDL5ED}}NUIru%Tg8OSn6;@dzUR7IFYO0U4!s@IvYW!@DG&Huf zTn_9sZbD4v%t|I7s0*9-PNppJxxA%TdCRD*x35{ z2KZXJy1L>}exs!grln1w;`&J|oW)F=$Rrv{qntviSxjS(WYd7M>g7sUhBJCJ5jp*3 z_Uuq%PBa(({lO$w-T1eGFyUnf8Dx3+F&GWlYGf{!dbKIjXO`~BZb;_6@%a&-@`4V zYjo1al`{W*%%0ULUNKAWxBoK*LGGz^@4NOM#M96u(z1rL;DoTye&xXr=B14T;{0N! zO%cV36Qlhh#+fZlo5*aFszMX1!5U#HQLIUo2WI(WMO)%bTW9$QX-?bhgwyCl3wL4< zGM10GS4|2GEOGl-=54=9&Ai4TuqCF{?oQM0&fekwe&eIouAb$tm0RNb#2B}-T+eUy zo;h3IK@CCNDe0O?kuHcZRd|AZZJGCQ9L+!!*KC&X{&$Aqq>nx6_A}Y4lO<038M>>b zI(tZmy*Br#sHo`Z*icwhXmWCVbXZbCcuGo2Xxg8o`qa$w`ihwJrj+`EfbmaGjDhjUp3NUU7hw};u=eHH{wvt{P2Ff;Zr^6n z@^#taHFEv7{_1utC3p%R(NkBo6Pb9Np0(2ub?~PQU5}pWN*YELUXCOjj2GYR*0r{_ z_6^KT4i3%^PE1Wr4NR`17RP)3ZFjGo_nh5MEN%_2olIWe?v4!Y&Hmf|*R!)YfjwTh z-t0QYPTrjM++I&^Z*TAJ9%7HT*Di04Hdn8&)^Bfb??I7vcjvxdNXclX{7bK@}e5(j675ngiNA!|a&Qyd(gzc3t}7mxnGKK)v&pz-xJFu6Hg z5=5aw(Rp9%*V@phyniELeR90yQ!P?h!utjcV5b7Br~dsje55pLJ$=%7kkb;sf$^IO8`smp9t zZk(DyQr|D^Ym(Wg+(8A!0Y-0p^i$f+EwK2XYMQO9zXDt9KLhM3E_n=KJn-$}Nl(rF zSELPY%C;bTzjyPsM&BlF>d#n|BpjxH0HVYvV3q0nyk3Xdt5!DZ*1G_ap9bkGlk=Bf zUd_C$1n9Ap`~mvQcAyT*l6Bd)RAn0h-qPck10F0M_h_%3404aUspOj?Q% zmYV0rD#kAzA2ftWn&?(C?p?T7D?DB9q$kPcP z4to%WV)b$b#XB>KfyG1kC3zI+jXX|O2i7-Kd%wND0D1UHDtk1PXdOh?5d7*-T!T>j zfCL!wTczh3)jgk-nGlLeGdW^(Fs4lYCmcoACPk2})MLkx=91pCG2s;V(2HWkB0nn{ zc?7Hf%^AH&(?=DXC3QggBpOsz6R=Qe zCed?}bUM-Au|`76ljm6pv$>3ivrN%3Xk^wm^2q@Br8zO^sabN0`P&Ul`AH*}p;sR8 z(tnn%^K(anP_AaSPK&5O$8HZ(Ki9CulN4*)*wqRT15O@b`_yNb2}1Wz^A2%^R9&A1 z!<;1SG!$$;HRq+;Z*<4$gbNZ45ENW@;{SdGK|ig?22>xySB!EQ_2CGB$@1bpOJV$P zVdku`&}CjCzdw94^Ij3B233XI{*u>jOf`3Vr`}NZh+sEUD*lS$dYKtS8E6q7nu3SVAaMv{vkOI2>H7c%UWE}Oe@FUDr z2f`NgL=2Tee3S?9QK~I-^8Nl(t!N+LcvP$69LMb|#6+aT8dU-U8(d2_yiyzHP*JUy zWR1>Cca%OIY1W_jEPS@(!NvLfCMom-M_rTn9~Jb=?Hgvf4-g7BY1yTkCycB^e}U9I zSQ|92?x&{rl+0Nb;rV!l@r_rhSr%_b18U&R0=xk4$_EreVHNQu7w=3a@#3oUz{r{j z<`H>FMucwxj-HVY238Zwc@%fVo%0u-P#pfq{+!L`zxKFJv(HCyC(PV#CrD_KKM@qvhmoo#w|f{;{^l^P07H# z!5Vo7O?(m$r~uvStZO{5=>%&z)VNeQvG)q67#%SSwLtdidNap62MqrU_xWe{YAk3c z6uQ%!w&``2L&F28C8oP1*^u1cISui|K-mlUp6Qp17a{$~M-raXbH@G!3VjhvQVRJk zr#IheL(6{Of+alt+uMf%z<2$tAK1tTx34({cepbM877rx{3KiW;ckTaN=hsF5g0Zc zV;h|rSj|Z_%MKt~5S|NX%23Lw6scm_`3$F1)>`R(2(E8`pDo`w4guNA_`iXk^p*;_ zOFtk(C}_7mr1)|a)(_)9uBeY374L%uklp|YHN>g?C|9zk*V_v+DThSdyphs@2Xk*- z(sTRQj%K@HHX1LHg>|0KzK&Z2+bhjB?F!g1d>4d|*@@SHZTAz3hL~8?qws{YfT`ko zADeY-(N5jue{>ONF`##^2l_g1>gS}t5zKLNyx}=f$aq$ShyXS*_foD5q*C@&O#hKL zsBr|_himbav^aQl3ZHuhDTX&KAhTH`T=JGkHZc$^E8dI$y{t^YGlWm%WFi8 ziDiC(BXGSZQDj{I^^q~CcNanZe2?j6m0mcJlQ-OueC3%)egA94djNdd(k)}e9S51A z4M|ms|8`nrzd|u`u)Zrb+rorfVC&P`;R{`$fQ-Yd3}y?Y*cqCx-iXvkD6DyWlZMDz#5G;2_L! z^RzO1+8Bha`U^8nTzQA@61mN=n`tGbQ)=%;tsba9=0-#1>eY zpXXx(L3ydXK;or)7>I<5R*e@F_d+P1$FX|_?TbE5ISYmAtXcFz*tFj`7+TWiiYH5@ zMR@-Q;$EqJX7V6Q`Pm9me`-^m8yLv$e$dBx{~Tz)k$zEMq!@&Z zA`H&fBgBy#9f9HM=9fQP;a?63CF&?2IV1o2I_pF`qd4K%aJ9?a^O8l(=`GQFajFqm z{~A!})nVdW=Ee>@9BWip4Hi$zu~2u?nS}al^i(YTG0xZMZ@>pQZ}I*^ZN7Z{f9tgB z^`WloB)y;?&fmkhzp&DG7bRklbDXc(M{r0u%4*)U9Uq62uEGNJQF4ojN)qSu#aAHs z%b~vK)cyk;G(KedW&B`$dk8U(p5$jV?3dpJjiBEP`GwuqtjdFf%BIx}FhG*RhpG7&l^BBIQEF+93yCI9W+uE$bYDV6T zX@ELbs(btHE=}&>fQF8cmd6+u(^Wi>F^!?G&n+YD;|3KaT`vmirj*PNj+2 z@2W1#jMH^IVq(Y*6T?>6^b2$c%)y=+m|1Qn?hGSe_+06tR>o>f{1>@!{{}0Tlt)K0 zV0b;Vq#&ZgDOg*n#nlWpol<$3+v{I)GzG2IyPiu1-szQDOVq^|6wMMx7XuNr)OX8S zp3yOZmp;kcPbO%HLw>=h7e3I{qR<4#ZXaCgdKop^AX$Y&uqsX~^N)bWHh7~OabZ=S#Xhr zcEBv!wwd7D4R+TBK&8gTxAMtn%7-T=4apb>m(@V}cpB#&8-Z!}9KuNI^__EDkL)QJ+jJ*jd<{OkX-gfQfn3}))wb}o)g(-2LC-Bc3iStbdret^ z{bF`omoGJDj-FA>$sr+|f__)jZpD|}|8}=4m6$M8PH&|v4xrkakW%BilkM-(oPSyS zv|RELbJ$!CA9!GbJk@==637xC+<9-NS(W;f@6)b1bE#9)`pX16aIiQD^IRdO&!t8l1wcB{q1ajknPd3RTw+xIW5(A>vLUJ`;xhf&Xz4dypEy4xOX zUshqSD>G#0X}*c+Ou|;sjHv5w*TsQBqhH=R028)*Q@>T)amrzZQ#-iv%ghu>S}Ngp z#-_Xl2mE`4S*(i`X?Sn1lv;`y4rz2KozcK0fx3AyNeY8atVb*=f#8xa5ueB{7mnj^ zZp_g=pPWCc>Ead8o2{mms6BrvD;p$xv6ht;&Loac>4?U6EV^l&9`U-FY@WIxkE1T`<|*>)ffsdoytqHX#y@@`#?PLX?ZV@#wH`D^_bsbGKtS4WQQkvhZ&b zRViZRK@BF-XhFo~T`?>A&~(D6*(u}TAG1E>OI&(Ia+=@1F3LFX+t|Q~`E9u-%>6)E<$)n_0n3NEfIOZ55WvTYe*f zw76-LT^u>+=e}lgcz#z*P?RIqBL7IZea*JVxkX`5S)qVjq6bDdJ^Aaddj{I9WV3u4 z&VOWLQpedOne`gO!aZB?$4$Mw=DAS*MxEEe`PLXLlroY8MZPqXhPmR( znz)#e^0c`Qz)a5)e>WL8Gk3*gezd=K(&BHzlqZ#mQ1TP%*-D@$tJcqGU_XpZcjTv1 zAH^WbClVnVNW_2J2sk+Mz-UbSS-qnOFK95ny`J~*8CF#xY1#udvug*e`PZ}PE-2GS z&*D%xp7t)TJhx|Jj7m0YWV>xaCDIL@0<0v*VU6lW*AMK=3jP$@shQjcTF;7)gKu1W z{mbZQi_rm%1GRl`h8DIrL}W<$gpMJ3P*Y{& z%S<0*cl3~2Oa0o5!ECsAbx@@5`tolx$a$%f8M@b!jC3bKjU}>?$a{Qr_sZ*VWIM(> zLdV4#%}#im*CL#0s0D6LI-RGxW<~yiGch*u`%1=n{!={M%h2U>dqiwIN?K2^2T2yV zg|7{ae9rW>EC?uTKD}6ZK3?1_7Wg}k^Am5oeIR(BlMt_3RX+~9qeptd!e}-`{eoAr5%!_u0 zdf8Y~Oz5+uZJJ(LTpJJ4K-5MSL)WqK5+#(P1}*9Qho1&bmANIm#axMZmN6nAbLfg=(eQ^9VRJY^c+0N|!s*|otTQqzHZFI}l+3{^Cktpij3u)R_Ta}U~ zt?@m)^;IxC@MIOQKay#?+NL2VQ-c&8(`1K1X>_Gri3cZNo1sO`0^#44H{1jgZ#=nTOSCjV0ZNov)U z9;~W-YlXX_lI&`g_p-XKS-jj$yf2vO3MBy1fmHUqqqUUw9F4i_f8hEsx)_i+APk(bA7B=`pE8K6l$~g1LZCV>L zJt@VH_!~Ozj8N;H--wLjU{JnMVlL8|`i`y1TcW0za@lGP)DBS77pX60wFO;t@UK@W z6%VPtS7+YgsA*k;C~+_$M}Bk804*`+#*x&(_j8l~iZ>wW{yi8ls;DW+)47OV?0Qh5 zJW1aMLt|sfwwVzvJT6v*viRy%vrZ+F!i1(vND>_1*ofpJF!-i#fiI zF!w*zensUTM%W^d1-xS`eJK&tn0bVPQReo{xN2jyCG)JC4)KXgA!2y>+Jo>KQVwv& zs%!{jo^FU)>{o=x`vJRR(x9%LMl22ax#C^V^`s5QbYl{NlIqh*N`Y6}iktomIChqs zGh?M@*p2D-8&>3Hxv}dRE`~D@?gSo{_$uf$dmO$0%f@}npoXtU0eTVmk(+6rANHqN zEh0m?x%a5))Y}+e!Tq}kCyEkxc^Wf9azjc93y%3d%Myu7m=fw)Xir94Zui1&WKr2a z|EY222*{owFJp|IipF#s9#!Y3&lSRtoMFt z_`Uk?@lzgy>C5mV#FP~E6iR`wiNvu47oJOf^EJMBq`vFvQ(T+c&BV2w>-_L1^&cSm z(}#p)F&!||WlPY;-OQ*J2J^c)`qP8)sy)H$dGq=hsYk3&;(hKo{2!h#e%NIn#J>$u z1s<@+xI`>@lfffD|2_eM7bAvue z5CVJ~nClE7HnZuk4sUY>wh1_YKw1UC6gst}`F5Uq%ED~;|N5NaSQeFUnHCZQnd=+F znm3s%yq33g_B`N$JD(UcEX0QGCDYTPA1bjShJ7Z9F?Ot+L%$$O9T| z$dtzN=MCaT7lt=rU6&D`XnZg5_=qpc?xD>sCGRCTiDi#r4)p>?mV$kbxll3BRODp)5a*v{RC*NW3 zODJ2lg-)D3(z@6aCfOxCwnbYD$RzlMl#cFb?<({}E8IZ;?Q{u z{h6Yb`1&FiAM^VFAG12Gyn=(-jaZFio{(N1*olHUQ&mr#w&VNmwi)jo@l>`n1ad|Zx zE}uvm+O48HebM!m@nQ}y?|Tl)?k)F`M3Ad2KWeph(4^iu0`Jfhx==t@m`D>I$!qZ) zvlYmTb@0&DSxt?ZW+1;eyr-k7TQuQT7&HXM%{vMk^|?H9iU(H9mmVMueU8{uIgDry zDQ?WFZIuK1JQzU#AqNihOGMcolO6bFjec0{H&ybGFQAL>A`)cVM12#LfK0@8<{LWh z^9>VPu*2O<(|qp;{PpU4vxD<|N;(zrpEjjt^68>$WtW4tJ|Y97Jb3Q>>4zpgpVEfN z3nv%LYJ2ruQxT2Ptyd0ad1eFKZSBfayvdk@qU~R>uFpv|C+vePz_4qe6)2j%CC=|; z++{H>I{Q3;LBjDOc1~rCM(}z>13Pz8q$`N ziyw3B@~>Xli`Zu>hhBKa@BK<-GeyD2_P3(Dt|u?-iKq}f!`(|uch29UM;%+<`6pnd zgT@G${ifRZwlqe3Zw@Ah;j3FCw?!WSO+X{PC6n&MSec5l7n<_o6} zd85fdZ)UIGtoOih*MOZcH)`5d&+qmeO|u*4nnM22usO@BjUe(BjHGVxU0RoHqjcgl zbcBT+AvH*0j`o)PCNdK1ZwP1YT{g-QWbmwPh)O&Kgmafs<$ciYfT2(s86?gNpgTWy z{p*r>>0{!R%jihpG%WJ&o0`7y>Xv9^OPS{jmA!v>{lHGq zdiT#0ipb>c%NbRjEL=11HKPDNx<#QcpFxVGuEaABbDkz+!#I{m(~eEeizSoqt`dzK z8vb0i-m$(;vIhBjMi14~YyODRtjsvTWH$cY+s}6#$h4Y|pTQDkW7H`yij-jETg3&q z>wtROv+GX5Krt;s?$*6~F@ z-EzZdV12>bWL-}Ta4qmg+wo1kwxeR^^!QN`*rCCAuS*N8kv3h8g@Ma;XFWphnbM_D z*tw&WzxLhl(e#=!uEYL??u+e(JT?PgE%u~1DY@Od){9QZaL9Q?Tjv_3xi6;Eo8BZl zVu2KS@X+w0bvndGz;{iQr5ez(MXie)rJ0L7C$|bH>Oz=D*kTGq<9w&ck+7*Tv6lrC z7|YC_4}k~wLalHvYF}vLenq#(xR_nI9(QC4NKI0MtH!;D1duG5I*5+RW}uDcqX) zNR?9sVqk4c2XS54n32Ak>Wrn*h!!1K<2kO4Hsa6bM0XG0M9D7aPvcQ@5Mn){i| zob!a=fCLBug!k+jUuInOsUuqq&_>-GYf@iu!A_hN*_-Om9r$3p*%JBq^3p@ojlwW5 zT*e+%EtuZ5&JItI3Lm?nh~v*qv!(&N9PH{_MrQ`RCpLBPaPYLMv|s~?#2Kmz2>W*A z$^ie)Z?ly*Y76L2u#fAM)_fCeaStbyg61Rm^voRO`Ak)iz41#Z``U^u0Y~B6U#?(v z2UDdaW3R%y&XVOYOEyk-lM(ro$-gh*o>`7(khLm)K-ZnXzdv?Mpb86KOV7{n7~qMe zig)2iN=>1%iIuMR1hIWk2N%!(R;M}odLlb1r|-a+Zr-%0at6trhfs{jAJs;BV9g z1Bt&hS>cN%f#)(hi9A_jk$f>J>x@d@TG<1a(S1h0pt^l_Ak~AcELGb~uWG-G8}v2i zOX;IqZgW?>SG(y&-_lW08qNpp7e_?j$vWJ72+u^8&f9jb(ZiH^rX8S5uzm(5iXqc_ zf=$xdr?v}L%I)A8UV^;ez61uJW94ogQs&3s%_(kPdt%4}tt?cng4|n$aa|5Ik6EiM zCQZ1aWbdx6zBE5~JR%I=|UM z{hjdd*Lf{rt$i^aC%GoxGcZ?N3d?l=cs_@dZa7f)nfjpUy*gcWyjtGOb@GLC>V1h4 z82;eD68PuRkyFQla_Rrd`u7on2mfbvk8ohbQ&j-wO!rViI-Cl>zcAqc%+3E( zsirMDx3V!t19_eZTUMVNrZoBu{eXjG7o!0dB!lAZ44_Z?$k@KriW;O6XiYr9`MfjC z0EaLUpuS58Sf3di)N65Rm7OSCZu1-cU;cbJ1@+j(>y|n;YdC|B8t{gG;?X_d%Xh+qlX5%W^%iAc&$6sG}%i%Co zXisb^rg=EbblU8#eqLW%lXA`6|GUP`Y{-l>WF@t*d0KK_pv7g#F5pd@5*OHfwcl=4 zl4!B{r!ezK&Hr4Y_e$zIA;Hq*6~xsLPmjx-hXK9!GxRu6|BR9JBG zR08%eoKGS+`V-H#2oCp7zkLh;6jg2ZX!!}Sb>u71EIb@IP93$PUztO$Z@c7MHR&nZ01%c@vtM&nY!KDBOPk!+T&1~# zoco);->4P`A7u=}G$`$TF)m*|e-@f{aT=Kfpj@6*JqA67|Gd|#8-+%rZzP;^{&B>+ z*&JqBRXza8a59!(cldk;Iyn#^6$u^wzGO#-(oI~{X61Rv@o*-B0BSIW!VaTt??K<@ z)+n3V5`kSMgE8Fz+XZ4Pkj3TV`k|*1jNIEenk~=B?)|K-6(|bmN&H;J08iRGC>Ef{ zem}E2_9%znM)l5dH(o8`)Mv#4do4Z8>QT;ZZQRMQr#z2+zvUaGt@7c9N7YpY!(JKI zPavIDj?8swoTaqBv8yCgQKaxFajK-{ca7v@>1_fcc0xs$G}4cbs_HZH+7AjF&Z4$y zC@`L9T9k&$TY#wCzNkuIc%?p?GDeO<%l>kg?2n2yxZpl#yTt|Z+w z$(iiy^^Lt`N(we*KFr;%8`E4iJf-m(UyP&4)1%@&Z?TsCzxX$qAZVd~`_+ea>7U!a zhHFXiUP{y_qmkkBUm@}CqJng3u}nCi2k@VHO~7dtcuV!$_+x_@(CFZq+`RG4X|s zJqlRbCG;`s`z!sFi;$k!O&hwjfQ^h^_q|4>jzpXOoMcQ^Bi(7HPEMS09ESusIqiaw z%Q~vCTGQ(8dT76=C#!lyP^y>m;1^XAUy_Guqoiu+;&PKfQx#jQWz?$qSXVlBON+;Rv}e8aakswk&p5^rk8S*tl}>wsQXB zK5h#QRe{{%WFv4(GQV+SflFA!s!18Da4C9aMhx*{PRuO<#w+Hi)Cw5?=QD>(9GN1f zPlX(^PMfLO*_gu3=5=6HGCqG^%3z>!va*o2X66-;*5Yfv*L>e-ZRow@B7jm*Te)la zY?mbBR6PES)|;8tc${EpE~W8;E!hvJtm-4}(EFoo*7rN~lE2ED(&wu74=XoGH98Dl zY$TwAt*r1jt3zOhZRJ*a28W=$lDA4OCMKU%Gy=sOrBt}n3Tu1I!;kbR3GYfQ9uyQl zb7zoU`v%}^aJUm3^!joX${$oeyqLiKB1sc~1f2v&o-Ldu<0gL#WaX(1T(~_-fdsYy zG}nB&*qW)+eo>j}FXaOCh+wVv!M)wa&nS;RI^P|HSc)m(Fb1~Wf_p3T%(Z3XFdz~8 zIzkw$cYUM<3jD(*wq?E}D<0=p=<~H&zb?Pd1*s?CvuLRYEdq4EVK1D30I)17&J6gMVw4|hb^a;!v`x7zzkC;;Q zS>QK@zJ+lSx78KyGh(`>I2T<&)XJ|nBkRyXQzB^?ReUnlc8h^7`?xF&!31lEIr@QX zfO{n1uEE$PGC#fifw@NHA?cnli_KDUs$Eml-ftQ)q}!gFPSGsg==REpqhpz2yf&C} z$DXZ$k0uN~>`7^4V2)z=O#4E1nmD=WHWi$aO%WfwF%$Om(l5_ns2W^?1@NWf0uF)-L*t_IZq$jbBz93+G19 zUC1m_R_L)0o6*}1#fJx18Ae`y3&>#n-K)ldFH)L}j4_oz3!8+&6D-^Db$U}8Wf-zU8QHN1UgCpWO?Yz=aQM-9xwJ%w1a>J*yzDfArM^4_ImC{yJ{=yWJ z=yZR)(~8fJfL!Dav`s{RLE9fxVhb*#^&);XOM+J~mKnwGP%}?L<0CWIn^H}Z(RWnj zecMHn(%eVgMUb0#|AV;g_a^aNqa~+sb0E{9g_VT7%`kwzs^;S+Y0QP_1!dc17T>&d)n5-sPGR!XI+ho2;^nr?M(|Cm67|6M5@<>F8jo{j*39(UX znA-$%8~{AL?U}e~XWMko$SI{H;aD3~{I(!L$8v7s5i)p1?JRm9H)HU~y{c=yXTMq9 z`0EklRlZlp&K}}p+`0>~s)2)3eqa$8yleeZAr7eKu-gaYcm)oKh+fyo_oS_OReR{} zjcnYS89jC>n&3Au`pP%ik|+w|3iJcvw^{%Yj)cd^<+}Ucp#?UXUuMnp;a<~ zY63Afx3tf`{6Wo5hd*E`rpvYh&s_MH<=9rPWTQXc=fu8JGE%Bl9P}B`DE|o}qQ9*8 z8^g&f+g+Sex9EW+fu{eh5$|V7oJ!nif*F2Ml5|BKW)m5HRkDcy=4A$Jx`>32%u(sG zO1w-ugu`ow*O!>0yve}>bPX^w&_-rbF;<&aA8?$&boGHYN#DzB>@CgDn4cqK%Zyif z<5N=_T#@mdXV0H6HDW(7i@nUeTT)52oxCYh{3gfWRPFN%Iyh%o>=1Z3#$4*Yis^q{ z$lIh@7XKsP7LG;rYeZpd6oJ}4d(feuvq|~u@fklnb2{w56TCLLWN>$yFCQ^|#dvi4 zIFscB|9(KML-sIQ#TXl&g=F8rs`8?pO($Q3n!34u;aE}XeIko-yE?W|r5~Q#6bG~I z4oFqi7k8#X_+8|bFHgBz>Q<|QU&tk8a56#)Z+Y0Dbm^ShNemw^mE=34$u? zey=~I~UK%4)ie zNy`Iwbe?ijth+GAcfs!QH`#OnPftWt$457v-W6-F|8YBz%R7T*rIsUA%IN};6Ad@5 z_s;{wk}r;%YD*n&-F%>G)^P0cP=C{GXcw&-6m%BL9_mnL{{`3es@%I>3}vz1rJ0Hy z`~B|dLnZe3BykIKy^*fn^r!6zbGVWI%JOlLxI-nDybOtL-MXsTOfsq7AjIEmYG!Z{ z(_ZE>ab@zhTzAxQ5b<(i7g0Bz^+yr&CsK%%I~DEqP~5sXO}0)WT$-V?^+(i%x!waX zui-py^gT0&6d2?#e1!Qi#+^e=0bxbwi7~ne@lLXTR1Q^)25cOOk;QWTx%Vl?FF}%i zA(7f4GjGm>N_FF)!SRWVFkq)vc^#w3%YtXj^0E<}z{X7Suuu+=G>WuI!ThO}!*3!H zdSXG#jfbe~(C{g%osS)P+o0JZys9>h9X74uW9_vtN1xj>(iU1vs*~+;0SGsu z$_ZNgfI!UQE0gw&J30BXAj8{pGEqr@stW|^<%bpmoaIpemNzDqDxB;n5L#$$ zGrNO^uf~iEcjRtg=P^hSUJNly>arRzp@um{9V3V|2Emx$-!_h!DF>FEBQ>yolYLRs zrmjwyL$oi(Z&oFh1Dsj2VScb;xv``vAo3w|+MRIH_}Wv}&n^xxWpq;4k)q!HNRVYv zkF)-{1%)YSCYi16GTLVtHFBpzd_?0BD*W@cOnHG|l;PX)o;-6@0t-8P46yK-ZZ>js zi1)|ctGKXFxU$^*_)R?G&X)M{7q&|NHP>9k+RvMrF%S?EoM5-<+J=xxU&byIe!Fre zn7zc)D3vFU28M>7?b1y*WSS#QG8E#wru`|wFo7jLu@4H1zIb@u8xnO4NnHn4qe7e* z8o8sp+g9+EogXxg$g4Bb51jrgy-2+@*{BHKN!rNm;h}O(@2TQrISF^H?i&+5>MpxM zK%EpJ-WNHu!XW@vduD_()5bnN8@whygV)T%;J_UFuCqFI1S}I|x74;oZd-Pkw=k6} zd8;LFV&R;a?5j8RYDanKzC;fyeg2n5tL79IpqX@RYRr!wjos=Rj^%~ne;T(gIf{Kt zg>-bq(a<5*OHZC(*Pm)NkUi?g8!AN*YPHRMH-O=h!W4};Ifb2NG^N?2_R>KHvm zc^ZQi?;)91AgGr(_xpN?r#*|{T1XtDIym=(zdeW5S!aJPzhBc)`0PM^Ognab^rOR*^#YvxilE}u}o z!iY4akdtFd`AWHM>}&Zr53Sui zy3e@3y3UgVSRV(p52+;HlhkRWr{pyvj;KVC4CpBrlHzqD5r<9M_Ll#seJa+`tHKfa zQw(xqL|gcZIsz1zM{#?!F%e89+!T86z*lDkk+UN=E}3}*OnJ5Ggek>Y zR5^u3cjXy~fe#U0D+XRZIe_4lx{+$r`y?Q4`%7=K$T{afrc^3*k=<@Ty0sBB*T+L| zBrx95`2j`oQ-_+n15`FM)4%dv&bV#EymG{P)`pR-?~u~aF2ng`DjRl!1ho?-gdfmo zSJE*WC~})1YJ#~Im6-o4Vu>~?2Q?(n8{huX_qPnCG7x{1*vPs31q6#U!nwBk>LzYvivuF-o7T+RHzE5IvL+-6CEb%mvgN{Ne)SCNDU zV0jvA_(1wW(@ax}bxt20vy`9;Rhr*dsF8K;PbD2iT$}c~H!K}%!hlVT&dfC^&?iirexkjlIl$(wxDHoefiZ-kvbUT^`q@W!6xF0`o* z+bt1m2dJ>98Ommh7bc~JImPx@0%#0pwKIH2vNkWqzfZZ`PE9x-0ytRo+KkAd%U))d z-C%B}$QGMZdrBuyx%*UutQFj77Q&}M0j2V@miM&kc1kLm$se5!B3Lt{=iT-o@;qIU1D&RQNO6D$MoMQE_DIuujf|MaasO z$~ilKA$vFe3P?A66%O4yv}sPJGQ|0%VGascF6>m>FMaD59UD}0VfGqXC+;s{Vv%{U z_ZE5u95igvzJ1b_bS@(#@^R1GZ*{{d)+e#M8O>N}aX6k*)8JJFk7-jD>B)p~2$C%Y zoC%08-*tj2&x2=XZR8fidqP)c&W{I5S7xkMpcjCqUe_XK-Jyc=vvOf~HD7QQ%Q+&; zQ+GsDHT7DJPX2B7>!^G^Lk5b2r)njm-S<+k$=OcLXswxjtw#Jh8sZ@a%!!b$wnS72%;YDj7gV8(i> zL*V{IrdDg(RO3m1%iDaHSJN)5T`IGVjMM6%CWPN*z5#bb=*(S`|12@aFut51eI|iT z@jiMrX4$;wT!1lGJuErKxX%s%H5cEG?j830qy|Gxgme+a^OuxJe}A8DwK-l7SC!~2 zC0ED#lc&#kuo2>u(}pA`nI%T;fze^K+o%iQ^OUxHyQy&&p)-v9qg4Wm?FE(b+uHz| z{gLqdyqrG=kUvVP^>@AyqWQH#m{wkmCZJfxFb6e}6pPI8R=+2I>~rsg`lFn4=Dy5? zhAZ`=%|JyR?i*~Qd9U;@Z>F@L=*lL4n;rOT&Oj|Ck%3-! z`a<_nA7YKqN^w{^fzG&V6-C203t!EX;#xrLSB|Qn66^rj9uxn&@s8+(XdQs2I1`Pjas5Hi@N;=iVzZJX5(^Wi+`$c2P=AgEyAZ z*R?5no(IGpI%UIK>)Z$1QgN7cu7&K61u#WluuEN!&ToH%Y|vTpT;QF&U`SY8MqN?e zw`!(P%`})n8fzlKYt7mbf~8)Hi3B$$|KV3ceL#>*{ESI5&?491$nad3E0|c*Tq(Hz z{R|lkKocedF2GM3axBIjLFO5|c--~$%&*ydp{DOeR_fnC8D?cFo-S-IHV7Ag{sfgp zf6dMJsjsm4*Qk3ioRgu6xmn%D5k!_z#9-6-&uB^AO7iU;O%fqnTf*bXHLHPl<$7fQ zvHJq5{)oI!K#~`!tULNXaS2!_{bXSOXG5X$%%xr-FZ#Og6C%wD>4~L=#5&p zZ#6W@pb5-{?A2*Bwa@kT*mPkx@eUO&?C#4%J&D;Vu_MhJVLh)K-^DB0^Y*{Le;q?T zKxvO|Z*mXZXPP62Dx4nOzpznc2#Qpal08 zm>5~()S0W-rTa&ELyjuxJdwGEnCcPW{GPS+j=PkBTr;IM@73_TVS>3gLbkPrjztQ) zhU6nYYa1iDOVf1`eS_5kv^o1Oa(B|ZJps4G$hn`53H(dHQr`p~U%>{3f2IGmm-`)d z?9o0Uj6e4&-gk;4k(Y%5{xck?|MIY-;gnz@?#|HT?uOWJ4R;(z_jA@rf_PzraHkDa z-k_r;&t+KO_*$7mb=>>;1SMfF^Ga(nw4Ye{m8Us=kA`lspeo(Xe(1)N>vjb!)!NK6 z-C+ZLd_!h${ntJ=mG2D_@!5_J_QvM%PlH{~A+HffFE-G^d+O+imxr%Szw*J{M=+8)1+U?BMQKDWx?YhVp->D*~boet;i zBIR5fvp}RtZXg9NwwKuRf3^3WQB8GQy8#4+AV`rSMOr9AKzb37swANaNDCc;w9r9{ zO79&)=!ih*LI@!gQ91&VD!mCvM^RB!eAV}E-uH}gzHz>9+6?7b0ajoDlh2Ia44M{^%8HQo7hR25tH#veIqjX#iq&l7jtL&>*5d(o#- zYi5T*c^S);$RTESxWE>s&j88)sd{qwl#} zbo!13z|(7#^T=-W#pG6CE;Z;oe}JAA*YuE!U!@MY@~XGx)h-+BtlhUdDb(eisySbK zEe73_k?XNVwkBp2q;pNIQ>2xTfXn>mzt%LyM5t(?tBh?~;Im0!Ip7*u*9vtDLgQAj z?=IF3&L)@c-@e>Gkv#}2*kt&w3=w3S` z3Yhpyd1A}XJ;}G{yW6cBwF?ZYh{e2vJXyINnjX{g8&~E}b=uvtT8D2IR>|b8ON2?@ z^nuM1xvbbRlcy*l%*=X|#t++JcVT{_17?Oex2*sFmYoAG@a8+Bvqe|^mPxv9t9<&=Lx;~db~0M?0msbZ0FlWC6n!(s7E zNr`xpR%JGuKWBA_WN?Zae0y_DP#%J2H1up)D(kXD9brTQgGF$JbeE`hNsCRx%FE~^ zkq+fCtbjMB2xGkzBU`O%+Br+<{`8A(5%v}lI-WHIzd`|OP_wq#&U#q1qIvTj1Gqsc z@AK(k_Rnj|jfvn6VA+*Xtxctj#f;U7Rm7H~Z6`))X=lUEOkF0g4&N@Al|PfMEI;zl zExHQI|2`l>X3WZ_$Q~suhB;OMZ(Td^TuOu{c{Tuj*t1kIgC%Fk^Ejc%$?`l7x@W&m zsHp`nfCYfTYf)`3FfmuxxrG8Wrvd>|`aqXJNJ=1e5D&014E(o4C$cg?UjY3(>yJB$ z3Z%jMKUe&}E5(PAxNEK}Fim*jWIRW<@0H0o)H3XaR59e0wdBrhd)U%qJvn*>rv5NW zT{KfXS0AxI#_61uhH1oDDK;!hl6t)@e}q{U*h&W*R5e603%tbdpq0({hupFzBUS7_ z_{okjROYI@t8B?7z9Vt(SwqP3scDYnKuf6l!)FmvbbJ14ASUxQ;)>cgxI)u7dGHwu^0=L)9g>*%Yvl(yJ!<&ncDnf+J$#Zly+VU~{|4Gqwj z*H*oH$(_h1KL>Jfekt7_;oDxEf4JgEm;*VkZdPLxjT@P`M1r~_0u?&1%>-ol`fIX3 z6ZPWMektz$aA`Wd+Vy4fk8oWtv3|ebo(YR4%bV+({zYPTVjIV~&!g}-!WtUZ-*)%q zuVISZtkFMYW^WxtU-Re%FAXSG zpmJ89(*(9oXQ~Ws}rywM)MR{9?xHEr-KKpR;f5$QL!9@AVn? z(R^8f_Wjdr$2I#~Us~Q9{nE5-Dk^8yNs0rb>3L$RHG@ysXPlyy#aVm$Pf4pI>OCCeLB`W`B9RqkKuKp(pKt z{Qk%)BN%u(r%06Bme#v~b-o#yGn0tRXGUV7x{b;aY8R{mI@pAa>|YnSNWXi2L8P52 z8-&W19>g|w?Bs5`5>Qn#1{|mE!hhPGbH3oKwrC>Zr}lko=o0CYWcGRVwKb0Eg2KE$ z17EeaA4OLt(logOsJ<0}*;j9aT?`xdCRkIS1ga@@wm^KiW@JP{9cx4mFx8?tVdbhv z6Jbyy?tw7wq@C9P!D}ZOD^la+V)KrNCRnr0OyjY~jI>d#Gh~{g)iPNa?mEiEofLpj zpeZb8zvA)Pyb4(=Zv-w%>kY%p7x?h=WRiL)V#kG=Ofm@H-O993s(Ej;yv0V-!wRj- zb{j;-362;kdyVhj=a;Fnpv)g4Ig zY8S6JJmq7HzcQ!B(te}R zQe^(s;9)Iti?rz$)b5gA7FV{M-O4Yu7HKV^%L0+zzTmZ=*1N5!c*JH_h_HxPMDZxL z0;$TaWt>N9yR@}rSvcY!A_0X^^^0 zH8VcV!u?SRX+NADqA!o>-P$Mz7Ym|4Mw2+S>reOsQ3#%=Us==d#NC3dE$5t+Fkocd z>s`9SAnkXxhgEgK%*0E9T>5fPCjtS0Do@HT@NbtlI_Hev7Cr`V7G_x-4D^QjTfZ`Q zF$M2OPDknWU3_ehGG8@!sqi!Q_7*!vRtb9X?~ND?d!8Q0ZJ9R$ptLVQyC%Fo!&YUrPt{0hb)DR=#Qo;(ssD6D}1k9V_uUYlGYzrL(d_j&{u%{w2^X zAE$S+J?b~A6*q4>r>54rx|uo|+51()1xd#;7eus7E;~b)L98*d)3eJR)z=C=)Oe$d zyh@N1!jXufh34?1ReEc7n|l6#YA7NcuZ*5Dt$fv=rfL5R1+~v{&wAR_U@D+kz@X|T z>BS!zPswX2>NG1blB^5kDLiUgMus5pCol})tSb;aq zXPOb?2mur`dCNsic!4+33*k8GW!1?VS7fnNc`qrN4HHO_M2yYd6Lxv%BAy~bwtzpv zDLfTgRX7f5p?1DtwR7fRJ6ki6UxcIYVuHz-rjfwWxje3l$*v!P>zV;L!T%^AcJ#4f93~Z>XwPNcDcl(gcJ{7&fLqWPGXR@)#s0RwFmwt3 z>X83-F9$KqJ4-cMy1fA7AV##AuRIj7wO`Zk<;ed_qBiy7oF$oJnD{bB;atp-9_Q+U z3=hM%JbU@IrzO+u7lnAVJ7-8CM2~jT#qL-y?P*efnGP-B>sy&^84V6)d5lwVZsjgj z+;Xo1SAKK2dE3&L%xjy|C5%sVL*vZa{=m7p&KsOrpJ?y>ph_gSMcJY#;)kIvJ2BIq z4qcH{#YQM~6W|J&RM1|Oxm~Fpxokp=M zx`2pW8toFZwT(rl=}bmLT1&;VeoH`Ertcqgmc{)vSx}jf3WI@7pd2BOPTME5`S-1> zhzNMN!jlwMZ%T@7gyX4WfXocOqnr)nOTA!>iyoKdD<`&LGR;omF!s5FU99lC8~qVd zKLG&w5H|cajlaYKE6}FL*@#wTE*KF-<}}8Zh6=P1*Ess3{+K=H@~7E#A$dv1uY|3S zIP7=gQTXMZAw6XUHeR*}V*1#Uo?OwoScgsjw>rO8--kb9>u)uEVilE`kACwscaO`a zU2G)M8mb%V%3>fZ&u`$H=0NYgD9@usO$;PO!1T)=2gz(-9P){}!Q^6sAeN)4?8z&1u1KZ!a$!|gnNL32oCXP%QHmJFM zXk&H0s$l`%*d3o8I9`%jy)bt|_e=##NwOiIyE?#jYy<7V`eoizO5W}^&$-odm{IR6 z=0KKDyZ_NPjQfRQ6jX47g|e^H&femsNe|Yu@XLFvH@o3w`AU(6I%;lF=F@}t6Qz$IQ7J=LQKq`Wvr9_-X_E{?h+dakF|P3QeD4Nwf;ea zE!{9p9X7$ra~vC=sGupOnsApVH*hKO_H`ChGg=TnYi|_0A5{zP=%TW|R=)X#4{I}! z?-9}mZ>KHPXAGA8&XMm2{TQezFP!rIJO0L1A>MW2mFCWa@z5JL+$KCiVr8YNb3q`k zQA7p@}82nCD)gqDS9Ca zeIJ>v7Oa`B%2MB6+|Z|tMt~HQRu^T!Q{O}Lt3?y#!2W!CDw|h?)PqY4T=PABYlcIG zG)AGS^lS{4Wi_q0i(-eqj|&z%T=rb*m>jsh@s5>CsUG&W+3c3La8*w&C^AjwK8d)X ze{oZFQ$p1A!1QyTVS8AkosnA+z>+Ey@cIeM9!a-f>VL91V-7OV4_JIWcY2u7PBI_J$4vqk+e87&anwDfiG z;SOVZz%FzoW%~J=nY##`s2ZHVBH3(R3=wutmKGRha}`OV#g48c-MNPnS>$`fi8$pZ zL1bOK>NAwwvW%CEl3?wlww$U;ho(BKR!|8H7`rbl1bUM1Opj5V+}@ENqEv0c9Z2{f z#?#s$8+`+rRf3-$b~#%>m)$Fi=0t_j==(0TBUZ_w`cE6R+DuuVdHO(?^SxfzJH`bIlvQJ3 z^6}ya`oc!)hcNf|P@cL1TO}dgUXJL3*p(0p5La3w8uml);?AEVSO4sSk~tX86L^8bSo(&eF1#3_>WRO1aVeWIZU2Hg+{kn= z!n=ZNOCk#QSyegptuIJH#d8w-K*mo=!R^(XCu?dmCX0@0arjxH zBnZH}bzc-Wz(qij@?(W+J^+iPC*8lMF6g1Hj1Io{GCukk^SJNv8F+!c=^Fh(}jLE#;^wOJEN|#K}ri#cT@C6e}!($f@G9OJGP;i)a z8Nbb2P|4m5NyzmuqynCy;kJ1XHiK~Gy)GJdG#X82Hd5R&xTyxo580fM|CUHk{3^#+ihv}xg@-;;|HC+bh0yPhB6b0{0 zB&O?@A0#(`DJ91a7GYyF&3Y%9CWfs&8LVv8yi#m6TL&a!M2=hXB31NsTo7~;y7@9V zE%M6t*zU$5#={L=TO0g3UUhpU7Grr*7T1GS$S|L9&1MHW+(j2yH%G3NEHN<&$_*Kv z(0{9r9_%ng?{|#T-bwKvb0x8Q$|~f*X~yEd(0IC#1&SnRE2ditPa9g8 zdIqsxoYn-!p|L(>)}ixB(5O`~GP%l3Uk;KDW*7jg<+k7q{v!MAA6+n6_sT7f zpt|#F6uCYHHjU7j^P?KgPZckD& z3PR~shz0NJRDd}_0wrmNcFb@aGk8C)R7Ef0wac>@L(~^IVXM|3=}#?rLo`$qtWqm> zRGgCm&l0sfa?X=E6MGTO=~@K+wN6+t4Q4h$?HOT~vypd&q}-zFD^1PIwWny0Aw~dI zszkjr!XML;Z^6IL=lSYafqcQFGIAliPq*~;;kNa4s7DZbycL=YSggi9i3I44ise%f zXg&AbKke92jHEvSz9sniO!%M+fAOmZ`kZ-ky?^!OF=xdf=zpUwv)QHqMUwut_Ww-P z+E2H5{ONi4)ixKQ^Iv5YGy~VPWT(*FN)Ac6TRqu%=RqIo2wEgn;(eb4%F$&RLL60s z#2uY?q6|SQ3*MFtzoh^o=A zx9ei@pvcRVB%yvQiQy^1Uxh1r4-!nesaebtb|!Z-@mrAtxpMUwCU+xHp@< zan-N>RUYnwCx1h0E?zOc)F%=2X7GMLdI`!cd)6pmE4SIv**yiqJ>C zsQzBx*0p*iE943?EY@G>nDmM#32f+HvXrf47E&(#MysrCeUo%^%GRZTfeVEH4Io@6 z{%ims1jKKTyWqzpedMIY6EPC~mz<36l*rPXX4m5%fH%rUoyayf*S71hSt!$cfHCzS zFPOCU+Yfdml>okzIH&<2SShCYe&DNdWhsaAjBc`eFv(P?5*GcCndm!S z!n)1o-;ArTH5X8eQ;|XP&cR>&kWr1Xl7Bi3J%k?3+!~2h$PePe>S;}G;KZpwcm^wB zNC(2N?JZ%OSRw2d$$lg+*e8lGaX0$j z>%%zgXkM(&#q%J%g4KP}yF|G4Et01^FK0T+L$%8PEsar2Rt zFCf~^z1#pbcs80N4^I1r%cR@y)R;0bk}rQFNhrrsgSyIXo|5SQ^mo1S&xjy*)1wvm zY9k@_mzV!9-)`2I2`}&jMybW44M^T^hoA1MAA6`W#;?AB=KOGa82TtB08AKt1STW{ zN1waD%wXDzy9YbI4@y01YJ77Bf}qheH~#PDdHlDMM*in!|IyID2EzZ%t^cROf4bz~ zOji3J-1+~CJ1wZ^u`nQVzm)$4KJ2Az(Zv4SQWn?*CO0R?{z!x!X<~T92}J&0mvlnM z*yPxMG(+xfKi&lf2R$@T7NW=F9KA+PH)NfF|F@#j=Djp`wFUrxR>q2)?7(kzZEs~F z`hh{h#c0t9E4T)&#Z`E^=PD-S{;Kgy%Qj-ET>nB##VYO^hFN@ZsReb`6{$ zQ2VI#{yzA*A%PAuX!HVFqEj2sHcFHSw!g7ZE}%ZSi>gs}^l}faGObSoKnJg>2d&9G zcQ@gnZ(Yyknum+~$^Kv=FvO_m4&wcF5HppcBy$;94DxCQXTJP*`8`eN#z$XunaX$M z0OD)gYewyFfcxC>W&1gm^3&lwK|(p$`j3bbs%>WjvJ1R-a7;B0JU^an$JF z?aQoXD76z8vLD-(ZRwgjav-ZpwmXC z#Gm~~n`8z_kGuuuDWJt5eof|>&90I4vMwGM#O6e2GfE`Nu>i_sO9?X1jm5RxbI8mV zBGZngYN1aXB@OLO^0+653L0nrZUSzLlOuCk*} zFzs0ET4?s@lWm0SB6>X$NYBuD?$`O={UtL}fB^KOdKmjuPwBX1e!fVq{SU{6_JbH! zL;qNqpW`dVgk*HmF!0hG2(@^XN}Ic~&ky9@MAj%uJcCv$DWgWR_<1tgDkias5F{v3o2SXAtO%Ueiux{x! zUQ@_3@J(nX;hh{lkYH02;AGTh5xT<%fR=}eNWewF>-&STG#Cxu6tLfh_Nq5x_LkbVjlIUM9nu@60?l^DdjGd}8MJ;NNfd^eT* zN~cxB;J3Etu&FCP?%(%i&XCcX@55l2|2BL}tXGl}QB`Ug{qZ&GY_6mg9@}Tm^#2kh y86yGH09jN0wS){nT@VO;HgYIa`|#-7pNv(hDzlLnYKDMf5KPxlr%}rx`hNkWwtn*f diff --git a/aspnetcore/client-side/spa-services/_static/tag_helper_intellisense_original.png b/aspnetcore/client-side/spa-services/_static/tag_helper_intellisense_original.png deleted file mode 100644 index 6e9aebc64320d35f956de76d39347d212c485825..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16315 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 Uc=QeC8Cooj=RlQmr4K>>7lsJ Date: Fri, 23 Jun 2017 14:24:55 -0500 Subject: [PATCH 20/23] Replace future tense usage --- aspnetcore/client-side/spa-services.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/aspnetcore/client-side/spa-services.md b/aspnetcore/client-side/spa-services.md index 5bcb79b5f982..514e0c27c0e5 100644 --- a/aspnetcore/client-side/spa-services.md +++ b/aspnetcore/client-side/spa-services.md @@ -17,7 +17,7 @@ ms.custom: H1Hack27Feb2017 By [Scott Addie](https://github.com/scottaddie) -In this article, you will learn about the value proposition of [SpaServices](https://github.com/aspnet/JavaScriptServices/tree/dev/src/Microsoft.AspNetCore.SpaServices) in building a Single Page Application (SPA) with ASP.NET Core. +This article describes the value proposition of [SpaServices](https://github.com/aspnet/JavaScriptServices/tree/dev/src/Microsoft.AspNetCore.SpaServices) in building a Single Page Application (SPA) with ASP.NET Core. [View or download sample code](https://github.com/aspnet/Docs/tree/master/aspnetcore/client-side/spa-services/sample) @@ -218,7 +218,7 @@ An extension method named `MapSpaFallbackRoute` is used in the `Startup` class' [!code-csharp[Main](../client-side/spa-services/sample/SpaServicesSampleApp/Startup.cs?name=mvc-routing-table&highlight=7-9)] > [!TIP] -> Routes are evaluated in the order in which they're configured. Consequently, the `default` route in the preceding code example will be consulted first for pattern matching. +> Routes are evaluated in the order in which they're configured. Consequently, the `default` route in the preceding code example is consulted first for pattern matching. @@ -277,17 +277,17 @@ Build and run the application: dotnet run ``` -The application will start on localhost according to the [runtime configuration mode](#runtime-config-mode). Navigating to `http://localhost:5000` in the browser will display the landing page. +The application starts on localhost according to the [runtime configuration mode](#runtime-config-mode). Navigating to `http://localhost:5000` in the browser displays the landing page. ### Running with Visual Studio 2017 -Open the *.csproj* file generated by the `dotnet new` command. The required NuGet and npm packages will be restored automatically upon project open. This restoration process may take up to a few minutes; and, the application is ready to run when it completes. Click the green run button or press `Ctrl` + `F5`, and the browser opens to the application's landing page. The application will be running on localhost according to the [runtime configuration mode](#runtime-config-mode). +Open the *.csproj* file generated by the `dotnet new` command. The required NuGet and npm packages are restored automatically upon project open. This restoration process may take up to a few minutes; and, the application is ready to run when it completes. Click the green run button or press `Ctrl` + `F5`, and the browser opens to the application's landing page. The application runs on localhost according to the [runtime configuration mode](#runtime-config-mode). ## Testing the application -SpaServices templates are pre-configured to run client-side tests using [Karma](https://karma-runner.github.io/1.0/index.html) and [Jasmine](https://jasmine.github.io/). Jasmine is a popular unit testing framework for JavaScript, whereas Karma is a test runner for those tests. Karma is configured to work with the [Webpack Dev Middleware](#webpack-dev-middleware) such that you don’t have to stop and run the test every time changes are made. Whether it's the code running against the test case or the test case itself, the test will run automatically. +SpaServices templates are pre-configured to run client-side tests using [Karma](https://karma-runner.github.io/1.0/index.html) and [Jasmine](https://jasmine.github.io/). Jasmine is a popular unit testing framework for JavaScript, whereas Karma is a test runner for those tests. Karma is configured to work with the [Webpack Dev Middleware](#webpack-dev-middleware) such that you don’t have to stop and run the test every time changes are made. Whether it's the code running against the test case or the test case itself, the test runs automatically. Using the Angular application as an example, two Jasmine test cases are already provided for the `CounterComponent` in the *counter.component.spec.ts* file: From 573272c08aeac59b1f3e35419b5e495c3bbd3259 Mon Sep 17 00:00:00 2001 From: Rick Anderson Date: Fri, 23 Jun 2017 13:38:33 -0700 Subject: [PATCH 21/23] Update spa-services.md --- aspnetcore/client-side/spa-services.md | 81 ++++++++++++-------------- 1 file changed, 38 insertions(+), 43 deletions(-) diff --git a/aspnetcore/client-side/spa-services.md b/aspnetcore/client-side/spa-services.md index 514e0c27c0e5..9fe315f6ed4a 100644 --- a/aspnetcore/client-side/spa-services.md +++ b/aspnetcore/client-side/spa-services.md @@ -25,13 +25,13 @@ This article describes the value proposition of [SpaServices](https://github.com ## Using SpaServices with ASP.NET Core -A SPA is a very popular breed of web application due to its inherent rich user experience. Alas, integrating client-side SPA frameworks or libraries, such as [Angular](https://angular.io/) or [React](https://facebook.github.io/react/), with server-side frameworks like ASP.NET Core can be daunting. The `Microsoft.AspNetCore.SpaServices` NuGet package, or SpaServices for short, was developed to reduce friction in the integration process. It enables seamless operation between the disparate client and server technology stacks. +A SPA is a popular web application approach due to its inherent rich user experience. Integrating client-side SPA frameworks or libraries, such as [Angular](https://angular.io/) or [React](https://facebook.github.io/react/), with server-side frameworks like ASP.NET Core can be difficult. The `Microsoft.AspNetCore.SpaServices` NuGet package, or SpaServices for short, was developed to reduce friction in the integration process. It enables seamless operation between the different client and server technology stacks. ## What is SpaServices? -SpaServices was created as a component of the larger [JavaScriptServices](https://github.com/aspnet/JavaScriptServices) project, whose goal is to position ASP.NET Core as developers' preferred server-side platform for building SPAs. With that said, SpaServices is not required to develop SPAs with ASP.NET Core. Because SpaServices is a nonopinionated, client framework-agnostic library, it doesn't lock you into a particular client framework, library, or coding style. +SpaServices was created as a component of the [JavaScriptServices](https://github.com/aspnet/JavaScriptServices) project, whose goal is to position ASP.NET Core as developers' preferred server-side platform for building SPAs. SpaServices is not required to develop SPAs with ASP.NET Core. SpaServices is a client framework-agnostic library, it doesn't lock you into a particular client framework. SpaServices provides useful infrastructure such as: * [Server-side prerendering](#server-prerendering) @@ -39,7 +39,7 @@ SpaServices provides useful infrastructure such as: * [Hot Module Replacement](#hot-module-replacement) * [Routing helpers](#routing-helpers) -Collectively, these infrastructure components enhance both the development workflow and the runtime experience. Moreover, the components may be adopted in an à la carte fashion. +Collectively, these infrastructure components enhance both the development workflow and the runtime experience. The components can be adopted in an à la carte fashion. @@ -53,23 +53,22 @@ To work with SpaServices, install the following: node -v && npm -v ``` - > [!NOTE] - > If you're deploying to an Azure web site, you don't need to do anything here — Node.js is already installed and available in the server environments. +Note: If you're deploying to an Azure web site, you don't need to do anything here — Node.js is installed and available in the server environments. -1. [.NET Core SDK](https://www.microsoft.com/net/download/core) 1.0 RC4 (or later) +1. [.NET Core SDK](https://www.microsoft.com/net/download/core) 1.0 (or later) * If you're on Windows, you can install Visual Studio 2017, which includes the .NET Core SDK. ## Server-side prerendering -A universal (also known as isomorphic) application is a JavaScript application capable of running both on the server and the client. Angular, React, and other popular frameworks provide a universal platform for this application development style. The idea is to first render the framework components on the server via Node.js and then delegate further execution to the client. +A universal (also known as isomorphic) application is a JavaScript application capable of running both on the server and the client. Angular, React, and other popular frameworks provide a universal platform for this application development style. The idea is to first render the framework components on the server via Node.js, and then delegate further execution to the client. SpaServices' ASP.NET Core [Tag Helpers](xref:mvc/views/tag-helpers/intro) simplify the implementation of server-side prerendering by invoking the JavaScript functions on the server for you. ### Prerequisites -Install the following prerequisites: +Install the following: 1. [Microsoft.AspNetCore.SpaServices](http://www.nuget.org/packages/Microsoft.AspNetCore.SpaServices/) NuGet package 1. [aspnet-prerendering](https://www.npmjs.com/package/aspnet-prerendering) npm package: @@ -83,14 +82,12 @@ The Tag Helpers are made discoverable via namespace registration in the project' [!code-csharp[Main](../client-side/spa-services/sample/SpaServicesSampleApp/Views/_ViewImports.cshtml?highlight=3)] -These Tag Helpers abstract away the intricacies of communicating directly with low-level APIs by leveraging an HTML-like syntax inside the Razor view: +These Tag Helpers abstract the intricacies of communicating directly with low-level APIs by leveraging an HTML-like syntax inside the Razor view: [!code-html[Main](../client-side/spa-services/sample/SpaServicesSampleApp/Views/Home/Index.cshtml?range=5)] -> [!TIP] -> Microsoft's **[Razor Language Services](https://marketplace.visualstudio.com/items?itemName=ms-madsk.RazorLanguageServices)** extension improves Visual Studio 2017's Tag Helpers development experience by adding context-aware IntelliSense and syntax highlighting: -> -> ![Tag Helpers intellisense](../client-side/spa-services/_static/tag_helper_intellisense.png) +Microsoft's **[Razor Language Services](https://marketplace.visualstudio.com/items?itemName=ms-madsk.RazorLanguageServices)** extension improves Visual Studio 2017's Tag Helpers development experience by adding context-aware IntelliSense and syntax highlighting: +![Tag Helpers intellisense](../client-side/spa-services/_static/tag_helper_intellisense.png) ### The `asp-prerender-module` Tag Helper @@ -104,7 +101,7 @@ In the following Angular example, the *ClientApp/boot-server.ts* file utilizes t ### The `asp-prerender-data` Tag Helper -Sometimes contextual information must be passed as arguments from the Razor view to the server-side JavaScript. To satisfy this requirement, the `asp-prerender-data` Tag Helper is used in conjunction with the aforementioned `asp-prerender-module` Tag Helper. For example, the following markup passes user data to the `main-server` module: +The `asp-prerender-data` and `asp-prerender-module` Tag Helpers can be used to pass contextual information from the Razor view to the server-side JavaScript. For example, the following markup passes user data to the `main-server` module: [!code-html[Main](../client-side/spa-services/sample/SpaServicesSampleApp/Views/Home/Index.cshtml?range=9-12)] @@ -112,8 +109,7 @@ The received `UserName` argument is serialized using the built-in JSON serialize [!code-javascript[Main](../client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/boot-server.ts?range=6,10-21,38-52,79-)] -> [!NOTE] -> Property names passed in Tag Helpers are represented with **PascalCase** notation. Contrast that to JavaScript, where the same property names are represented with **camelCase**. The default JSON serialization configuration is responsible for this difference. +Note: Property names passed in Tag Helpers are represented with **PascalCase** notation. Contrast that to JavaScript, where the same property names are represented with **camelCase**. The default JSON serialization configuration is responsible for this difference. To expand upon the preceding code example, data can be passed from the server to the view by hydrating the `globals` property provided to the `resolve` function: @@ -127,13 +123,13 @@ The `postList` array defined inside the `globals` object is attached to the brow ## Webpack Dev Middleware -[Webpack Dev Middleware](https://webpack.github.io/docs/webpack-dev-middleware.html) introduces a streamlined development workflow whereby Webpack builds resources on demand. The middleware automatically compiles and serves client-side resources when a page is reloaded in the browser. The alternate, less efficient approach is to manually invoke Webpack via the project's npm build script when a third-party dependency or the custom code changes. An example of said build script in the `package.json` file is: +[Webpack Dev Middleware](https://webpack.github.io/docs/webpack-dev-middleware.html) introduces a streamlined development workflow whereby Webpack builds resources on demand. The middleware automatically compiles and serves client-side resources when a page is reloaded in the browser. The alternate approach is to manually invoke Webpack via the project's npm build script when a third-party dependency or the custom code changes. An npm build script in the *package.json* file is shown in the following example: [!code-json[Main](../client-side/spa-services/sample/SpaServicesSampleApp/package.json?range=5)] ### Prerequisites -Install the following prerequisites: +Install the following: 1. [Microsoft.AspNetCore.SpaServices](http://www.nuget.org/packages/Microsoft.AspNetCore.SpaServices/) NuGet package 1. [aspnet-webpack](https://www.npmjs.com/package/aspnet-webpack) npm package: @@ -147,11 +143,11 @@ Webpack Dev Middleware is registered into the HTTP request pipeline via the foll [!code-csharp[Main](../client-side/spa-services/sample/SpaServicesSampleApp/Startup.cs?name=webpack-middleware-registration&highlight=4)] -With regard to the `UseWebpackDevMiddleware` extension method, some critical details are: -1. It must be called before [registering static file hosting](xref:fundamentals/static-files) via the `UseStaticFiles` extension method -1. It should be registered for use only when running the application in development mode +The `UseWebpackDevMiddleware` extension method: +1. Must be called before [registering static file hosting](xref:fundamentals/static-files) via the `UseStaticFiles` extension method. +1. For security reasons, registered only when running the app in development mode -Finally, the *webpack.config.js* file's `output.publicPath` property tells the middleware to watch the `dist` folder for changes: +~Finally, this is a banned word~ the *webpack.config.js* file's `output.publicPath` property tells the middleware to watch the `dist` folder for changes: [!code-javascript[Main](../client-side/spa-services/sample/SpaServicesSampleApp/webpack.config.js?range=6,13-16)] @@ -159,11 +155,11 @@ Finally, the *webpack.config.js* file's `output.publicPath` property tells the m ## Hot Module Replacement -Think of Webpack's [Hot Module Replacement](https://webpack.github.io/docs/hot-module-replacement-with-webpack.html) (HMR) feature as an evolution of [Webpack Dev Middleware](#webpack-dev-middleware). HMR introduces all the same benefits; but, it further streamlines the development workflow by automatically updating page content after compiling the changes. Don't confuse this with a refresh of the browser, which would interfere with the current in-memory state and debugging session of the SPA. There is a live link between the Webpack Dev Middleware service and the browser, which means changes are simply pushed to the browser. +Think of Webpack's [Hot Module Replacement](https://webpack.github.io/docs/hot-module-replacement-with-webpack.html) (HMR) feature as an evolution of [Webpack Dev Middleware](#webpack-dev-middleware). HMR introduces all the same benefits; but, it further streamlines the development workflow by automatically updating page content after compiling the changes. Don't confuse this with a refresh of the browser, which would interfere with the current in-memory state and debugging session of the SPA. There is a live link between the Webpack Dev Middleware service and the browser, which means changes are ~simply another banned word~ pushed to the browser. ### Prerequisites -Install the following prerequisites: +Install the following: 1. [Microsoft.AspNetCore.SpaServices](http://www.nuget.org/packages/Microsoft.AspNetCore.SpaServices/) NuGet package 1. [webpack-hot-middleware](https://www.npmjs.com/package/webpack-hot-middleware) npm package: @@ -173,7 +169,7 @@ Install the following prerequisites: ### Configuration -The HMR component must be registered into MVC's HTTP request pipeline. An overload of the `UseWebpackDevMiddleware` extension method must be used in the `Startup` class' `Configure` method: +The HMR component must be registered into MVC's HTTP request pipeline (in the `Configure` method): ```csharp app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions { @@ -181,15 +177,15 @@ app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions { }); ``` -As was true with Webpack Dev Middleware, there are a few critical points when using `UseWebpackDevMiddleware`: +Consider the following when using `UseWebpackDevMiddleware`: 1. It must be called before the `UseStaticFiles` extension method -1. It should be registered for use only when running the application in development mode +1. For security reasons, registered only when running the app in development mode. -Finally, the *webpack.config.js* file must define a `plugins` array, even if it's left empty: +The *webpack.config.js* file must define a `plugins` array, even if it's left empty: [!code-javascript[Main](../client-side/spa-services/sample/SpaServicesSampleApp/webpack.config.js?range=6,25)] -After loading the application in the browser, the developer tools' Console tab provides confirmation of HMR activation: +After loading the app in the browser, the developer tools' Console tab provides confirmation of HMR activation: ![Hot Module Replacement connected message](spa-services/_static/hmr_connected.png) @@ -199,11 +195,11 @@ After loading the application in the browser, the developer tools' Console tab p In most ASP.NET Core-based SPAs, you'll want client-side routing in addition to server-side routing. The SPA and MVC routing systems can work independently without interference. There is, however, one edge case posing challenges: identifying 404 HTTP responses. -Consider the scenario in which an extensionless route of `/some/page` is used. Assume the request doesn't pattern-match a server-side route, but its pattern does match a client-side route. Now consider an incoming request for `/images/user-512.png`, which undoubtedly expects to find an image file on the server. As such, if that requested resource path doesn't match any server-side route or static file, it's unlikely that the client-side application would handle it — you probably want to return a 404 HTTP status code. +Consider the scenario in which an extensionless route of `/some/page` is used. Assume the request doesn't pattern-match a server-side route, but its pattern does match a client-side route. Now consider an incoming request for `/images/user-512.png`, which generally expects to find an image file on the server. If that requested resource path doesn't match any server-side route or static file, it's unlikely that the client-side application would handle it — you generally want to return a 404 HTTP status code. ### Prerequisites -Install the following prerequisites: +Install the following: 1. [Microsoft.AspNetCore.SpaServices](http://www.nuget.org/packages/Microsoft.AspNetCore.SpaServices/) NuGet package 1. The client-side routing npm package. Using Angular as an example: @@ -213,12 +209,11 @@ Install the following prerequisites: ### Configuration -An extension method named `MapSpaFallbackRoute` is used in the `Startup` class' `Configure` method: +An extension method named `MapSpaFallbackRoute` is used in the `Configure` method: [!code-csharp[Main](../client-side/spa-services/sample/SpaServicesSampleApp/Startup.cs?name=mvc-routing-table&highlight=7-9)] -> [!TIP] -> Routes are evaluated in the order in which they're configured. Consequently, the `default` route in the preceding code example is consulted first for pattern matching. +Tip: Routes are evaluated in the order in which they're configured. Consequently, the `default` route in the preceding code example is used first for pattern matching. @@ -232,7 +227,7 @@ These templates can be installed via the .NET Core CLI by running the following dotnet new --install Microsoft.AspNetCore.SpaTemplates::* ``` -Upon successful installation, a list of available SPA templates is provided: +A list of available SPA templates is displayed: | Templates | Short Name | Language | Tags | |:------------------------------------------|:-----------|:---------|:------------| @@ -253,15 +248,15 @@ dotnet new angular ### Set the runtime configuration mode -Be aware that two primary runtime configuration modes exist: +Two primary runtime configuration modes exist: 1. **Development**: - * includes source maps to ease debugging - * doesn't optimize the client-side code for performance + * Includes source maps to ease debugging. + * Doesn't optimize the client-side code for performance. 1. **Production**: - * excludes source maps - * optimizes the client-side code via bundling & minification + * Excludes source maps. + * Optimizes the client-side code via bundling & minification. -ASP.NET Core uses an environment variable named `ASPNETCORE_ENVIRONMENT` to store the configuration mode. See instructions at **[Setting the environment](xref:fundamentals/environments#setting-the-environment)** for more information. +ASP.NET Core uses an environment variable named `ASPNETCORE_ENVIRONMENT` to store the configuration mode. See **[Setting the environment](xref:fundamentals/environments#setting-the-environment)** for more information. ### Running with .NET Core CLI @@ -285,7 +280,7 @@ Open the *.csproj* file generated by the `dotnet new` command. The required NuGe -## Testing the application +## Testing the app SpaServices templates are pre-configured to run client-side tests using [Karma](https://karma-runner.github.io/1.0/index.html) and [Jasmine](https://jasmine.github.io/). Jasmine is a popular unit testing framework for JavaScript, whereas Karma is a test runner for those tests. Karma is configured to work with the [Webpack Dev Middleware](#webpack-dev-middleware) such that you don’t have to stop and run the test every time changes are made. Whether it's the code running against the test case or the test case itself, the test runs automatically. @@ -325,4 +320,4 @@ dotnet publish -c Release ## Additional resources -* [Angular Docs](https://angular.io/docs) \ No newline at end of file +* [Angular Docs](https://angular.io/docs) From bc665a3c49e264a5061272dfe9745e1a64b09b38 Mon Sep 17 00:00:00 2001 From: Scott Addie Date: Tue, 27 Jun 2017 15:04:29 -0500 Subject: [PATCH 22/23] Implement suggestions from Tom and Rick --- aspnetcore/client-side/index.md | 2 +- aspnetcore/client-side/spa-services.md | 90 ++++++++++++++------------ aspnetcore/client-side/toc.md | 2 +- 3 files changed, 50 insertions(+), 44 deletions(-) diff --git a/aspnetcore/client-side/index.md b/aspnetcore/client-side/index.md index d6b805445131..3313a719a77d 100644 --- a/aspnetcore/client-side/index.md +++ b/aspnetcore/client-side/index.md @@ -19,7 +19,7 @@ ms.prod: asp.net-core - [Building beautiful, responsive sites with Bootstrap](bootstrap.md) - [Knockout.js MVVM Framework](knockout.md) - [Using AngularJS for Single Page Apps (SPAs)](angular.md) -- [Using SpaServices for Single Page Apps (SPAs)](spa-services.md) +- [Using JavaScriptServices for Single Page Apps (SPAs)](spa-services.md) - [Styling applications with Less, Sass, and Font Awesome](less-sass-fa.md) - [Bundling and minification](bundling-and-minification.md) - [TypeScript](https://www.typescriptlang.org/docs/handbook/asp-net-core.html) diff --git a/aspnetcore/client-side/spa-services.md b/aspnetcore/client-side/spa-services.md index 9fe315f6ed4a..6aac5dcb6a0c 100644 --- a/aspnetcore/client-side/spa-services.md +++ b/aspnetcore/client-side/spa-services.md @@ -1,7 +1,7 @@ --- -title: Using SpaServices for Creating Single Page Applications | Microsoft Docs +title: Using JavaScriptServices for Creating Single Page Applications | Microsoft Docs author: scottaddie -description: Learn about the benefits of using SpaServices to build a SPA with ASP.NET Core +description: Learn about the benefits of using JavaScriptServices to build a SPA with ASP.NET Core keywords: ASP.NET Core, Angular, SPA, JavaScriptServices, SpaServices ms.author: scaddie manager: wpickett @@ -13,25 +13,37 @@ ms.prod: asp.net-core uid: client-side/spa-services ms.custom: H1Hack27Feb2017 --- -# Using SpaServices for Creating Single Page Applications with ASP.NET Core +# Using JavaScriptServices for Creating Single Page Applications with ASP.NET Core -By [Scott Addie](https://github.com/scottaddie) +By [Scott Addie](https://github.com/scottaddie) and [Fiyaz Hasan](http://fiyazhasan.me/) -This article describes the value proposition of [SpaServices](https://github.com/aspnet/JavaScriptServices/tree/dev/src/Microsoft.AspNetCore.SpaServices) in building a Single Page Application (SPA) with ASP.NET Core. +A Single Page Application (SPA) is a popular type of web application due to its inherent rich user experience. Integrating client-side SPA frameworks or libraries, such as [Angular](https://angular.io/) or [React](https://facebook.github.io/react/), with server-side frameworks like ASP.NET Core can be difficult. [JavaScriptServices](https://github.com/aspnet/JavaScriptServices) was developed to reduce friction in the integration process. It enables seamless operation between the different client and server technology stacks. [View or download sample code](https://github.com/aspnet/Docs/tree/master/aspnetcore/client-side/spa-services/sample) - + -## Using SpaServices with ASP.NET Core +## What is JavaScriptServices? -A SPA is a popular web application approach due to its inherent rich user experience. Integrating client-side SPA frameworks or libraries, such as [Angular](https://angular.io/) or [React](https://facebook.github.io/react/), with server-side frameworks like ASP.NET Core can be difficult. The `Microsoft.AspNetCore.SpaServices` NuGet package, or SpaServices for short, was developed to reduce friction in the integration process. It enables seamless operation between the different client and server technology stacks. +JavaScriptServices is a collection of client-side technologies for ASP.NET Core. Its goal is to position ASP.NET Core as developers' preferred server-side platform for building SPAs. + +JavaScriptServices consists of three distinct NuGet packages: +* [Microsoft.AspNetCore.NodeServices](http://www.nuget.org/packages/Microsoft.AspNetCore.NodeServices/) (NodeServices) +* [Microsoft.AspNetCore.SpaServices](http://www.nuget.org/packages/Microsoft.AspNetCore.SpaServices/) (SpaServices) +* [Microsoft.AspNetCore.SpaTemplates](http://www.nuget.org/packages/Microsoft.AspNetCore.SpaTemplates/) (SpaTemplates) + +These packages are useful if you: +* Run JavaScript on the server +* Use a SPA framework or library +* Build client-side assets with Webpack + +Much of the focus in this article is placed on using the SpaServices package. ## What is SpaServices? -SpaServices was created as a component of the [JavaScriptServices](https://github.com/aspnet/JavaScriptServices) project, whose goal is to position ASP.NET Core as developers' preferred server-side platform for building SPAs. SpaServices is not required to develop SPAs with ASP.NET Core. SpaServices is a client framework-agnostic library, it doesn't lock you into a particular client framework. +SpaServices was created to position ASP.NET Core as developers' preferred server-side platform for building SPAs. SpaServices is not required to develop SPAs with ASP.NET Core, and it doesn't lock you into a particular client framework. SpaServices provides useful infrastructure such as: * [Server-side prerendering](#server-prerendering) @@ -39,15 +51,15 @@ SpaServices provides useful infrastructure such as: * [Hot Module Replacement](#hot-module-replacement) * [Routing helpers](#routing-helpers) -Collectively, these infrastructure components enhance both the development workflow and the runtime experience. The components can be adopted in an à la carte fashion. +Collectively, these infrastructure components enhance both the development workflow and the runtime experience. The components can be adopted individually. ## Prerequisites for using SpaServices To work with SpaServices, install the following: -1. [Node.js](https://nodejs.org/) (version 6 or later) with npm - * To verify these component are installed and can be found, run the following from the command line: +* [Node.js](https://nodejs.org/) (version 6 or later) with npm + * To verify these components are installed and can be found, run the following from the command line: ```console node -v && npm -v @@ -55,8 +67,10 @@ To work with SpaServices, install the following: Note: If you're deploying to an Azure web site, you don't need to do anything here — Node.js is installed and available in the server environments. -1. [.NET Core SDK](https://www.microsoft.com/net/download/core) 1.0 (or later) - * If you're on Windows, you can install Visual Studio 2017, which includes the .NET Core SDK. +* [.NET Core SDK](https://www.microsoft.com/net/download/core) 1.0 (or later) + * If you're on Windows, this can be installed by selecting Visual Studio 2017's **.NET Core cross-platform development** workload. + +* [Microsoft.AspNetCore.SpaServices](http://www.nuget.org/packages/Microsoft.AspNetCore.SpaServices/) NuGet package @@ -64,13 +78,12 @@ Note: If you're deploying to an Azure web site, you don't need to do anything he A universal (also known as isomorphic) application is a JavaScript application capable of running both on the server and the client. Angular, React, and other popular frameworks provide a universal platform for this application development style. The idea is to first render the framework components on the server via Node.js, and then delegate further execution to the client. -SpaServices' ASP.NET Core [Tag Helpers](xref:mvc/views/tag-helpers/intro) simplify the implementation of server-side prerendering by invoking the JavaScript functions on the server for you. +ASP.NET Core [Tag Helpers](xref:mvc/views/tag-helpers/intro) provided by SpaServices simplify the implementation of server-side prerendering by invoking the JavaScript functions on the server for you. ### Prerequisites Install the following: -1. [Microsoft.AspNetCore.SpaServices](http://www.nuget.org/packages/Microsoft.AspNetCore.SpaServices/) NuGet package -1. [aspnet-prerendering](https://www.npmjs.com/package/aspnet-prerendering) npm package: +* [aspnet-prerendering](https://www.npmjs.com/package/aspnet-prerendering) npm package: ```console npm i -S aspnet-prerendering @@ -86,22 +99,22 @@ These Tag Helpers abstract the intricacies of communicating directly with low-le [!code-html[Main](../client-side/spa-services/sample/SpaServicesSampleApp/Views/Home/Index.cshtml?range=5)] -Microsoft's **[Razor Language Services](https://marketplace.visualstudio.com/items?itemName=ms-madsk.RazorLanguageServices)** extension improves Visual Studio 2017's Tag Helpers development experience by adding context-aware IntelliSense and syntax highlighting: +Microsoft's [Razor Language Services](https://marketplace.visualstudio.com/items?itemName=ms-madsk.RazorLanguageServices) extension improves Visual Studio 2017's Tag Helpers development experience by adding context-aware IntelliSense and syntax highlighting: ![Tag Helpers intellisense](../client-side/spa-services/_static/tag_helper_intellisense.png) ### The `asp-prerender-module` Tag Helper -The `asp-prerender-module` Tag Helper, used in the preceding code example, executes *ClientApp/dist/main-server.js* on the server via Node.js. For clarity's sake, *main-server.js* file is an artifact of the [Webpack](http://webpack.github.io/) build process' TypeScript-to-JavaScript transpilation task. Webpack defines an entry point alias of `main-server`; and, traversal of the dependency graph for this alias begins at the *ClientApp/boot-server.ts* file: +The `asp-prerender-module` Tag Helper, used in the preceding code example, executes *ClientApp/dist/main-server.js* on the server via Node.js. For clarity's sake, *main-server.js* file is an artifact of the TypeScript-to-JavaScript transpilation task in the [Webpack](http://webpack.github.io/) build process. Webpack defines an entry point alias of `main-server`; and, traversal of the dependency graph for this alias begins at the *ClientApp/boot-server.ts* file: [!code-javascript[Main](../client-side/spa-services/sample/SpaServicesSampleApp/webpack.config.js?range=53)] -In the following Angular example, the *ClientApp/boot-server.ts* file utilizes the `createServerRenderer` function and `RenderResult` type of the `aspnet-prerendering` npm package to configure server rendering via Node.js. The HTML markup destined for server-side rendering is passed to a `resolve` function call, which is wrapped in a JavaScript `Promise` object of type `RenderResult`. The `Promise` object's significance is that it asynchronously supplies the HTML markup to the page for injection in the DOM's placeholder element. +In the following Angular example, the *ClientApp/boot-server.ts* file utilizes the `createServerRenderer` function and `RenderResult` type of the `aspnet-prerendering` npm package to configure server rendering via Node.js. The HTML markup destined for server-side rendering is passed to a resolve function call, which is wrapped in a strongly-typed JavaScript `Promise` object. The `Promise` object's significance is that it asynchronously supplies the HTML markup to the page for injection in the DOM's placeholder element. [!code-javascript[Main](../client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/boot-server.ts?range=6,10-34,79-)] ### The `asp-prerender-data` Tag Helper -The `asp-prerender-data` and `asp-prerender-module` Tag Helpers can be used to pass contextual information from the Razor view to the server-side JavaScript. For example, the following markup passes user data to the `main-server` module: +When coupled with the `asp-prerender-module` Tag Helper, the `asp-prerender-data` Tag Helper can be used to pass contextual information from the Razor view to the server-side JavaScript. For example, the following markup passes user data to the `main-server` module: [!code-html[Main](../client-side/spa-services/sample/SpaServicesSampleApp/Views/Home/Index.cshtml?range=9-12)] @@ -130,8 +143,7 @@ The `postList` array defined inside the `globals` object is attached to the brow ### Prerequisites Install the following: -1. [Microsoft.AspNetCore.SpaServices](http://www.nuget.org/packages/Microsoft.AspNetCore.SpaServices/) NuGet package -1. [aspnet-webpack](https://www.npmjs.com/package/aspnet-webpack) npm package: +* [aspnet-webpack](https://www.npmjs.com/package/aspnet-webpack) npm package: ```console npm i -D aspnet-webpack @@ -143,11 +155,9 @@ Webpack Dev Middleware is registered into the HTTP request pipeline via the foll [!code-csharp[Main](../client-side/spa-services/sample/SpaServicesSampleApp/Startup.cs?name=webpack-middleware-registration&highlight=4)] -The `UseWebpackDevMiddleware` extension method: -1. Must be called before [registering static file hosting](xref:fundamentals/static-files) via the `UseStaticFiles` extension method. -1. For security reasons, registered only when running the app in development mode +The `UseWebpackDevMiddleware` extension method must be called before [registering static file hosting](xref:fundamentals/static-files) via the `UseStaticFiles` extension method. For security reasons, register the middleware only when the app runs in development mode. -~Finally, this is a banned word~ the *webpack.config.js* file's `output.publicPath` property tells the middleware to watch the `dist` folder for changes: +The *webpack.config.js* file's `output.publicPath` property tells the middleware to watch the `dist` folder for changes: [!code-javascript[Main](../client-side/spa-services/sample/SpaServicesSampleApp/webpack.config.js?range=6,13-16)] @@ -155,13 +165,12 @@ The `UseWebpackDevMiddleware` extension method: ## Hot Module Replacement -Think of Webpack's [Hot Module Replacement](https://webpack.github.io/docs/hot-module-replacement-with-webpack.html) (HMR) feature as an evolution of [Webpack Dev Middleware](#webpack-dev-middleware). HMR introduces all the same benefits; but, it further streamlines the development workflow by automatically updating page content after compiling the changes. Don't confuse this with a refresh of the browser, which would interfere with the current in-memory state and debugging session of the SPA. There is a live link between the Webpack Dev Middleware service and the browser, which means changes are ~simply another banned word~ pushed to the browser. +Think of Webpack's [Hot Module Replacement](https://webpack.github.io/docs/hot-module-replacement-with-webpack.html) (HMR) feature as an evolution of [Webpack Dev Middleware](#webpack-dev-middleware). HMR introduces all the same benefits, but it further streamlines the development workflow by automatically updating page content after compiling the changes. Don't confuse this with a refresh of the browser, which would interfere with the current in-memory state and debugging session of the SPA. There is a live link between the Webpack Dev Middleware service and the browser, which means changes are ~simply another banned word~ pushed to the browser. ### Prerequisites Install the following: -1. [Microsoft.AspNetCore.SpaServices](http://www.nuget.org/packages/Microsoft.AspNetCore.SpaServices/) NuGet package -1. [webpack-hot-middleware](https://www.npmjs.com/package/webpack-hot-middleware) npm package: +* [webpack-hot-middleware](https://www.npmjs.com/package/webpack-hot-middleware) npm package: ```console npm i -D webpack-hot-middleware @@ -169,7 +178,7 @@ Install the following: ### Configuration -The HMR component must be registered into MVC's HTTP request pipeline (in the `Configure` method): +The HMR component must be registered into MVC's HTTP request pipeline in the `Configure` method: ```csharp app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions { @@ -177,9 +186,7 @@ app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions { }); ``` -Consider the following when using `UseWebpackDevMiddleware`: -1. It must be called before the `UseStaticFiles` extension method -1. For security reasons, registered only when running the app in development mode. +As was true with [Webpack Dev Middleware](#webpack-dev-middleware), the `UseWebpackDevMiddleware` extension method must be called before the `UseStaticFiles` extension method. For security reasons, register the middleware only when the app runs in development mode. The *webpack.config.js* file must define a `plugins` array, even if it's left empty: @@ -200,8 +207,7 @@ Consider the scenario in which an extensionless route of `/some/page` is used. A ### Prerequisites Install the following: -1. [Microsoft.AspNetCore.SpaServices](http://www.nuget.org/packages/Microsoft.AspNetCore.SpaServices/) NuGet package -1. The client-side routing npm package. Using Angular as an example: +* The client-side routing npm package. Using Angular as an example: ```console npm i -S @angular/router @@ -238,7 +244,7 @@ A list of available SPA templates is displayed: | MVC ASP.NET Core with React.js and Redux | reactredux | [C#] | Web/MVC/SPA | | MVC ASP.NET Core with Vue.js | vue | [C#] | Web/MVC/SPA | -To create a new project using one of the SPA templates, include the **Short Name** of the template in the `dotnet new` command. The following command creates an Angular application with ASP.NET Core MVC configured for the server-side: +To create a new project using one of the SPA templates, include the **Short Name** of the template in the `dotnet new` command. The following command creates an Angular application with ASP.NET Core MVC configured for the server side: ```console dotnet new angular @@ -249,10 +255,10 @@ dotnet new angular ### Set the runtime configuration mode Two primary runtime configuration modes exist: -1. **Development**: +* **Development**: * Includes source maps to ease debugging. * Doesn't optimize the client-side code for performance. -1. **Production**: +* **Production**: * Excludes source maps. * Optimizes the client-side code via bundling & minification. @@ -276,7 +282,7 @@ The application starts on localhost according to the [runtime configuration mode ### Running with Visual Studio 2017 -Open the *.csproj* file generated by the `dotnet new` command. The required NuGet and npm packages are restored automatically upon project open. This restoration process may take up to a few minutes; and, the application is ready to run when it completes. Click the green run button or press `Ctrl` + `F5`, and the browser opens to the application's landing page. The application runs on localhost according to the [runtime configuration mode](#runtime-config-mode). +Open the *.csproj* file generated by the `dotnet new` command. The required NuGet and npm packages are restored automatically upon project open. This restoration process may take up to a few minutes, and the application is ready to run when it completes. Click the green run button or press `Ctrl + F5`, and the browser opens to the application's landing page. The application runs on localhost according to the [runtime configuration mode](#runtime-config-mode). @@ -294,7 +300,7 @@ Open the command prompt at the project root, and run the following command: npm test ``` -The script launches the Karma test runner, which reads the settings defined in the *karma.conf.js* file. Amongst other settings, the *karma.conf.js* identifies the test files to be executed via its `files` array: +The script launches the Karma test runner, which reads the settings defined in the *karma.conf.js* file. Among other settings, the *karma.conf.js* identifies the test files to be executed via its `files` array: [!code-javascript[Main](../client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/test/karma.conf.js?range=4-5,8-11)] @@ -320,4 +326,4 @@ dotnet publish -c Release ## Additional resources -* [Angular Docs](https://angular.io/docs) +* [Angular Docs](https://angular.io/docs) \ No newline at end of file diff --git a/aspnetcore/client-side/toc.md b/aspnetcore/client-side/toc.md index 79566466b7e9..afcb46120ce7 100644 --- a/aspnetcore/client-side/toc.md +++ b/aspnetcore/client-side/toc.md @@ -4,7 +4,7 @@ # [Building beautiful, responsive sites with Bootstrap](bootstrap.md) # [Knockout.js MVVM Framework](knockout.md) # [Using Angular for Single Page Apps (SPAs)](angular.md) -# [Using SpaServices for Single Page Apps (SPAs)](spa-services.md) +# [Using JavaScriptServices for Single Page Apps (SPAs)](spa-services.md) # [Styling applications with Less, Sass, and Font Awesome](less-sass-fa.md) # [Bundling and minification](bundling-and-minification.md) From 170da913b6611650979d0ed3b7cc574e9c4f240e Mon Sep 17 00:00:00 2001 From: Scott Addie Date: Wed, 28 Jun 2017 13:08:43 -0500 Subject: [PATCH 23/23] Implement suggestions from Damien --- aspnetcore/client-side/spa-services.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/aspnetcore/client-side/spa-services.md b/aspnetcore/client-side/spa-services.md index 6aac5dcb6a0c..a2277d2b9fba 100644 --- a/aspnetcore/client-side/spa-services.md +++ b/aspnetcore/client-side/spa-services.md @@ -78,7 +78,7 @@ Note: If you're deploying to an Azure web site, you don't need to do anything he A universal (also known as isomorphic) application is a JavaScript application capable of running both on the server and the client. Angular, React, and other popular frameworks provide a universal platform for this application development style. The idea is to first render the framework components on the server via Node.js, and then delegate further execution to the client. -ASP.NET Core [Tag Helpers](xref:mvc/views/tag-helpers/intro) provided by SpaServices simplify the implementation of server-side prerendering by invoking the JavaScript functions on the server for you. +ASP.NET Core [Tag Helpers](xref:mvc/views/tag-helpers/intro) provided by SpaServices simplify the implementation of server-side prerendering by invoking the JavaScript functions on the server. ### Prerequisites @@ -110,7 +110,7 @@ The `asp-prerender-module` Tag Helper, used in the preceding code example, execu In the following Angular example, the *ClientApp/boot-server.ts* file utilizes the `createServerRenderer` function and `RenderResult` type of the `aspnet-prerendering` npm package to configure server rendering via Node.js. The HTML markup destined for server-side rendering is passed to a resolve function call, which is wrapped in a strongly-typed JavaScript `Promise` object. The `Promise` object's significance is that it asynchronously supplies the HTML markup to the page for injection in the DOM's placeholder element. -[!code-javascript[Main](../client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/boot-server.ts?range=6,10-34,79-)] +[!code-typescript[Main](../client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/boot-server.ts?range=6,10-34,79-)] ### The `asp-prerender-data` Tag Helper @@ -120,13 +120,13 @@ When coupled with the `asp-prerender-module` Tag Helper, the `asp-prerender-data The received `UserName` argument is serialized using the built-in JSON serializer and is stored in the `params.data` object. In the following Angular example, the data is used to construct a personalized greeting within an `h1` element: -[!code-javascript[Main](../client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/boot-server.ts?range=6,10-21,38-52,79-)] +[!code-typescript[Main](../client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/boot-server.ts?range=6,10-21,38-52,79-)] Note: Property names passed in Tag Helpers are represented with **PascalCase** notation. Contrast that to JavaScript, where the same property names are represented with **camelCase**. The default JSON serialization configuration is responsible for this difference. To expand upon the preceding code example, data can be passed from the server to the view by hydrating the `globals` property provided to the `resolve` function: -[!code-javascript[Main](../client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/boot-server.ts?range=6,10-21,57-77,79-)] +[!code-typescript[Main](../client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/boot-server.ts?range=6,10-21,57-77,79-)] The `postList` array defined inside the `globals` object is attached to the browser's global `window` object. This variable hoisting to global scope eliminates duplication of effort, particularly as it pertains to loading the same data once on the server and again on the client. @@ -292,7 +292,7 @@ SpaServices templates are pre-configured to run client-side tests using [Karma]( Using the Angular application as an example, two Jasmine test cases are already provided for the `CounterComponent` in the *counter.component.spec.ts* file: -[!code-javascript[Main](../client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/components/counter/counter.component.spec.ts?range=15-28)] +[!code-typescript[Main](../client-side/spa-services/sample/SpaServicesSampleApp/ClientApp/app/components/counter/counter.component.spec.ts?range=15-28)] Open the command prompt at the project root, and run the following command: