Compare commits

..

44 Commits

Author SHA1 Message Date
Smit Patel
1e19873874 Cleanup NuGet.config 2017-07-10 17:15:22 -07:00
Steve Sanderson
c128fa6e06 Switch NuGet config to use preview2-final feed. This change needs to be reverted when merged back to dev branch. 2017-06-16 13:14:05 +02:00
Mike Harder
3ef23ece23 Changed all references of PackageTargetFallback to AssetTargetFallback (#1035) 2017-06-14 15:47:11 -07:00
Steve Sanderson
68e84bac3f Update templates' ASP.NET dependencies to 2.0.0-preview2-final 2017-06-13 22:05:19 +01:00
Steve Sanderson
a38d3bdc4b Change NETStandard.Library.NETFramework reference to preview2-25405-01 for consistency with stock Web templates 2017-06-13 09:46:18 +01:00
Steve Sanderson
8e5f255346 Update templates to reference preview2-25661 2017-06-13 09:37:40 +01:00
Steve Sanderson
a3bcc0d863 Update debugger detection to support Node v8.1+, and stop aborting if debugger messages are unrecognised. 2017-06-11 20:28:14 +01:00
Steve Sanderson
5e7bb0f5c5 Don't log OperationCanceledException every time an HMR client disconnects 2017-06-11 19:57:26 +01:00
Steve Sanderson
8396be24c8 Fix VS2017.3 warning about not specifying TypeScriptToolsVersion 2017-06-11 19:39:46 +01:00
Steve Sanderson
728b18431d In dotnet new templates, only show "npm install" instruction when executing in CLI 2017-06-11 19:28:51 +01:00
Steve Sanderson
9a59bccad9 If Node isn't installed, give clear instructions rather than random build errors 2017-06-10 14:11:20 +01:00
Steve Sanderson
bf6d2227db In dotnet new templates, display instruction to run "npm install" manually 2017-06-08 11:43:38 +01:00
danobri
bcaa3af92e Re-add bootstrap import
In the update to Angular 4, the bootstrap import got removed from boot.client.ts, causing the navbar-toggle menu to longer work in the template.  Re-adding so navbar-toggle is functional again.
2017-06-08 11:14:18 +01:00
Steve Sanderson
f3b7103c83 In AngularSpa template, redefine app.module.shared as a real NgModule. Fixes #986. 2017-06-08 11:08:36 +01:00
Steve Sanderson
0dd24068ca Update VS template name localisation IDs and texts as instructed 2017-06-08 10:38:00 +01:00
Steve Sanderson
0526852e05 Pin templates' .NET reference version numbers on Coherence 25571 in same way as stock Web templates do. This also fixes net461 support. 2017-06-08 10:33:27 +01:00
Steve Sanderson
60c8a3471d Enable localisation for template names/descriptions in VS 2017-06-07 22:53:55 +01:00
Steve Sanderson
c7b7f40d2c Update template package to support generating net461 projects as well as netcoreapp2.0 2017-06-07 22:27:43 +01:00
Steve Sanderson
5127a74d3f Clean up dependencies.props 2017-06-07 14:59:00 +01:00
Steve Sanderson
e38399fbf4 Remove redundant explicit SpaServices references from templates now it's on the .All metapackage anyway 2017-06-07 14:58:54 +01:00
Steve Sanderson
045c05fc88 Update samples to target both netcoreapp2.0 and net461 2017-06-07 14:58:49 +01:00
Steve Sanderson
b444831c8d Remove System.Runtime.Loader dependency to enable net461 support. Use IApplicationLifetime instead of AssemblyLoadContext.Default.Unloading. 2017-06-07 14:58:44 +01:00
Steve Sanderson
2aaceaa9f8 Move core packages to netstandard2.0 2017-06-07 14:58:33 +01:00
Steve Sanderson
1c5bd98d8a Stop doing NPM restores during CI builds, as the result isn't used anyway 2017-06-07 14:58:28 +01:00
Pranav K
dbdc81a06a Updating build scripts to point to 2.0.0-preview2 KoreBuild 2017-05-31 19:53:23 -07:00
Pranav K
8a1d64cc73 Branching for rel/2.0.0-preview2 2017-05-31 19:36:47 -07:00
Kiran Challa
087183ea18 Updated to use the latest shared runtime 2017-05-29 04:40:44 -07:00
Pranav K
9b131ea7d3 Remove unused usings 2017-05-27 18:58:17 -07:00
Steve Sanderson
75fd215b1a Change Microsoft.DotNet.Web.Spa.ProjectTemplates to use 1.0.0-preview-* version numbers since the first release will be 1.0.0 2017-05-25 13:06:42 +01:00
Steve Sanderson
38997c3f3f Pad build numbers so that alpha-sort produces correct ordering 2017-05-25 12:22:48 +01:00
Steve Sanderson
bee4c6ff11 Stop "dotnet new" from stripping out important parts of .csproj files 2017-05-25 12:08:10 +01:00
Steve Sanderson
cc859306a3 Make template package version numbers more descriptive 2017-05-25 12:08:09 +01:00
Steve Sanderson
44512226c6 Stop including prebuilt .js bundles with template packages, since they are now generated on first build 2017-05-25 12:08:09 +01:00
Steve Sanderson
a2bde750be Make templates auto-run webpack on Debug build if wwwroot/dist is not already present 2017-05-25 12:08:09 +01:00
Thomas Hermann
c592282646 Fix Node debugging by catching warning 2017-05-24 17:32:01 +01:00
Steve Sanderson
b86e9f9254 Change template description to be consistent with others in 2.0 2017-05-24 15:10:37 +01:00
Steve Sanderson
970dc6c457 Add --silent to npm install command in CI. Fixes #972 (at least, to the extent that fixing it is reasonably possible) 2017-05-24 15:01:00 +01:00
Steve Sanderson
6baa2faf09 Split out built-in templates into new package Microsoft.DotNet.Web.Spa.ProjectTemplates 2017-05-24 14:47:23 +01:00
Steve Sanderson
918e7edbff Remove global.json files from templates for consistency with stock MVC template. 2017-05-23 17:05:42 +01:00
Steve Sanderson
e1c90b3601 Change "dotnet new" config choices to reference netcoreapp2.0 (not that it makes a difference when it's the only choice) 2017-05-23 11:20:04 +01:00
Steve Sanderson
b9e62cd4c9 Fix "dotnet new" postAction issues (and disable the "npm install" action) 2017-05-23 11:18:51 +01:00
Steve Sanderson
ad9cfbb176 Make quotes consistent 2017-05-23 10:08:41 +01:00
Steve Sanderson
941ae9a34d Add postActions to "dotnet new" templates 2017-05-23 10:06:35 +01:00
Ryan Brandenburg
3645ae6cf2 Generate Documentation files 2017-05-22 10:54:25 +01:00
51 changed files with 492 additions and 311 deletions

View File

@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<configuration> <configuration>
<packageSources> <packageSources>
<add key="AspNetCore" value="https://dotnet.myget.org/F/aspnetcore-ci-dev/api/v3/index.json" /> <clear />
<add key="AspNetCoreTools" value="https://dotnet.myget.org/F/aspnetcore-tools/api/v3/index.json" /> <add key="AspNetCore" value="https://dotnet.myget.org/F/aspnetcore-master/api/v3/index.json" />
<add key="NuGet" value="https://api.nuget.org/v3/index.json" /> <add key="NuGet" value="https://api.nuget.org/v3/index.json" />
</packageSources> </packageSources>
</configuration> </configuration>

View File

@@ -33,7 +33,7 @@ cd $PSScriptRoot
$repoFolder = $PSScriptRoot $repoFolder = $PSScriptRoot
$env:REPO_FOLDER = $repoFolder $env:REPO_FOLDER = $repoFolder
$koreBuildZip="https://github.com/aspnet/KoreBuild/archive/dev.zip" $koreBuildZip="https://github.com/aspnet/KoreBuild/archive/rel/2.0.0-preview2.zip"
if ($env:KOREBUILD_ZIP) if ($env:KOREBUILD_ZIP)
{ {
$koreBuildZip=$env:KOREBUILD_ZIP $koreBuildZip=$env:KOREBUILD_ZIP

View File

@@ -2,7 +2,7 @@
repoFolder="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" repoFolder="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
cd $repoFolder cd $repoFolder
koreBuildZip="https://github.com/aspnet/KoreBuild/archive/dev.zip" koreBuildZip="https://github.com/aspnet/KoreBuild/archive/rel/2.0.0-preview2.zip"
if [ ! -z $KOREBUILD_ZIP ]; then if [ ! -z $KOREBUILD_ZIP ]; then
koreBuildZip=$KOREBUILD_ZIP koreBuildZip=$KOREBUILD_ZIP
fi fi

View File

@@ -16,7 +16,8 @@
<PackageReference Include="Internal.AspNetCore.Sdk" Version="$(InternalAspNetCoreSdkVersion)" PrivateAssets="All" /> <PackageReference Include="Internal.AspNetCore.Sdk" Version="$(InternalAspNetCoreSdkVersion)" PrivateAssets="All" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(TargetFrameworkIdentifier)'=='.NETFramework' AND '$(OutputType)'=='library'"> <ItemGroup Condition="'$(TargetFrameworkIdentifier)'=='.NETFramework'">
<PackageReference Include="NETStandard.Library" Version="$(BundledNETStandardPackageVersion)" /> <PackageReference Include="NETStandard.Library" Version="$(BundledNETStandardPackageVersion)" />
<PackageReference Include="NETStandard.Library.NETFramework" Version="$(NETStandardLibraryNETFrameworkVersion)" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -1,11 +1,12 @@
<Project> <Project>
<PropertyGroup> <PropertyGroup>
<AspNetCoreVersion>2.0.0-*</AspNetCoreVersion> <AspNetCoreVersion>2.0.0-preview2-*</AspNetCoreVersion>
<AutoMapperVersion>5.0.2</AutoMapperVersion> <AutoMapperVersion>5.0.2</AutoMapperVersion>
<CoreFxVersion>4.3.0</CoreFxVersion>
<InternalAspNetCoreSdkVersion>2.1.0-*</InternalAspNetCoreSdkVersion> <InternalAspNetCoreSdkVersion>2.1.0-*</InternalAspNetCoreSdkVersion>
<JsonNetVersion>10.0.1</JsonNetVersion> <JsonNetVersion>10.0.1</JsonNetVersion>
<NETStandardImplicitPackageVersion>$(BundledNETStandardPackageVersion)</NETStandardImplicitPackageVersion> <NETStandardImplicitPackageVersion>$(BundledNETStandardPackageVersion)</NETStandardImplicitPackageVersion>
<NETStandardLibraryNETFrameworkVersion>2.0.0-*</NETStandardLibraryNETFrameworkVersion>
<RuntimeFrameworkVersion Condition="'$(TargetFramework)'=='netcoreapp2.0'">2.0.0-*</RuntimeFrameworkVersion>
<ThreadingDataflowVersion>4.7.0</ThreadingDataflowVersion> <ThreadingDataflowVersion>4.7.0</ThreadingDataflowVersion>
</PropertyGroup> </PropertyGroup>
</Project> </Project>

View File

@@ -1,12 +0,0 @@
<Project>
<Target Name="NpmRestore" AfterTargets="Restore" Condition="'$(PreflightRestore)' != 'true'">
<ItemGroup>
<NpmModules Include="$(RepositoryRoot)**\package.json"
Exclude="$(RepositoryRoot)**\node_modules\**\*" />
</ItemGroup>
<Message Text="Restoring NPM modules for: %0A - @(NpmModules -> '%(FullPath)','%0A - ')" Importance="high" />
<Exec Command="npm install" WorkingDirectory="%(NpmModules.RootDir)%(Directory)" />
</Target>
</Project>

View File

@@ -1,9 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\build\dependencies.props" /> <Import Project="..\..\..\build\common.props" />
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework> <TargetFrameworks>netcoreapp2.0;net461</TargetFrameworks>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
<OutputType>exe</OutputType> <OutputType>exe</OutputType>
</PropertyGroup> </PropertyGroup>

View File

@@ -1,9 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk.Web"> <Project Sdk="Microsoft.NET.Sdk.Web">
<Import Project="..\..\..\build\dependencies.props" /> <Import Project="..\..\..\build\common.props" />
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework> <TargetFrameworks>netcoreapp2.0;net461</TargetFrameworks>
<TypeScriptCompileBlocked>true</TypeScriptCompileBlocked> <TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
</PropertyGroup> </PropertyGroup>

View File

@@ -27,6 +27,7 @@ namespace Webpack.ActionResults
{ {
var nodeServices = context.HttpContext.RequestServices.GetRequiredService<INodeServices>(); var nodeServices = context.HttpContext.RequestServices.GetRequiredService<INodeServices>();
var hostEnv = context.HttpContext.RequestServices.GetRequiredService<IHostingEnvironment>(); var hostEnv = context.HttpContext.RequestServices.GetRequiredService<IHostingEnvironment>();
var applicationLifetime = context.HttpContext.RequestServices.GetRequiredService<IApplicationLifetime>();
var applicationBasePath = hostEnv.ContentRootPath; var applicationBasePath = hostEnv.ContentRootPath;
var request = context.HttpContext.Request; var request = context.HttpContext.Request;
var response = context.HttpContext.Response; var response = context.HttpContext.Response;
@@ -34,6 +35,7 @@ namespace Webpack.ActionResults
var prerenderedHtml = await Prerenderer.RenderToString( var prerenderedHtml = await Prerenderer.RenderToString(
applicationBasePath, applicationBasePath,
nodeServices, nodeServices,
applicationLifetime.ApplicationStopping,
_moduleExport, _moduleExport,
request.GetEncodedUrl(), request.GetEncodedUrl(),
request.Path + request.QueryString.Value, request.Path + request.QueryString.Value,

View File

@@ -1,9 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk.Web"> <Project Sdk="Microsoft.NET.Sdk.Web">
<Import Project="..\..\..\build\dependencies.props" /> <Import Project="..\..\..\build\common.props" />
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework> <TargetFrameworks>netcoreapp2.0;net461</TargetFrameworks>
<TypeScriptCompileBlocked>true</TypeScriptCompileBlocked> <TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
</PropertyGroup> </PropertyGroup>

View File

@@ -4,7 +4,7 @@
<PropertyGroup> <PropertyGroup>
<Description>Socket-based RPC for Microsoft.AspNetCore.NodeServices.</Description> <Description>Socket-based RPC for Microsoft.AspNetCore.NodeServices.</Description>
<TargetFramework>netcoreapp2.0</TargetFramework> <TargetFramework>netstandard2.0</TargetFramework>
<PackageTags>aspnetcore;aspnetcoremvc;nodeservices</PackageTags> <PackageTags>aspnetcore;aspnetcoremvc;nodeservices</PackageTags>
<TypeScriptCompileBlocked>true</TypeScriptCompileBlocked> <TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
</PropertyGroup> </PropertyGroup>
@@ -15,14 +15,6 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Microsoft.AspNetCore.NodeServices\Microsoft.AspNetCore.NodeServices.csproj" /> <ProjectReference Include="..\Microsoft.AspNetCore.NodeServices\Microsoft.AspNetCore.NodeServices.csproj" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net461' ">
<PackageReference Include="Microsoft.Tpl.Dataflow" Version="$(MicrosoftDataflowVersion)" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netcoreapp2.0' ">
<PackageReference Include="System.IO.Pipes" Version="$(CoreFxVersion)" />
<PackageReference Include="System.Threading.Tasks.Dataflow" Version="$(ThreadingDataflowVersion)" /> <PackageReference Include="System.Threading.Tasks.Dataflow" Version="$(ThreadingDataflowVersion)" />
</ItemGroup> </ItemGroup>

View File

@@ -18,11 +18,7 @@ namespace Microsoft.AspNetCore.NodeServices.Sockets.PhysicalConnections
PipeDirection.InOut, PipeDirection.InOut,
PipeOptions.Asynchronous); PipeOptions.Asynchronous);
#if NET451
_namedPipeClientStream.Connect();
#else
await _namedPipeClientStream.ConnectAsync().ConfigureAwait(false); await _namedPipeClientStream.ConnectAsync().ConfigureAwait(false);
#endif
return _namedPipeClientStream; return _namedPipeClientStream;
} }

View File

@@ -11,9 +11,6 @@ namespace Microsoft.AspNetCore.NodeServices.Sockets.PhysicalConnections
public static StreamConnection Create() public static StreamConnection Create()
{ {
#if NET451
return new NamedPipeConnection();
#else
var useNamedPipes = System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform( var useNamedPipes = System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(
System.Runtime.InteropServices.OSPlatform.Windows); System.Runtime.InteropServices.OSPlatform.Windows);
if (useNamedPipes) if (useNamedPipes)
@@ -24,7 +21,6 @@ namespace Microsoft.AspNetCore.NodeServices.Sockets.PhysicalConnections
{ {
return new UnixDomainSocketConnection(); return new UnixDomainSocketConnection();
} }
#endif
} }
} }
} }

View File

@@ -10,16 +10,6 @@ namespace Microsoft.AspNetCore.NodeServices.Sockets.PhysicalConnections
private NetworkStream _networkStream; private NetworkStream _networkStream;
private Socket _socket; private Socket _socket;
#if NET451
public override Task<Stream> Open(string address)
{
// The 'null' assignments avoid the compiler warnings about unassigned fields.
// Note that this whole class isn't supported on .NET 4.5.1, since that's not cross-platform.
_networkStream = null;
_socket = null;
throw new System.PlatformNotSupportedException();
}
#else
public override async Task<Stream> Open(string address) public override async Task<Stream> Open(string address)
{ {
var endPoint = new UnixDomainSocketEndPoint("/tmp/" + address); var endPoint = new UnixDomainSocketEndPoint("/tmp/" + address);
@@ -28,7 +18,6 @@ namespace Microsoft.AspNetCore.NodeServices.Sockets.PhysicalConnections
_networkStream = new NetworkStream(_socket); _networkStream = new NetworkStream(_socket);
return _networkStream; return _networkStream;
} }
#endif
public override void Dispose() public override void Dispose()
{ {

View File

@@ -59,11 +59,7 @@ namespace Microsoft.AspNetCore.NodeServices.Sockets.PhysicalConnections
} }
else else
{ {
#if NET451
_encodedPath = new byte[0];
#else
_encodedPath = Array.Empty<byte>(); _encodedPath = Array.Empty<byte>();
#endif
_path = string.Empty; _path = string.Empty;
} }
} }

View File

@@ -49,6 +49,7 @@ namespace Microsoft.AspNetCore.NodeServices.Sockets
options.ProjectPath, options.ProjectPath,
options.WatchFileExtensions, options.WatchFileExtensions,
MakeNewCommandLineOptions(socketAddress), MakeNewCommandLineOptions(socketAddress),
options.ApplicationStoppingToken,
options.NodeInstanceOutputLogger, options.NodeInstanceOutputLogger,
options.EnvironmentVariables, options.EnvironmentVariables,
options.InvocationTimeoutMilliseconds, options.InvocationTimeoutMilliseconds,

View File

@@ -12,11 +12,7 @@ namespace Microsoft.AspNetCore.NodeServices.Sockets.VirtualConnections
/// </summary> /// </summary>
internal class VirtualConnection : Stream internal class VirtualConnection : Stream
{ {
#if NET451
private readonly static Task CompletedTask = Task.FromResult((object)null);
#else
private readonly static Task CompletedTask = Task.CompletedTask; private readonly static Task CompletedTask = Task.CompletedTask;
#endif
private VirtualConnectionClient _host; private VirtualConnectionClient _host;
private readonly BufferBlock<byte[]> _receivedDataQueue = new BufferBlock<byte[]>(); private readonly BufferBlock<byte[]> _receivedDataQueue = new BufferBlock<byte[]>();
private ArraySegment<byte> _receivedDataNotYetUsed; private ArraySegment<byte> _receivedDataNotYetUsed;

View File

@@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading;
using Microsoft.AspNetCore.NodeServices.HostingModels; using Microsoft.AspNetCore.NodeServices.HostingModels;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
@@ -42,6 +43,12 @@ namespace Microsoft.AspNetCore.NodeServices
EnvironmentVariables["NODE_ENV"] = hostEnv.IsDevelopment() ? "development" : "production"; // De-facto standard values for Node EnvironmentVariables["NODE_ENV"] = hostEnv.IsDevelopment() ? "development" : "production"; // De-facto standard values for Node
} }
var applicationLifetime = serviceProvider.GetService<IApplicationLifetime>();
if (applicationLifetime != null)
{
ApplicationStoppingToken = applicationLifetime.ApplicationStopping;
}
// If the DI system gives us a logger, use it. Otherwise, set up a default one. // If the DI system gives us a logger, use it. Otherwise, set up a default one.
var loggerFactory = serviceProvider.GetService<ILoggerFactory>(); var loggerFactory = serviceProvider.GetService<ILoggerFactory>();
NodeInstanceOutputLogger = loggerFactory != null NodeInstanceOutputLogger = loggerFactory != null
@@ -93,5 +100,10 @@ namespace Microsoft.AspNetCore.NodeServices
/// Specifies the maximum duration, in milliseconds, that your .NET code should wait for Node.js RPC calls to return. /// Specifies the maximum duration, in milliseconds, that your .NET code should wait for Node.js RPC calls to return.
/// </summary> /// </summary>
public int InvocationTimeoutMilliseconds { get; set; } public int InvocationTimeoutMilliseconds { get; set; }
/// <summary>
/// A token that indicates when the host application is stopping.
/// </summary>
public CancellationToken ApplicationStoppingToken { get; set; }
} }
} }

View File

@@ -42,6 +42,7 @@ namespace Microsoft.AspNetCore.NodeServices.HostingModels
options.ProjectPath, options.ProjectPath,
options.WatchFileExtensions, options.WatchFileExtensions,
MakeCommandLineOptions(port), MakeCommandLineOptions(port),
options.ApplicationStoppingToken,
options.NodeInstanceOutputLogger, options.NodeInstanceOutputLogger,
options.EnvironmentVariables, options.EnvironmentVariables,
options.InvocationTimeoutMilliseconds, options.InvocationTimeoutMilliseconds,

View File

@@ -45,6 +45,7 @@ namespace Microsoft.AspNetCore.NodeServices.HostingModels
/// <param name="projectPath">The root path of the current project. This is used when resolving Node.js module paths relative to the project root.</param> /// <param name="projectPath">The root path of the current project. This is used when resolving Node.js module paths relative to the project root.</param>
/// <param name="watchFileExtensions">The filename extensions that should be watched within the project root. The Node instance will automatically shut itself down if any matching file changes.</param> /// <param name="watchFileExtensions">The filename extensions that should be watched within the project root. The Node instance will automatically shut itself down if any matching file changes.</param>
/// <param name="commandLineArguments">Additional command-line arguments to be passed to the Node.js instance.</param> /// <param name="commandLineArguments">Additional command-line arguments to be passed to the Node.js instance.</param>
/// <param name="applicationStoppingToken">A token that indicates when the host application is stopping.</param>
/// <param name="nodeOutputLogger">The <see cref="ILogger"/> to which the Node.js instance's stdout/stderr (and other log information) should be written.</param> /// <param name="nodeOutputLogger">The <see cref="ILogger"/> to which the Node.js instance's stdout/stderr (and other log information) should be written.</param>
/// <param name="environmentVars">Environment variables to be set on the Node.js process.</param> /// <param name="environmentVars">Environment variables to be set on the Node.js process.</param>
/// <param name="invocationTimeoutMilliseconds">The maximum duration, in milliseconds, to wait for RPC calls to complete.</param> /// <param name="invocationTimeoutMilliseconds">The maximum duration, in milliseconds, to wait for RPC calls to complete.</param>
@@ -55,6 +56,7 @@ namespace Microsoft.AspNetCore.NodeServices.HostingModels
string projectPath, string projectPath,
string[] watchFileExtensions, string[] watchFileExtensions,
string commandLineArguments, string commandLineArguments,
CancellationToken applicationStoppingToken,
ILogger nodeOutputLogger, ILogger nodeOutputLogger,
IDictionary<string, string> environmentVars, IDictionary<string, string> environmentVars,
int invocationTimeoutMilliseconds, int invocationTimeoutMilliseconds,
@@ -67,7 +69,7 @@ namespace Microsoft.AspNetCore.NodeServices.HostingModels
} }
OutputLogger = nodeOutputLogger; OutputLogger = nodeOutputLogger;
_entryPointScript = new StringAsTempFile(entryPointScript); _entryPointScript = new StringAsTempFile(entryPointScript, applicationStoppingToken);
_invocationTimeoutMilliseconds = invocationTimeoutMilliseconds; _invocationTimeoutMilliseconds = invocationTimeoutMilliseconds;
_launchWithDebugging = launchWithDebugging; _launchWithDebugging = launchWithDebugging;
@@ -315,11 +317,7 @@ namespace Microsoft.AspNetCore.NodeServices.HostingModels
private static void SetEnvironmentVariable(ProcessStartInfo startInfo, string name, string value) private static void SetEnvironmentVariable(ProcessStartInfo startInfo, string name, string value)
{ {
#if NET451
startInfo.EnvironmentVariables[name] = value;
#else
startInfo.Environment[name] = value; startInfo.Environment[name] = value;
#endif
} }
private static Process LaunchNodeProcess(ProcessStartInfo startInfo) private static Process LaunchNodeProcess(ProcessStartInfo startInfo)
@@ -383,12 +381,6 @@ namespace Microsoft.AspNetCore.NodeServices.HostingModels
{ {
OutputLogger.LogWarning(evt.Data); OutputLogger.LogWarning(evt.Data);
} }
else if (!initializationIsCompleted)
{
_connectionIsReadySource.SetException(
new InvalidOperationException("The Node.js process failed to initialize: " + evt.Data));
initializationIsCompleted = true;
}
else else
{ {
OnErrorDataReceived(UnencodeNewlines(evt.Data)); OnErrorDataReceived(UnencodeNewlines(evt.Data));
@@ -402,9 +394,11 @@ namespace Microsoft.AspNetCore.NodeServices.HostingModels
private static bool IsDebuggerMessage(string message) private static bool IsDebuggerMessage(string message)
{ {
return message.StartsWith("Debugger attached", StringComparison.OrdinalIgnoreCase) || return message.StartsWith("Debugger attached", StringComparison.Ordinal) ||
message.StartsWith("Debugger listening ", StringComparison.OrdinalIgnoreCase) || message.StartsWith("Debugger listening ", StringComparison.Ordinal) ||
message.StartsWith("To start debugging", StringComparison.OrdinalIgnoreCase) || message.StartsWith("To start debugging", StringComparison.Ordinal) ||
message.Equals("Warning: This is an experimental feature and could change at any time.", StringComparison.Ordinal) ||
message.Equals("For help see https://nodejs.org/en/docs/inspector", StringComparison.Ordinal) ||
message.Contains("chrome-devtools:"); message.Contains("chrome-devtools:");
} }

View File

@@ -4,9 +4,10 @@
<PropertyGroup> <PropertyGroup>
<Description>Invoke Node.js modules at runtime in ASP.NET Core applications.</Description> <Description>Invoke Node.js modules at runtime in ASP.NET Core applications.</Description>
<TargetFramework>netcoreapp2.0</TargetFramework> <TargetFramework>netstandard2.0</TargetFramework>
<PackageTags>aspnetcore;aspnetcoremvc;nodeservices</PackageTags> <PackageTags>aspnetcore;aspnetcoremvc;nodeservices</PackageTags>
<TypeScriptCompileBlocked>true</TypeScriptCompileBlocked> <TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@@ -19,12 +20,6 @@
<PackageReference Include="Newtonsoft.Json" Version="$(JsonNetVersion)" /> <PackageReference Include="Newtonsoft.Json" Version="$(JsonNetVersion)" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netcoreapp2.0' ">
<PackageReference Include="System.Diagnostics.Process" Version="$(CoreFxVersion)" />
<PackageReference Include="System.IO.FileSystem.Watcher" Version="$(CoreFxVersion)" />
<PackageReference Include="System.Runtime.Loader" Version="$(CoreFxVersion)" />
</ItemGroup>
<Target Name="PrepublishScript" BeforeTargets="PrepareForPublish" Condition=" '$(IsCrossTargetingBuild)' != 'true' "> <Target Name="PrepublishScript" BeforeTargets="PrepareForPublish" Condition=" '$(IsCrossTargetingBuild)' != 'true' ">
<Exec Command="npm install" /> <Exec Command="npm install" />
<Exec Command="node node_modules/webpack/bin/webpack.js" /> <Exec Command="node node_modules/webpack/bin/webpack.js" />

View File

@@ -1,5 +1,6 @@
using System; using System;
using System.IO; using System.IO;
using System.Threading;
namespace Microsoft.AspNetCore.NodeServices namespace Microsoft.AspNetCore.NodeServices
{ {
@@ -11,27 +12,21 @@ namespace Microsoft.AspNetCore.NodeServices
private bool _disposedValue; private bool _disposedValue;
private bool _hasDeletedTempFile; private bool _hasDeletedTempFile;
private object _fileDeletionLock = new object(); private object _fileDeletionLock = new object();
private IDisposable _applicationLifetimeRegistration;
/// <summary> /// <summary>
/// Create a new instance of <see cref="StringAsTempFile"/>. /// Create a new instance of <see cref="StringAsTempFile"/>.
/// </summary> /// </summary>
/// <param name="content">The contents of the temporary file to be created.</param> /// <param name="content">The contents of the temporary file to be created.</param>
public StringAsTempFile(string content) /// <param name="applicationStoppingToken">A token that indicates when the host application is stopping.</param>
public StringAsTempFile(string content, CancellationToken applicationStoppingToken)
{ {
FileName = Path.GetTempFileName(); FileName = Path.GetTempFileName();
File.WriteAllText(FileName, content); File.WriteAllText(FileName, content);
// Because .NET finalizers don't reliably run when the process is terminating, also // Because .NET finalizers don't reliably run when the process is terminating, also
// add event handlers for other shutdown scenarios. // add event handlers for other shutdown scenarios.
#if NET451 _applicationLifetimeRegistration = applicationStoppingToken.Register(EnsureTempFileDeleted);
AppDomain.CurrentDomain.ProcessExit += HandleProcessExit;
AppDomain.CurrentDomain.DomainUnload += HandleProcessExit;
#else
// Note that this still doesn't capture SIGKILL (at least on macOS) - there doesn't
// appear to be a way of doing that. So in that case, the temporary file will be
// left behind.
System.Runtime.Loader.AssemblyLoadContext.Default.Unloading += HandleAssemblyUnloading;
#endif
} }
/// <summary> /// <summary>
@@ -55,12 +50,7 @@ namespace Microsoft.AspNetCore.NodeServices
if (disposing) if (disposing)
{ {
// Dispose managed state // Dispose managed state
#if NET451 _applicationLifetimeRegistration.Dispose();
AppDomain.CurrentDomain.ProcessExit -= HandleProcessExit;
AppDomain.CurrentDomain.DomainUnload -= HandleProcessExit;
#else
System.Runtime.Loader.AssemblyLoadContext.Default.Unloading -= HandleAssemblyUnloading;
#endif
} }
EnsureTempFileDeleted(); EnsureTempFileDeleted();
@@ -81,18 +71,6 @@ namespace Microsoft.AspNetCore.NodeServices
} }
} }
#if NET451
private void HandleProcessExit(object sender, EventArgs args)
{
EnsureTempFileDeleted();
}
#else
private void HandleAssemblyUnloading(System.Runtime.Loader.AssemblyLoadContext context)
{
EnsureTempFileDeleted();
}
#endif
/// <summary> /// <summary>
/// Implements the finalization part of the IDisposable pattern by calling Dispose(false). /// Implements the finalization part of the IDisposable pattern by calling Dispose(false).
/// </summary> /// </summary>

View File

@@ -4,15 +4,17 @@
<PropertyGroup> <PropertyGroup>
<Description>Helpers for building single-page applications on ASP.NET MVC Core.</Description> <Description>Helpers for building single-page applications on ASP.NET MVC Core.</Description>
<TargetFramework>netcoreapp2.0</TargetFramework> <TargetFramework>netstandard2.0</TargetFramework>
<PackageTags>aspnetcore;aspnetcoremvc;nodeservices</PackageTags> <PackageTags>aspnetcore;aspnetcoremvc;nodeservices</PackageTags>
<TypeScriptCompileBlocked>true</TypeScriptCompileBlocked> <TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<EmbeddedResource Include="Content\**\*" /> <EmbeddedResource Include="Content\**\*" />
<ProjectReference Include="..\Microsoft.AspNetCore.NodeServices\Microsoft.AspNetCore.NodeServices.csproj" /> <ProjectReference Include="..\Microsoft.AspNetCore.NodeServices\Microsoft.AspNetCore.NodeServices.csproj" />
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="$(AspNetCoreVersion)" /> <PackageReference Include="Microsoft.AspNetCore.Mvc.TagHelpers" Version="$(AspNetCoreVersion)" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.ViewFeatures" Version="$(AspNetCoreVersion)" />
</ItemGroup> </ItemGroup>
<Target Name="PrepublishScript" BeforeTargets="PrepareForPublish" Condition=" '$(IsCrossTargetingBuild)' != 'true' "> <Target Name="PrepublishScript" BeforeTargets="PrepareForPublish" Condition=" '$(IsCrossTargetingBuild)' != 'true' ">

View File

@@ -1,5 +1,6 @@
using System; using System;
using System.Text; using System.Text;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Http.Features;
@@ -24,6 +25,7 @@ namespace Microsoft.AspNetCore.SpaServices.Prerendering
private static INodeServices _fallbackNodeServices; // Used only if no INodeServices was registered with DI private static INodeServices _fallbackNodeServices; // Used only if no INodeServices was registered with DI
private readonly string _applicationBasePath; private readonly string _applicationBasePath;
private readonly CancellationToken _applicationStoppingToken;
private readonly INodeServices _nodeServices; private readonly INodeServices _nodeServices;
/// <summary> /// <summary>
@@ -36,6 +38,9 @@ namespace Microsoft.AspNetCore.SpaServices.Prerendering
_nodeServices = (INodeServices) serviceProvider.GetService(typeof(INodeServices)) ?? _fallbackNodeServices; _nodeServices = (INodeServices) serviceProvider.GetService(typeof(INodeServices)) ?? _fallbackNodeServices;
_applicationBasePath = hostEnv.ContentRootPath; _applicationBasePath = hostEnv.ContentRootPath;
var applicationLifetime = (IApplicationLifetime) serviceProvider.GetService(typeof(IApplicationLifetime));
_applicationStoppingToken = applicationLifetime.ApplicationStopping;
// Consider removing the following. Having it means you can get away with not putting app.AddNodeServices() // Consider removing the following. Having it means you can get away with not putting app.AddNodeServices()
// in your startup file, but then again it might be confusing that you don't need to. // in your startup file, but then again it might be confusing that you don't need to.
if (_nodeServices == null) if (_nodeServices == null)
@@ -101,6 +106,7 @@ namespace Microsoft.AspNetCore.SpaServices.Prerendering
var result = await Prerenderer.RenderToString( var result = await Prerenderer.RenderToString(
_applicationBasePath, _applicationBasePath,
_nodeServices, _nodeServices,
_applicationStoppingToken,
new JavaScriptModuleExport(ModuleName) new JavaScriptModuleExport(ModuleName)
{ {
ExportName = ExportName ExportName = ExportName

View File

@@ -1,4 +1,5 @@
using System; using System;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.NodeServices; using Microsoft.AspNetCore.NodeServices;
@@ -9,22 +10,16 @@ namespace Microsoft.AspNetCore.SpaServices.Prerendering
/// </summary> /// </summary>
public static class Prerenderer public static class Prerenderer
{ {
private static readonly Lazy<StringAsTempFile> NodeScript; private static readonly object CreateNodeScriptLock = new object();
static Prerenderer() private static StringAsTempFile NodeScript;
{
NodeScript = new Lazy<StringAsTempFile>(() =>
{
var script = EmbeddedResourceReader.Read(typeof(Prerenderer), "/Content/Node/prerenderer.js");
return new StringAsTempFile(script); // Will be cleaned up on process exit
});
}
/// <summary> /// <summary>
/// Performs server-side prerendering by invoking code in Node.js. /// Performs server-side prerendering by invoking code in Node.js.
/// </summary> /// </summary>
/// <param name="applicationBasePath">The root path to your application. This is used when resolving project-relative paths.</param> /// <param name="applicationBasePath">The root path to your application. This is used when resolving project-relative paths.</param>
/// <param name="nodeServices">The instance of <see cref="INodeServices"/> that will be used to invoke JavaScript code.</param> /// <param name="nodeServices">The instance of <see cref="INodeServices"/> that will be used to invoke JavaScript code.</param>
/// <param name="applicationStoppingToken">A token that indicates when the host application is stopping.</param>
/// <param name="bootModule">The path to the JavaScript file containing the prerendering logic.</param> /// <param name="bootModule">The path to the JavaScript file containing the prerendering logic.</param>
/// <param name="requestAbsoluteUrl">The URL of the currently-executing HTTP request. This is supplied to the prerendering code.</param> /// <param name="requestAbsoluteUrl">The URL of the currently-executing HTTP request. This is supplied to the prerendering code.</param>
/// <param name="requestPathAndQuery">The path and query part of the URL of the currently-executing HTTP request. This is supplied to the prerendering code.</param> /// <param name="requestPathAndQuery">The path and query part of the URL of the currently-executing HTTP request. This is supplied to the prerendering code.</param>
@@ -35,6 +30,7 @@ namespace Microsoft.AspNetCore.SpaServices.Prerendering
public static Task<RenderToStringResult> RenderToString( public static Task<RenderToStringResult> RenderToString(
string applicationBasePath, string applicationBasePath,
INodeServices nodeServices, INodeServices nodeServices,
CancellationToken applicationStoppingToken,
JavaScriptModuleExport bootModule, JavaScriptModuleExport bootModule,
string requestAbsoluteUrl, string requestAbsoluteUrl,
string requestPathAndQuery, string requestPathAndQuery,
@@ -43,7 +39,7 @@ namespace Microsoft.AspNetCore.SpaServices.Prerendering
string requestPathBase) string requestPathBase)
{ {
return nodeServices.InvokeExportAsync<RenderToStringResult>( return nodeServices.InvokeExportAsync<RenderToStringResult>(
NodeScript.Value.FileName, GetNodeScriptFilename(applicationStoppingToken),
"renderToString", "renderToString",
applicationBasePath, applicationBasePath,
bootModule, bootModule,
@@ -53,5 +49,19 @@ namespace Microsoft.AspNetCore.SpaServices.Prerendering
timeoutMilliseconds, timeoutMilliseconds,
requestPathBase); requestPathBase);
} }
private static string GetNodeScriptFilename(CancellationToken applicationStoppingToken)
{
lock(CreateNodeScriptLock)
{
if (NodeScript == null)
{
var script = EmbeddedResourceReader.Read(typeof(Prerenderer), "/Content/Node/prerenderer.js");
NodeScript = new StringAsTempFile(script, applicationStoppingToken); // Will be cleaned up on process exit
}
}
return NodeScript.FileName;
}
} }
} }

View File

@@ -97,9 +97,17 @@ namespace Microsoft.AspNetCore.SpaServices.Webpack
context.Response.Headers.Remove("transfer-encoding"); context.Response.Headers.Remove("transfer-encoding");
using (var responseStream = await responseMessage.Content.ReadAsStreamAsync()) using (var responseStream = await responseMessage.Content.ReadAsStreamAsync())
{
try
{ {
await responseStream.CopyToAsync(context.Response.Body, DefaultHttpBufferSize, context.RequestAborted); await responseStream.CopyToAsync(context.Response.Body, DefaultHttpBufferSize, context.RequestAborted);
} }
catch (OperationCanceledException)
{
// The CopyToAsync task will be canceled if the client disconnects (e.g., user
// closes or refreshes the browser tab). Don't treat this as an error.
}
}
return true; return true;
} }

View File

@@ -1,14 +1,9 @@
using System; using System;
using System.IO; using System.IO;
using System.Threading; using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.NodeServices; using Microsoft.AspNetCore.NodeServices;
using Microsoft.AspNetCore.SpaServices.Webpack; using Microsoft.AspNetCore.SpaServices.Webpack;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.PlatformAbstractions;
using Newtonsoft.Json; using Newtonsoft.Json;
using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Builder namespace Microsoft.AspNetCore.Builder
{ {
@@ -73,7 +68,7 @@ namespace Microsoft.AspNetCore.Builder
// Get a filename matching the middleware Node script // Get a filename matching the middleware Node script
var script = EmbeddedResourceReader.Read(typeof(WebpackDevMiddleware), var script = EmbeddedResourceReader.Read(typeof(WebpackDevMiddleware),
"/Content/Node/webpack-dev-middleware.js"); "/Content/Node/webpack-dev-middleware.js");
var nodeScript = new StringAsTempFile(script); // Will be cleaned up on process exit var nodeScript = new StringAsTempFile(script, nodeServicesOptions.ApplicationStoppingToken); // Will be cleaned up on process exit
// Ideally, this would be relative to the application's PathBase (so it could work in virtual directories) // Ideally, this would be relative to the application's PathBase (so it could work in virtual directories)
// but it's not clear that such information exists during application startup, as opposed to within the context // but it's not clear that such information exists during application startup, as opposed to within the context

View File

@@ -1,19 +1,27 @@
<Project Sdk="Microsoft.NET.Sdk.Web"> <Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework> <TargetFramework Condition="'$(TargetFrameworkOverride)' == ''">netcoreapp2.0</TargetFramework>
<TargetFramework Condition="'$(TargetFrameworkOverride)' != ''">TargetFrameworkOverride</TargetFramework>
<TypeScriptCompileBlocked>true</TypeScriptCompileBlocked> <TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
<PackageTargetFallback>$(PackageTargetFallback);portable-net45+win8+wp8+wpa81;</PackageTargetFallback> <TypeScriptToolsVersion>Latest</TypeScriptToolsVersion>
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup Condition="'$(TargetFrameworkOverride)' == ''">
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0-*" /> <PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0-preview2-final" />
<PackageReference Include="Microsoft.AspNetCore.SpaServices" Version="2.0.0-*" /> </ItemGroup>
<ItemGroup Condition="'$(TargetFrameworkOverride)' != ''">
<PackageReference Include="NETStandard.Library.NETFramework" Version="2.0.0-preview2-25405-01" />
<PackageReference Include="Microsoft.AspNetCore" Version="2.0.0-preview2-final" />
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.0.0-preview2-final" />
<PackageReference Include="Microsoft.AspNetCore.SpaServices" Version="2.0.0-preview2-final" />
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="2.0.0-preview2-final" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.0-*" /> <DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.0-preview2-final" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@@ -21,7 +29,23 @@
<Content Remove="ClientApp\**" /> <Content Remove="ClientApp\**" />
</ItemGroup> </ItemGroup>
<Target Name="RunWebpack" AfterTargets="ComputeFilesToPublish"> <!--/-:cnd:noEmit -->
<Target Name="DebugRunWebpack" BeforeTargets="Build" Condition=" '$(Configuration)' == 'Debug' And !Exists('wwwroot\dist') ">
<!-- Ensure Node.js is installed -->
<Exec Command="node --version" ContinueOnError="true">
<Output TaskParameter="ExitCode" PropertyName="ErrorCode" />
</Exec>
<Error Condition="'$(ErrorCode)' != '0'" Text="Node.js is required to build and run this project. To continue, please install Node.js from https://nodejs.org/, and then restart your command prompt or IDE." />
<!-- In development, the dist files won't exist on the first run or when cloning to
a different machine, so rebuild them if not already present. -->
<Message Importance="high" Text="Performing first-run Webpack build..." />
<Exec Command="node node_modules/webpack/bin/webpack.js --config webpack.config.vendor.js" />
<Exec Command="node node_modules/webpack/bin/webpack.js" />
</Target>
<!--/+:cnd:noEmit -->
<Target Name="PublishRunWebpack" AfterTargets="ComputeFilesToPublish">
<!-- As part of publishing, ensure the JS resources are freshly built in production mode --> <!-- As part of publishing, ensure the JS resources are freshly built in production mode -->
<Exec Command="npm install" /> <Exec Command="npm install" />
<Exec Command="node node_modules/webpack/bin/webpack.js --config webpack.config.vendor.js --env.prod" /> <Exec Command="node node_modules/webpack/bin/webpack.js --config webpack.config.vendor.js --env.prod" />

View File

@@ -0,0 +1,17 @@
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppModuleShared } from './app.module.shared';
import { AppComponent } from './components/app/app.component';
@NgModule({
bootstrap: [ AppComponent ],
imports: [
BrowserModule,
AppModuleShared
],
providers: [
{ provide: 'ORIGIN_URL', useValue: location.origin }
]
})
export class AppModule {
}

View File

@@ -1,22 +0,0 @@
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 },
{ provide: 'PRERENDERING_DATA', useValue: (window as any).PRERENDERING_DATA }
]
})
export class AppModule {
}

View File

@@ -1,13 +1,13 @@
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { ServerModule } from '@angular/platform-server'; import { ServerModule } from '@angular/platform-server';
import { sharedConfig } from './app.module.shared'; import { AppModuleShared } from './app.module.shared';
import { AppComponent } from './components/app/app.component';
@NgModule({ @NgModule({
bootstrap: sharedConfig.bootstrap, bootstrap: [ AppComponent ],
declarations: sharedConfig.declarations,
imports: [ imports: [
ServerModule, ServerModule,
...sharedConfig.imports AppModuleShared
] ]
}) })
export class AppModule { export class AppModule {

View File

@@ -1,14 +1,16 @@
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { RouterModule } from '@angular/router'; import { RouterModule } from '@angular/router';
import { AppComponent } from './components/app/app.component' import { AppComponent } from './components/app/app.component';
import { NavMenuComponent } from './components/navmenu/navmenu.component'; import { NavMenuComponent } from './components/navmenu/navmenu.component';
import { HomeComponent } from './components/home/home.component'; import { HomeComponent } from './components/home/home.component';
import { FetchDataComponent } from './components/fetchdata/fetchdata.component'; import { FetchDataComponent } from './components/fetchdata/fetchdata.component';
import { CounterComponent } from './components/counter/counter.component'; import { CounterComponent } from './components/counter/counter.component';
export const sharedConfig: NgModule = { @NgModule({
bootstrap: [ AppComponent ],
declarations: [ declarations: [
AppComponent, AppComponent,
NavMenuComponent, NavMenuComponent,
@@ -17,6 +19,9 @@ export const sharedConfig: NgModule = {
HomeComponent HomeComponent
], ],
imports: [ imports: [
CommonModule,
HttpModule,
FormsModule,
RouterModule.forRoot([ RouterModule.forRoot([
{ path: '', redirectTo: 'home', pathMatch: 'full' }, { path: '', redirectTo: 'home', pathMatch: 'full' },
{ path: 'home', component: HomeComponent }, { path: 'home', component: HomeComponent },
@@ -25,4 +30,6 @@ export const sharedConfig: NgModule = {
{ path: '**', redirectTo: 'home' } { path: '**', redirectTo: 'home' }
]) ])
] ]
}; })
export class AppModuleShared {
}

View File

@@ -1,7 +1,5 @@
<h1>Weather forecast</h1> <h1>Weather forecast</h1>
<p>Prerendering data: {{ prerenderingDataString }}</p>
<p>This component demonstrates fetching data from the server.</p> <p>This component demonstrates fetching data from the server.</p>
<p *ngIf="!forecasts"><em>Loading...</em></p> <p *ngIf="!forecasts"><em>Loading...</em></p>

View File

@@ -7,11 +7,8 @@ import { Http } from '@angular/http';
}) })
export class FetchDataComponent { export class FetchDataComponent {
public forecasts: WeatherForecast[]; public forecasts: WeatherForecast[];
public prerenderingDataString: string;
constructor(http: Http, @Inject('ORIGIN_URL') originUrl: string, @Inject('PRERENDERING_DATA') prerenderingData: any) {
this.prerenderingDataString = JSON.stringify(prerenderingData);
constructor(http: Http, @Inject('ORIGIN_URL') originUrl: string) {
http.get(originUrl + '/api/SampleData/WeatherForecasts').subscribe(result => { http.get(originUrl + '/api/SampleData/WeatherForecasts').subscribe(result => {
this.forecasts = result.json() as WeatherForecast[]; this.forecasts = result.json() as WeatherForecast[];
}); });

View File

@@ -1,8 +1,9 @@
import 'reflect-metadata'; import 'reflect-metadata';
import 'zone.js'; import 'zone.js';
import 'bootstrap';
import { enableProdMode } from '@angular/core'; import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module.client'; import { AppModule } from './app/app.module.browser';
if (module['hot']) { if (module['hot']) {
module['hot'].accept(); module['hot'].accept();

View File

@@ -11,8 +11,7 @@ enableProdMode();
export default createServerRenderer(params => { export default createServerRenderer(params => {
const providers = [ const providers = [
{ provide: INITIAL_CONFIG, useValue: { document: '<app></app>', url: params.url } }, { provide: INITIAL_CONFIG, useValue: { document: '<app></app>', url: params.url } },
{ provide: 'ORIGIN_URL', useValue: params.origin }, { provide: 'ORIGIN_URL', useValue: params.origin }
{ provide: 'PRERENDERING_DATA', useValue: params.data }
]; ];
return platformDynamicServer(providers).bootstrapModule(AppModule).then(moduleRef => { return platformDynamicServer(providers).bootstrapModule(AppModule).then(moduleRef => {
@@ -27,8 +26,7 @@ export default createServerRenderer(params => {
// completing the request in case there's an error to report // completing the request in case there's an error to report
setImmediate(() => { setImmediate(() => {
resolve({ resolve({
html: state.renderToString(), html: state.renderToString()
globals: { PRERENDERING_DATA: params.data }
}); });
moduleRef.destroy(); moduleRef.destroy();
}); });

View File

@@ -2,11 +2,7 @@
ViewData["Title"] = "Home Page"; ViewData["Title"] = "Home Page";
} }
<app asp-prerender-module="ClientApp/dist/main-server" <app asp-prerender-module="ClientApp/dist/main-server">Loading...</app>
asp-prerender-data="new {
IsGoldUser = true,
Cookies = ViewContext.HttpContext.Request.Cookies
}">Loading...</app>
<script src="~/dist/vendor.js" asp-append-version="true"></script> <script src="~/dist/vendor.js" asp-append-version="true"></script>
@section scripts { @section scripts {

View File

@@ -1,3 +0,0 @@
{
"sdk": { "version": "sdkVersionInjectedHere" }
}

View File

@@ -1,22 +1,46 @@
<Project Sdk="Microsoft.NET.Sdk.Web"> <Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework> <TargetFramework Condition="'$(TargetFrameworkOverride)' == ''">netcoreapp2.0</TargetFramework>
<TargetFramework Condition="'$(TargetFrameworkOverride)' != ''">TargetFrameworkOverride</TargetFramework>
<TypeScriptCompileBlocked>true</TypeScriptCompileBlocked> <TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
<PackageTargetFallback>$(PackageTargetFallback);portable-net45+win8+wp8+wpa81;</PackageTargetFallback> <TypeScriptToolsVersion>Latest</TypeScriptToolsVersion>
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup Condition="'$(TargetFrameworkOverride)' == ''">
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0-*" /> <PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0-preview2-final" />
<PackageReference Include="Microsoft.AspNetCore.SpaServices" Version="2.0.0-*" /> </ItemGroup>
<ItemGroup Condition="'$(TargetFrameworkOverride)' != ''">
<PackageReference Include="NETStandard.Library.NETFramework" Version="2.0.0-preview2-25405-01" />
<PackageReference Include="Microsoft.AspNetCore" Version="2.0.0-preview2-final" />
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.0.0-preview2-final" />
<PackageReference Include="Microsoft.AspNetCore.SpaServices" Version="2.0.0-preview2-final" />
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="2.0.0-preview2-final" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.0-*" /> <DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.0-*" />
</ItemGroup> </ItemGroup>
<Target Name="RunWebpack" AfterTargets="ComputeFilesToPublish"> <!--/-:cnd:noEmit -->
<Target Name="DebugRunWebpack" BeforeTargets="Build" Condition=" '$(Configuration)' == 'Debug' And !Exists('wwwroot\dist') ">
<!-- Ensure Node.js is installed -->
<Exec Command="node --version" ContinueOnError="true">
<Output TaskParameter="ExitCode" PropertyName="ErrorCode" />
</Exec>
<Error Condition="'$(ErrorCode)' != '0'" Text="Node.js is required to build and run this project. To continue, please install Node.js from https://nodejs.org/, and then restart your command prompt or IDE." />
<!-- In development, the dist files won't exist on the first run or when cloning to
a different machine, so rebuild them if not already present. -->
<Message Importance="high" Text="Performing first-run Webpack build..." />
<Exec Command="node node_modules/webpack/bin/webpack.js --config webpack.config.vendor.js" />
<Exec Command="node node_modules/webpack/bin/webpack.js" />
</Target>
<!--/+:cnd:noEmit -->
<Target Name="PublishRunWebpack" AfterTargets="ComputeFilesToPublish">
<!-- As part of publishing, ensure the JS resources are freshly built in production mode --> <!-- As part of publishing, ensure the JS resources are freshly built in production mode -->
<Exec Command="npm install" /> <Exec Command="npm install" />
<Exec Command="node node_modules/webpack/bin/webpack.js --config webpack.config.vendor.js --env.prod" /> <Exec Command="node node_modules/webpack/bin/webpack.js --config webpack.config.vendor.js --env.prod" />

View File

@@ -1,3 +0,0 @@
{
"sdk": { "version": "sdkVersionInjectedHere" }
}

View File

@@ -1,22 +1,46 @@
<Project Sdk="Microsoft.NET.Sdk.Web"> <Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework> <TargetFramework Condition="'$(TargetFrameworkOverride)' == ''">netcoreapp2.0</TargetFramework>
<TargetFramework Condition="'$(TargetFrameworkOverride)' != ''">TargetFrameworkOverride</TargetFramework>
<TypeScriptCompileBlocked>true</TypeScriptCompileBlocked> <TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
<PackageTargetFallback>$(PackageTargetFallback);portable-net45+win8+wp8+wpa81;</PackageTargetFallback> <TypeScriptToolsVersion>Latest</TypeScriptToolsVersion>
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup Condition="'$(TargetFrameworkOverride)' == ''">
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0-*" /> <PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0-preview2-final" />
<PackageReference Include="Microsoft.AspNetCore.SpaServices" Version="2.0.0-*" /> </ItemGroup>
<ItemGroup Condition="'$(TargetFrameworkOverride)' != ''">
<PackageReference Include="NETStandard.Library.NETFramework" Version="2.0.0-preview2-25405-01" />
<PackageReference Include="Microsoft.AspNetCore" Version="2.0.0-preview2-final" />
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.0.0-preview2-final" />
<PackageReference Include="Microsoft.AspNetCore.SpaServices" Version="2.0.0-preview2-final" />
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="2.0.0-preview2-final" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.0-*" /> <DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.0-*" />
</ItemGroup> </ItemGroup>
<Target Name="RunWebpack" AfterTargets="ComputeFilesToPublish"> <!--/-:cnd:noEmit -->
<Target Name="DebugRunWebpack" BeforeTargets="Build" Condition=" '$(Configuration)' == 'Debug' And !Exists('wwwroot\dist') ">
<!-- Ensure Node.js is installed -->
<Exec Command="node --version" ContinueOnError="true">
<Output TaskParameter="ExitCode" PropertyName="ErrorCode" />
</Exec>
<Error Condition="'$(ErrorCode)' != '0'" Text="Node.js is required to build and run this project. To continue, please install Node.js from https://nodejs.org/, and then restart your command prompt or IDE." />
<!-- In development, the dist files won't exist on the first run or when cloning to
a different machine, so rebuild them if not already present. -->
<Message Importance="high" Text="Performing first-run Webpack build..." />
<Exec Command="node node_modules/webpack/bin/webpack.js --config webpack.config.vendor.js" />
<Exec Command="node node_modules/webpack/bin/webpack.js" />
</Target>
<!--/+:cnd:noEmit -->
<Target Name="PublishRunWebpack" AfterTargets="ComputeFilesToPublish">
<!-- As part of publishing, ensure the JS resources are freshly built in production mode --> <!-- As part of publishing, ensure the JS resources are freshly built in production mode -->
<Exec Command="npm install" /> <Exec Command="npm install" />
<Exec Command="node node_modules/webpack/bin/webpack.js --config webpack.config.vendor.js --env.prod" /> <Exec Command="node node_modules/webpack/bin/webpack.js --config webpack.config.vendor.js --env.prod" />

View File

@@ -1,3 +0,0 @@
{
"sdk": { "version": "sdkVersionInjectedHere" }
}

View File

@@ -1,15 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk.Web"> <Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework> <TargetFramework Condition="'$(TargetFrameworkOverride)' == ''">netcoreapp2.0</TargetFramework>
<TargetFramework Condition="'$(TargetFrameworkOverride)' != ''">TargetFrameworkOverride</TargetFramework>
<TypeScriptCompileBlocked>true</TypeScriptCompileBlocked> <TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
<PackageTargetFallback>$(PackageTargetFallback);portable-net45+win8+wp8+wpa81;</PackageTargetFallback> <TypeScriptToolsVersion>Latest</TypeScriptToolsVersion>
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup Condition="'$(TargetFrameworkOverride)' == ''">
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0-*" /> <PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0-preview2-final" />
<PackageReference Include="Microsoft.AspNetCore.SpaServices" Version="2.0.0-*" /> </ItemGroup>
<ItemGroup Condition="'$(TargetFrameworkOverride)' != ''">
<PackageReference Include="NETStandard.Library.NETFramework" Version="2.0.0-preview2-25405-01" />
<PackageReference Include="Microsoft.AspNetCore" Version="2.0.0-preview2-final" />
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.0.0-preview2-final" />
<PackageReference Include="Microsoft.AspNetCore.SpaServices" Version="2.0.0-preview2-final" />
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="2.0.0-preview2-final" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@@ -21,7 +29,23 @@
<Content Remove="ClientApp\**" /> <Content Remove="ClientApp\**" />
</ItemGroup> </ItemGroup>
<Target Name="RunWebpack" AfterTargets="ComputeFilesToPublish"> <!--/-:cnd:noEmit -->
<Target Name="DebugRunWebpack" BeforeTargets="Build" Condition=" '$(Configuration)' == 'Debug' And !Exists('wwwroot\dist') ">
<!-- Ensure Node.js is installed -->
<Exec Command="node --version" ContinueOnError="true">
<Output TaskParameter="ExitCode" PropertyName="ErrorCode" />
</Exec>
<Error Condition="'$(ErrorCode)' != '0'" Text="Node.js is required to build and run this project. To continue, please install Node.js from https://nodejs.org/, and then restart your command prompt or IDE." />
<!-- In development, the dist files won't exist on the first run or when cloning to
a different machine, so rebuild them if not already present. -->
<Message Importance="high" Text="Performing first-run Webpack build..." />
<Exec Command="node node_modules/webpack/bin/webpack.js --config webpack.config.vendor.js" />
<Exec Command="node node_modules/webpack/bin/webpack.js" />
</Target>
<!--/+:cnd:noEmit -->
<Target Name="PublishRunWebpack" AfterTargets="ComputeFilesToPublish">
<!-- As part of publishing, ensure the JS resources are freshly built in production mode --> <!-- As part of publishing, ensure the JS resources are freshly built in production mode -->
<Exec Command="npm install" /> <Exec Command="npm install" />
<Exec Command="node node_modules/webpack/bin/webpack.js --config webpack.config.vendor.js --env.prod" /> <Exec Command="node node_modules/webpack/bin/webpack.js --config webpack.config.vendor.js --env.prod" />

View File

@@ -1,3 +0,0 @@
{
"sdk": { "version": "sdkVersionInjectedHere" }
}

View File

@@ -1,22 +1,46 @@
<Project Sdk="Microsoft.NET.Sdk.Web"> <Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework> <TargetFramework Condition="'$(TargetFrameworkOverride)' == ''">netcoreapp2.0</TargetFramework>
<TargetFramework Condition="'$(TargetFrameworkOverride)' != ''">TargetFrameworkOverride</TargetFramework>
<TypeScriptCompileBlocked>true</TypeScriptCompileBlocked> <TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
<PackageTargetFallback>$(PackageTargetFallback);portable-net45+win8+wp8+wpa81;</PackageTargetFallback> <TypeScriptToolsVersion>Latest</TypeScriptToolsVersion>
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup Condition="'$(TargetFrameworkOverride)' == ''">
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0-*" /> <PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0-preview2-final" />
<PackageReference Include="Microsoft.AspNetCore.SpaServices" Version="2.0.0-*" /> </ItemGroup>
<ItemGroup Condition="'$(TargetFrameworkOverride)' != ''">
<PackageReference Include="NETStandard.Library.NETFramework" Version="2.0.0-preview2-25405-01" />
<PackageReference Include="Microsoft.AspNetCore" Version="2.0.0-preview2-final" />
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.0.0-preview2-final" />
<PackageReference Include="Microsoft.AspNetCore.SpaServices" Version="2.0.0-preview2-final" />
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="2.0.0-preview2-final" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.0-*" /> <DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.0-*" />
</ItemGroup> </ItemGroup>
<Target Name="RunWebpack" AfterTargets="ComputeFilesToPublish"> <!--/-:cnd:noEmit -->
<Target Name="DebugRunWebpack" BeforeTargets="Build" Condition=" '$(Configuration)' == 'Debug' And !Exists('wwwroot\dist') ">
<!-- Ensure Node.js is installed -->
<Exec Command="node --version" ContinueOnError="true">
<Output TaskParameter="ExitCode" PropertyName="ErrorCode" />
</Exec>
<Error Condition="'$(ErrorCode)' != '0'" Text="Node.js is required to build and run this project. To continue, please install Node.js from https://nodejs.org/, and then restart your command prompt or IDE." />
<!-- In development, the dist files won't exist on the first run or when cloning to
a different machine, so rebuild them if not already present. -->
<Message Importance="high" Text="Performing first-run Webpack build..." />
<Exec Command="node node_modules/webpack/bin/webpack.js --config webpack.config.vendor.js" />
<Exec Command="node node_modules/webpack/bin/webpack.js" />
</Target>
<!--/+:cnd:noEmit -->
<Target Name="PublishRunWebpack" AfterTargets="ComputeFilesToPublish">
<!-- As part of publishing, ensure the JS resources are freshly built in production mode --> <!-- As part of publishing, ensure the JS resources are freshly built in production mode -->
<Exec Command="npm install" /> <Exec Command="npm install" />
<Exec Command="node node_modules/webpack/bin/webpack.js --config webpack.config.vendor.js --env.prod" /> <Exec Command="node node_modules/webpack/bin/webpack.js --config webpack.config.vendor.js --env.prod" />

View File

@@ -1,3 +0,0 @@
{
"sdk": { "version": "sdkVersionInjectedHere" }
}

View File

@@ -1,22 +1,46 @@
<Project Sdk="Microsoft.NET.Sdk.Web"> <Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework> <TargetFramework Condition="'$(TargetFrameworkOverride)' == ''">netcoreapp2.0</TargetFramework>
<TargetFramework Condition="'$(TargetFrameworkOverride)' != ''">TargetFrameworkOverride</TargetFramework>
<TypeScriptCompileBlocked>true</TypeScriptCompileBlocked> <TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
<PackageTargetFallback>$(PackageTargetFallback);portable-net45+win8+wp8+wpa81;</PackageTargetFallback> <TypeScriptToolsVersion>Latest</TypeScriptToolsVersion>
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup Condition="'$(TargetFrameworkOverride)' == ''">
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0-*" /> <PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0-preview2-final" />
<PackageReference Include="Microsoft.AspNetCore.SpaServices" Version="2.0.0-*" /> </ItemGroup>
<ItemGroup Condition="'$(TargetFrameworkOverride)' != ''">
<PackageReference Include="NETStandard.Library.NETFramework" Version="2.0.0-preview2-25405-01" />
<PackageReference Include="Microsoft.AspNetCore" Version="2.0.0-preview2-final" />
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.0.0-preview2-final" />
<PackageReference Include="Microsoft.AspNetCore.SpaServices" Version="2.0.0-preview2-final" />
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="2.0.0-preview2-final" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.0-*" /> <DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.0-*" />
</ItemGroup> </ItemGroup>
<Target Name="RunWebpack" AfterTargets="ComputeFilesToPublish"> <!--/-:cnd:noEmit -->
<Target Name="DebugRunWebpack" BeforeTargets="Build" Condition=" '$(Configuration)' == 'Debug' And !Exists('wwwroot\dist') ">
<!-- Ensure Node.js is installed -->
<Exec Command="node --version" ContinueOnError="true">
<Output TaskParameter="ExitCode" PropertyName="ErrorCode" />
</Exec>
<Error Condition="'$(ErrorCode)' != '0'" Text="Node.js is required to build and run this project. To continue, please install Node.js from https://nodejs.org/, and then restart your command prompt or IDE." />
<!-- In development, the dist files won't exist on the first run or when cloning to
a different machine, so rebuild them if not already present. -->
<Message Importance="high" Text="Performing first-run Webpack build..." />
<Exec Command="node node_modules/webpack/bin/webpack.js --config webpack.config.vendor.js" />
<Exec Command="node node_modules/webpack/bin/webpack.js" />
</Target>
<!--/+:cnd:noEmit -->
<Target Name="PublishRunWebpack" AfterTargets="ComputeFilesToPublish">
<!-- As part of publishing, ensure the JS resources are freshly built in production mode --> <!-- As part of publishing, ensure the JS resources are freshly built in production mode -->
<Exec Command="npm install" /> <Exec Command="npm install" />
<Exec Command="node node_modules/webpack/bin/webpack.js --config webpack.config.vendor.js --env.prod" /> <Exec Command="node node_modules/webpack/bin/webpack.js --config webpack.config.vendor.js --env.prod" />

View File

@@ -1,3 +0,0 @@
{
"sdk": { "version": "sdkVersionInjectedHere" }
}

View File

@@ -11,26 +11,29 @@ import * as targz from 'tar.gz';
const isWindows = /^win/.test(process.platform); const isWindows = /^win/.test(process.platform);
const textFileExtensions = ['.gitignore', 'template_gitignore', '.config', '.cs', '.cshtml', '.csproj', '.html', '.js', '.json', '.jsx', '.md', '.nuspec', '.ts', '.tsx']; const textFileExtensions = ['.gitignore', 'template_gitignore', '.config', '.cs', '.cshtml', '.csproj', '.html', '.js', '.json', '.jsx', '.md', '.nuspec', '.ts', '.tsx'];
const yeomanGeneratorSource = './src/yeoman'; const yeomanGeneratorSource = './src/yeoman';
const webToolsVSPackageGuid = '{0CD94836-1526-4E85-87D3-FB5274C5AFC9}';
// To support the "dotnet new" templates, we want to bundle prebuilt dist dev-mode files, because "dotnet new" can't auto-run const dotNetPackages = {
// webpack on project creation. Note that these script entries are *not* the same as the project's usual prepublish builtIn: 'Microsoft.DotNet.Web.Spa.ProjectTemplates',
// scripts, because here we want dev-mode builds (e.g., to support HMR), not prod-mode builds. extra: 'Microsoft.AspNetCore.SpaTemplates'
const commonTemplatePrepublishSteps = [
'npm install',
'node node_modules/webpack/bin/webpack.js --config webpack.config.vendor.js',
'node node_modules/webpack/bin/webpack.js'
];
const commonForceInclusionRegex = /^(wwwroot|ClientApp)\/dist\//; // Files to be included in template, even though gitignored
const templates: { [key: string]: { dir: string, dotNetNewId: string, displayName: string, prepublish?: string[], forceInclusion?: RegExp } } = {
'angular': { dir: '../../templates/AngularSpa/', dotNetNewId: 'Angular', displayName: 'Angular' },
'aurelia': { dir: '../../templates/AureliaSpa/', dotNetNewId: 'Aurelia', displayName: 'Aurelia' },
'knockout': { dir: '../../templates/KnockoutSpa/', dotNetNewId: 'Knockout', displayName: 'Knockout.js' },
'react-redux': { dir: '../../templates/ReactReduxSpa/', dotNetNewId: 'ReactRedux', displayName: 'React.js and Redux' },
'react': { dir: '../../templates/ReactSpa/', dotNetNewId: 'React', displayName: 'React.js' },
'vue': { dir: '../../templates/VueSpa/', dotNetNewId: 'Vue', displayName: 'Vue.js' }
}; };
interface TemplateConfig {
dir: string;
dotNetNewId: string;
dotNetPackageId: string;
displayName: string;
localizationIdStart: number;
}
const templates: { [key: string]: TemplateConfig } = {
'angular': { dotNetPackageId: dotNetPackages.builtIn, dir: '../../templates/AngularSpa/', dotNetNewId: 'Angular', displayName: 'Angular', localizationIdStart: 1100 },
'aurelia': { dotNetPackageId: dotNetPackages.extra, dir: '../../templates/AureliaSpa/', dotNetNewId: 'Aurelia', displayName: 'Aurelia', localizationIdStart: 1200 },
'knockout': { dotNetPackageId: dotNetPackages.extra, dir: '../../templates/KnockoutSpa/', dotNetNewId: 'Knockout', displayName: 'Knockout.js', localizationIdStart: 1300 },
'react-redux': { dotNetPackageId: dotNetPackages.builtIn, dir: '../../templates/ReactReduxSpa/', dotNetNewId: 'ReactRedux', displayName: 'React.js and Redux', localizationIdStart: 1400 },
'react': { dotNetPackageId: dotNetPackages.builtIn, dir: '../../templates/ReactSpa/', dotNetNewId: 'React', displayName: 'React.js', localizationIdStart: 1500 },
'vue': { dotNetPackageId: dotNetPackages.extra, dir: '../../templates/VueSpa/', dotNetNewId: 'Vue', displayName: 'Vue.js', localizationIdStart: 1600 }
};
function isTextFile(filename: string): boolean { function isTextFile(filename: string): boolean {
return textFileExtensions.indexOf(path.extname(filename).toLowerCase()) >= 0 return textFileExtensions.indexOf(path.extname(filename).toLowerCase()) >= 0
@@ -43,7 +46,7 @@ function writeFileEnsuringDirExists(root: string, filename: string, contents: st
fs.writeFileSync(fullPath, contents); fs.writeFileSync(fullPath, contents);
} }
function listFilesExcludingGitignored(root: string, forceInclusion: RegExp): string[] { function listFilesExcludingGitignored(root: string): string[] {
// Note that the gitignore files, prior to be written by the generator, are called 'template_gitignore' // Note that the gitignore files, prior to be written by the generator, are called 'template_gitignore'
// instead of '.gitignore'. This is a workaround for Yeoman doing strange stuff with .gitignore files // instead of '.gitignore'. This is a workaround for Yeoman doing strange stuff with .gitignore files
// (it renames them to .npmignore, which is not helpful). // (it renames them to .npmignore, which is not helpful).
@@ -52,21 +55,23 @@ function listFilesExcludingGitignored(root: string, forceInclusion: RegExp): str
? gitignore.compile(fs.readFileSync(gitIgnorePath, 'utf8')) ? gitignore.compile(fs.readFileSync(gitIgnorePath, 'utf8'))
: { accepts: () => true }; : { accepts: () => true };
return glob.sync('**/*', { cwd: root, dot: true, nodir: true }) return glob.sync('**/*', { cwd: root, dot: true, nodir: true })
.filter(fn => gitignoreEvaluator.accepts(fn) || (forceInclusion && forceInclusion.test(fn))); .filter(fn => gitignoreEvaluator.accepts(fn));
} }
function writeTemplate(sourceRoot: string, destRoot: string, contentReplacements: { from: RegExp, to: string }[], filenameReplacements: { from: RegExp, to: string }[], forceInclusion: RegExp) { function applyContentReplacements(sourceContent: Buffer, contentReplacements: { from: RegExp, to: string }[]) {
listFilesExcludingGitignored(sourceRoot, forceInclusion).forEach(fn => {
let sourceContent = fs.readFileSync(path.join(sourceRoot, fn));
// For text files, replace hardcoded values with template tags
if (isTextFile(fn)) {
let sourceText = sourceContent.toString('utf8'); let sourceText = sourceContent.toString('utf8');
contentReplacements.forEach(replacement => { contentReplacements.forEach(replacement => {
sourceText = sourceText.replace(replacement.from, replacement.to); sourceText = sourceText.replace(replacement.from, replacement.to);
}); });
sourceContent = new Buffer(sourceText, 'utf8'); return new Buffer(sourceText, 'utf8');
}
function writeTemplate(sourceRoot: string, destRoot: string, contentReplacements: { from: RegExp, to: string }[], filenameReplacements: { from: RegExp, to: string }[]) {
listFilesExcludingGitignored(sourceRoot).forEach(fn => {
let sourceContent = fs.readFileSync(path.join(sourceRoot, fn));
if (isTextFile(fn)) {
sourceContent = applyContentReplacements(sourceContent, contentReplacements);
} }
// Also apply replacements in filenames // Also apply replacements in filenames
@@ -86,6 +91,22 @@ function copyRecursive(sourceRoot: string, destRoot: string, matchGlob: string)
}); });
} }
function leftPad(str: string, minLength: number, padChar: string) {
while (str.length < minLength) {
str = padChar + str;
}
return str;
}
function getBuildNumber(): string {
if (process.env.APPVEYOR_BUILD_NUMBER) {
return leftPad(process.env.APPVEYOR_BUILD_NUMBER, 6, '0');
}
// For local builds, use timestamp
return 't-' + Math.floor((new Date().valueOf() - new Date(2017, 0, 1).valueOf()) / (60*1000));
}
function buildYeomanNpmPackage(outputRoot: string) { function buildYeomanNpmPackage(outputRoot: string) {
const outputTemplatesRoot = path.join(outputRoot, 'app/templates'); const outputTemplatesRoot = path.join(outputRoot, 'app/templates');
rimraf.sync(outputTemplatesRoot); rimraf.sync(outputTemplatesRoot);
@@ -95,12 +116,11 @@ function buildYeomanNpmPackage(outputRoot: string) {
{ from: /.*\.csproj$/, to: 'tokenreplace-namePascalCase.csproj' } { from: /.*\.csproj$/, to: 'tokenreplace-namePascalCase.csproj' }
]; ];
const contentReplacements = [ const contentReplacements = [
// global.json items // Currently, there are none
{ from: /sdkVersionInjectedHere/, to: '<%= sdkVersion %>' }
]; ];
_.forEach(templates, (templateConfig, templateName) => { _.forEach(templates, (templateConfig, templateName) => {
const outputDir = path.join(outputTemplatesRoot, templateName); const outputDir = path.join(outputTemplatesRoot, templateName);
writeTemplate(templateConfig.dir, outputDir, contentReplacements, filenameReplacements, commonForceInclusionRegex); writeTemplate(templateConfig.dir, outputDir, contentReplacements, filenameReplacements);
}); });
// Also copy the generator files (that's the compiled .js files, plus all other non-.ts files) // Also copy the generator files (that's the compiled .js files, plus all other non-.ts files)
@@ -112,7 +132,17 @@ function buildYeomanNpmPackage(outputRoot: string) {
rimraf.sync(tempRoot); rimraf.sync(tempRoot);
} }
function buildDotNetNewNuGetPackage() { function buildDotNetNewNuGetPackages(outputDir: string) {
const dotNetPackageIds = _.values(dotNetPackages);
dotNetPackageIds.forEach(packageId => {
const dotNetNewNupkgPath = buildDotNetNewNuGetPackage(packageId);
// Move the .nupkg file to the output dir
fs.renameSync(dotNetNewNupkgPath, path.join(outputDir, path.basename(dotNetNewNupkgPath)));
});
}
function buildDotNetNewNuGetPackage(packageId: string) {
const outputRoot = './dist/dotnetnew'; const outputRoot = './dist/dotnetnew';
rimraf.sync(outputRoot); rimraf.sync(outputRoot);
@@ -125,8 +155,13 @@ function buildDotNetNewNuGetPackage() {
]; ];
const contentReplacements = []; const contentReplacements = [];
_.forEach(templates, (templateConfig, templateName) => { _.forEach(templates, (templateConfig, templateName) => {
// Only include templates matching the output package ID
if (templateConfig.dotNetPackageId !== packageId) {
return;
}
const templateOutputDir = path.join(outputRoot, 'Content', templateName); const templateOutputDir = path.join(outputRoot, 'Content', templateName);
writeTemplate(templateConfig.dir, templateOutputDir, contentReplacements, filenameReplacements, commonForceInclusionRegex); writeTemplate(templateConfig.dir, templateOutputDir, contentReplacements, filenameReplacements);
// Add the .template.config dir and its contents // Add the .template.config dir and its contents
const templateConfigDir = path.join(templateOutputDir, '.template.config'); const templateConfigDir = path.join(templateOutputDir, '.template.config');
@@ -134,10 +169,10 @@ function buildDotNetNewNuGetPackage() {
fs.writeFileSync(path.join(templateConfigDir, 'template.json'), JSON.stringify({ fs.writeFileSync(path.join(templateConfigDir, 'template.json'), JSON.stringify({
author: 'Microsoft', author: 'Microsoft',
classifications: ["Web", "MVC", "SPA"], classifications: ['Web', 'MVC', 'SPA'],
groupIdentity: `Microsoft.AspNetCore.SpaTemplates.${templateConfig.dotNetNewId}`, groupIdentity: `${packageId}.${templateConfig.dotNetNewId}`,
identity: `Microsoft.AspNetCore.SpaTemplates.${templateConfig.dotNetNewId}.CSharp`, identity: `${packageId}.${templateConfig.dotNetNewId}.CSharp`,
name: `MVC ASP.NET Core with ${templateConfig.displayName}`, name: `ASP.NET Core with ${templateConfig.displayName}`,
preferNameDirectory: true, preferNameDirectory: true,
primaryOutputs: [{ path: `${sourceProjectName}.csproj` }], primaryOutputs: [{ path: `${sourceProjectName}.csproj` }],
shortName: `${templateConfig.dotNetNewId.toLowerCase()}`, shortName: `${templateConfig.dotNetNewId.toLowerCase()}`,
@@ -148,46 +183,116 @@ function buildDotNetNewNuGetPackage() {
exclude: ['.template.config/**'] exclude: ['.template.config/**']
}], }],
symbols: { symbols: {
sdkVersion: { TargetFrameworkOverride: {
type: 'bind', type: 'parameter',
binding: 'dotnet-cli-version', description: 'Overrides the target framework',
replaces: 'sdkVersionInjectedHere' replaces: 'TargetFrameworkOverride',
datatype: 'string',
defaultValue: ''
}, },
Framework: { Framework: {
type: "parameter", type: 'parameter',
description: "The target framework for the project.", description: 'The target framework for the project.',
datatype: "choice", datatype: 'choice',
choices: [ choices: [
{ {
choice: "netcoreapp1.1", choice: 'netcoreapp2.0',
description: "Target netcoreapp1.1" description: 'Target netcoreapp2.0'
} }
], ],
defaultValue: "netcoreapp1.1" replaces: 'netcoreapp2.0',
defaultValue: 'netcoreapp2.0'
},
HostIdentifier: {
type: 'bind',
binding: 'HostIdentifier'
},
skipRestore: {
type: 'parameter',
datatype: 'bool',
description: 'If specified, skips the automatic restore of packages on project creation.',
defaultValue: 'false'
} }
}, },
tags: { language: 'C#', type: 'project' }, tags: { language: 'C#', type: 'project' },
postActions: [
{
condition: '(!skipRestore)',
description: 'Restores NuGet packages required by this project.',
manualInstructions: [{ text: 'Run \'dotnet restore\'' }],
actionId: '210D431B-A78B-4D2F-B762-4ED3E3EA9025',
continueOnError: true
},
/*
// Currently it doesn't appear to be possible to run `npm install` from a
// postAction, due to https://github.com/dotnet/templating/issues/849
// We will re-enable this when that bug is fixed.
{
condition: '(!skipRestore)',
description: 'Restores NPM packages required by this project.',
manualInstructions: [{ text: 'Run \'npm install\'' }],
actionId: '3A7C4B45-1F5D-4A30-959A-51B88E82B5D2',
args: { executable: 'npm', args: 'install' },
continueOnError: false
}
*/
{
// For the preview2 release, just display manual instructions instead.
// This is only applicable on the command line, because VS will restore
// NPM packages automatically by default.
condition: '(HostIdentifier == "dotnetcli" || HostIdentifier == "dotnetcli-preview")',
actionId: 'AC1156F7-BB77-4DB8-B28F-24EEBCCA1E5C',
description: '\n\n-------------------------------------------------------------------\nIMPORTANT: Before running this project on the command line,\n you must restore NPM packages by running "npm install"\n-------------------------------------------------------------------\n',
manualInstructions: [{ text: 'Run "npm install"' }]
}
],
}, null, 2)); }, null, 2));
fs.writeFileSync(path.join(templateConfigDir, 'dotnetcli.host.json'), JSON.stringify({ fs.writeFileSync(path.join(templateConfigDir, 'dotnetcli.host.json'), JSON.stringify({
symbolInfo: {} $schema: 'http://json.schemastore.org/dotnetcli.host',
symbolInfo: {
TargetFrameworkOverride: {
isHidden: 'true',
longName: 'target-framework-override',
shortName: ''
},
Framework: {
longName: 'framework'
},
skipRestore: {
longName: 'no-restore',
shortName: ''
}
}
}, null, 2)); }, null, 2));
const localisedNameId = templateConfig.localizationIdStart + 0;
const localisedDescId = templateConfig.localizationIdStart + 1;
fs.writeFileSync(path.join(templateConfigDir, 'vs-2017.3.host.json'), JSON.stringify({ fs.writeFileSync(path.join(templateConfigDir, 'vs-2017.3.host.json'), JSON.stringify({
name: { text: templateConfig.displayName }, $schema: 'http://json.schemastore.org/vs-2017.3.host',
description: { text: `Web application built with MVC ASP.NET Core and ${templateConfig.displayName}` }, name: { text: templateConfig.displayName, package: webToolsVSPackageGuid, id: localisedNameId.toString() },
order: 2000, description: { text: `A project template for creating an ASP.NET Core application with ${templateConfig.displayName}`, package: webToolsVSPackageGuid, id: localisedDescId.toString() },
order: 301,
icon: 'icon.png', icon: 'icon.png',
learnMoreLink: 'https://github.com/aspnet/JavaScriptServices', learnMoreLink: 'https://github.com/aspnet/JavaScriptServices',
uiFilters: [ 'oneaspnet' ] uiFilters: [ 'oneaspnet' ],
minFullFrameworkVersion: '4.6.1'
}, null, 2)); }, null, 2));
}); });
// Invoke NuGet to create the final package // Create the .nuspec file
const yeomanPackageVersion = JSON.parse(fs.readFileSync(path.join(yeomanGeneratorSource, 'package.json'), 'utf8')).version; const yeomanPackageVersion = JSON.parse(fs.readFileSync(path.join(yeomanGeneratorSource, 'package.json'), 'utf8')).version;
writeTemplate('./src/dotnetnew', outputRoot, [ const nuspecContentTemplate = fs.readFileSync(`./src/dotnetnew/${ packageId }.nuspec`);
{ from: /\{version\}/g, to: yeomanPackageVersion }, writeFileEnsuringDirExists(outputRoot,
], [], null); `${ packageId }.nuspec`,
applyContentReplacements(nuspecContentTemplate, [
{ from: /\{yeomanversion\}/g, to: yeomanPackageVersion },
{ from: /\{buildnumber\}/g, to: getBuildNumber() },
])
);
// Invoke NuGet to create the final package
const nugetExe = path.join(process.cwd(), './bin/NuGet.exe'); const nugetExe = path.join(process.cwd(), './bin/NuGet.exe');
const nugetStartInfo = { cwd: outputRoot, stdio: 'inherit' }; const nugetStartInfo = { cwd: outputRoot, stdio: 'inherit' };
if (isWindows) { if (isWindows) {
@@ -204,21 +309,7 @@ function buildDotNetNewNuGetPackage() {
return glob.sync(path.join(outputRoot, './*.nupkg'))[0]; return glob.sync(path.join(outputRoot, './*.nupkg'))[0];
} }
function runAllPrepublishScripts() { function runPrepublishScripts(rootDir: string, scripts: string[]) {
Object.getOwnPropertyNames(templates).forEach(templateKey => {
const templateInfo = templates[templateKey];
// First run standard prepublish steps
runScripts(templateInfo.dir, commonTemplatePrepublishSteps);
// Second, run any template-specific prepublish steps
if (templateInfo.prepublish) {
runScripts(templateInfo.dir, templateInfo.prepublish);
}
});
}
function runScripts(rootDir: string, scripts: string[]) {
console.log(`[Prepublish] In directory: ${ rootDir }`); console.log(`[Prepublish] In directory: ${ rootDir }`);
scripts.forEach(script => { scripts.forEach(script => {
console.log(`[Prepublish] Running: ${ script }`); console.log(`[Prepublish] Running: ${ script }`);
@@ -233,12 +324,8 @@ const yeomanOutputRoot = path.join(distDir, 'generator-aspnetcore-spa');
rimraf.sync(distDir); rimraf.sync(distDir);
mkdirp.sync(artifactsDir); mkdirp.sync(artifactsDir);
runAllPrepublishScripts();
buildYeomanNpmPackage(yeomanOutputRoot); buildYeomanNpmPackage(yeomanOutputRoot);
const dotNetNewNupkgPath = buildDotNetNewNuGetPackage(); buildDotNetNewNuGetPackages(artifactsDir);
// Move the .nupkg file to the artifacts dir
fs.renameSync(dotNetNewNupkgPath, path.join(artifactsDir, path.basename(dotNetNewNupkgPath)));
// Finally, create a .tar.gz file containing the built generator-aspnetcore-spa. // Finally, create a .tar.gz file containing the built generator-aspnetcore-spa.
// The CI system can treat this as the final built artifact. // The CI system can treat this as the final built artifact.

View File

@@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd"> <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata> <metadata>
<id>Microsoft.AspNetCore.SpaTemplates</id> <id>Microsoft.AspNetCore.SpaTemplates</id>
<version>{version}</version> <version>{yeomanversion}</version>
<description>Single Page Application templates for ASP.NET Core</description> <description>Single Page Application templates for ASP.NET Core</description>
<authors>Microsoft</authors> <authors>Microsoft</authors>
<language>en-US</language> <language>en-US</language>

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>Microsoft.DotNet.Web.Spa.ProjectTemplates</id>
<version>1.0.0-preview-{buildnumber}</version>
<description>Single Page Application templates for ASP.NET Core</description>
<authors>Microsoft</authors>
<language>en-US</language>
<projectUrl>https://github.com/aspnet/javascriptservices</projectUrl>
<licenseUrl>https://www.microsoft.com/web/webpi/eula/net_library_eula_enu.htm</licenseUrl>
<copyright>Copyright © Microsoft Corporation</copyright>
<requireLicenseAcceptance>true</requireLicenseAcceptance>
<packageTypes>
<packageType name="Template" />
</packageTypes>
</metadata>
</package>