mirror of
https://github.com/aspnet/JavaScriptServices.git
synced 2025-12-23 10:08:57 +00:00
Compare commits
1 Commits
release/2.
...
angular4-p
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
678e230021 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -28,8 +28,6 @@ nuget.exe
|
|||||||
.vs/
|
.vs/
|
||||||
npm-debug.log
|
npm-debug.log
|
||||||
/.build/
|
/.build/
|
||||||
global.json
|
|
||||||
*.g.targets
|
|
||||||
|
|
||||||
# The templates can't contain their own .gitignore files, because Yeoman has strange default handling for
|
# The templates can't contain their own .gitignore files, because Yeoman has strange default handling for
|
||||||
# files with that name (https://github.com/npm/npm/issues/1862). So, each template instead has a template_gitignore
|
# files with that name (https://github.com/npm/npm/issues/1862). So, each template instead has a template_gitignore
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
<Project>
|
|
||||||
<Import Project="version.props" />
|
|
||||||
<Import Project="build\dependencies.props" />
|
|
||||||
<Import Project="build\sources.props" />
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<RepositoryRoot>$(MSBuildThisFileDirectory)</RepositoryRoot>
|
|
||||||
</PropertyGroup>
|
|
||||||
</Project>
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
<Project>
|
|
||||||
<PropertyGroup>
|
|
||||||
<NETStandardImplicitPackageVersion Condition=" '$(TargetFramework)' == 'netstandard2.0' ">$(NETStandardLibrary20PackageVersion)</NETStandardImplicitPackageVersion>
|
|
||||||
<RuntimeFrameworkVersion Condition=" '$(TargetFramework)' == 'netcoreapp2.0' ">$(MicrosoftNETCoreApp20PackageVersion)</RuntimeFrameworkVersion>
|
|
||||||
</PropertyGroup>
|
|
||||||
</Project>
|
|
||||||
@@ -1,12 +1,10 @@
|
|||||||
Copyright (c) .NET Foundation and Contributors
|
Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||||
this file except in compliance with the License. You may obtain a copy of the
|
these files except in compliance with the License. You may obtain a copy of the
|
||||||
License at
|
License at
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software distributed
|
Unless required by applicable law or agreed to in writing, software distributed
|
||||||
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<configuration>
|
<configuration>
|
||||||
<packageSources>
|
<packageSources>
|
||||||
<clear />
|
<add key="AspNetCore" value="https://dotnet.myget.org/F/aspnetcore-ci-dev/api/v3/index.json" />
|
||||||
<!-- Restore sources should be defined in build/sources.props. -->
|
<add key="AspNetCoreTools" value="https://dotnet.myget.org/F/aspnetcore-tools/api/v3/index.json" />
|
||||||
|
<add key="NuGet" value="https://api.nuget.org/v3/index.json" />
|
||||||
</packageSources>
|
</packageSources>
|
||||||
</configuration>
|
</configuration>
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ If you're interested in contributing to the various packages, samples, and proje
|
|||||||
* Run `dotnet restore` at the repo root dir
|
* Run `dotnet restore` at the repo root dir
|
||||||
* Go to whatever sample or template you want to run (for example, `cd templates/AngularSpa`)
|
* Go to whatever sample or template you want to run (for example, `cd templates/AngularSpa`)
|
||||||
* Restore NPM dependencies (run `npm install`)
|
* Restore NPM dependencies (run `npm install`)
|
||||||
* If the sample/template you're trying to run has a file called `webpack.config.vendor.js` at its root, run `webpack --config webpack.config.vendor.js`. If it has a file called `webpack.config.js`, run `webpack` (no args). You might need to install webpack first, by running `npm install -g webpack`.
|
* If the sample/template you're trying to run has a file called `webpack.config.vendor.js` at its root, run `webpack --config webpack.config.vendor.js`. It it has a file called `webpack.config.js`, run `webpack` (no args). You might need to install webpack first, by running `npm install -g webpack`.
|
||||||
* Launch it (`dotnet run`)
|
* Launch it (`dotnet run`)
|
||||||
|
|
||||||
If you're planning to submit a pull request, and if it's more than a trivial fix (for example, for a typo), it's usually a good idea first to file an issue describing what you're proposing to do and how it will work. Then you can find out if it's likely that such a pull request will be accepted, and how it fits into wider ongoing plans.
|
If you're planning to submit a pull request, and if it's more than a trivial fix (for example, for a typo), it's usually a good idea first to file an issue describing what you're proposing to do and how it will work. Then you can find out if it's likely that such a pull request will be accepted, and how it fits into wider ongoing plans.
|
||||||
|
|||||||
@@ -3,11 +3,11 @@ init:
|
|||||||
install:
|
install:
|
||||||
- ps: Install-Product node 6.9.2 x64
|
- ps: Install-Product node 6.9.2 x64
|
||||||
# .NET Core SDK binaries
|
# .NET Core SDK binaries
|
||||||
# Download .NET Core 2.0 Preview 3 SDK and add to PATH
|
# Download .NET Core 2.0 Preview 1 SDK and add to PATH
|
||||||
- ps: $urlCurrent = "https://dotnetcli.azureedge.net/dotnet/Sdk/2.0.0-preview3-006729/dotnet-sdk-2.0.0-preview3-006729-win-x64.zip"
|
- ps: $urlCurrent = "https://download.microsoft.com/download/0/6/5/0656B047-5F2F-4281-A851-F30776F8616D/dotnet-dev-win-x64.2.0.0-preview1-005977.zip"
|
||||||
- ps: $env:DOTNET_INSTALL_DIR = "$pwd\.dotnetsdk"
|
- ps: $env:DOTNET_INSTALL_DIR = "$pwd\.dotnetsdk"
|
||||||
- ps: mkdir $env:DOTNET_INSTALL_DIR -Force | Out-Null
|
- ps: mkdir $env:DOTNET_INSTALL_DIR -Force | Out-Null
|
||||||
- ps: $tempFileCurrent = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName())
|
- ps: $tempFileCurrent = [System.IO.Path]::GetTempFileName()
|
||||||
- ps: (New-Object System.Net.WebClient).DownloadFile($urlCurrent, $tempFileCurrent)
|
- ps: (New-Object System.Net.WebClient).DownloadFile($urlCurrent, $tempFileCurrent)
|
||||||
- ps: Add-Type -AssemblyName System.IO.Compression.FileSystem; [System.IO.Compression.ZipFile]::ExtractToDirectory($tempFileCurrent, $env:DOTNET_INSTALL_DIR)
|
- ps: Add-Type -AssemblyName System.IO.Compression.FileSystem; [System.IO.Compression.ZipFile]::ExtractToDirectory($tempFileCurrent, $env:DOTNET_INSTALL_DIR)
|
||||||
- ps: $env:Path = "$env:DOTNET_INSTALL_DIR;$env:Path"
|
- ps: $env:Path = "$env:DOTNET_INSTALL_DIR;$env:Path"
|
||||||
|
|||||||
219
build.ps1
219
build.ps1
@@ -1,186 +1,67 @@
|
|||||||
#!/usr/bin/env powershell
|
$ErrorActionPreference = "Stop"
|
||||||
#requires -version 4
|
|
||||||
|
|
||||||
<#
|
function DownloadWithRetry([string] $url, [string] $downloadLocation, [int] $retries)
|
||||||
.SYNOPSIS
|
|
||||||
Build this repository
|
|
||||||
|
|
||||||
.DESCRIPTION
|
|
||||||
Downloads korebuild if required. Then builds the repository.
|
|
||||||
|
|
||||||
.PARAMETER Path
|
|
||||||
The folder to build. Defaults to the folder containing this script.
|
|
||||||
|
|
||||||
.PARAMETER Channel
|
|
||||||
The channel of KoreBuild to download. Overrides the value from the config file.
|
|
||||||
|
|
||||||
.PARAMETER DotNetHome
|
|
||||||
The directory where .NET Core tools will be stored.
|
|
||||||
|
|
||||||
.PARAMETER ToolsSource
|
|
||||||
The base url where build tools can be downloaded. Overrides the value from the config file.
|
|
||||||
|
|
||||||
.PARAMETER Update
|
|
||||||
Updates KoreBuild to the latest version even if a lock file is present.
|
|
||||||
|
|
||||||
.PARAMETER ConfigFile
|
|
||||||
The path to the configuration file that stores values. Defaults to version.props.
|
|
||||||
|
|
||||||
.PARAMETER MSBuildArgs
|
|
||||||
Arguments to be passed to MSBuild
|
|
||||||
|
|
||||||
.NOTES
|
|
||||||
This function will create a file $PSScriptRoot/korebuild-lock.txt. This lock file can be committed to source, but does not have to be.
|
|
||||||
When the lockfile is not present, KoreBuild will create one using latest available version from $Channel.
|
|
||||||
|
|
||||||
The $ConfigFile is expected to be an JSON file. It is optional, and the configuration values in it are optional as well. Any options set
|
|
||||||
in the file are overridden by command line parameters.
|
|
||||||
|
|
||||||
.EXAMPLE
|
|
||||||
Example config file:
|
|
||||||
```json
|
|
||||||
{
|
{
|
||||||
"$schema": "https://raw.githubusercontent.com/aspnet/BuildTools/dev/tools/korebuild.schema.json",
|
while($true)
|
||||||
"channel": "dev",
|
{
|
||||||
"toolsSource": "https://aspnetcore.blob.core.windows.net/buildtools"
|
try
|
||||||
|
{
|
||||||
|
Invoke-WebRequest $url -OutFile $downloadLocation
|
||||||
|
break
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
$exceptionMessage = $_.Exception.Message
|
||||||
|
Write-Host "Failed to download '$url': $exceptionMessage"
|
||||||
|
if ($retries -gt 0) {
|
||||||
|
$retries--
|
||||||
|
Write-Host "Waiting 10 seconds before retrying. Retries left: $retries"
|
||||||
|
Start-Sleep -Seconds 10
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$exception = $_.Exception
|
||||||
|
throw $exception
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
```
|
|
||||||
#>
|
|
||||||
[CmdletBinding(PositionalBinding = $false)]
|
|
||||||
param(
|
|
||||||
[string]$Path = $PSScriptRoot,
|
|
||||||
[Alias('c')]
|
|
||||||
[string]$Channel,
|
|
||||||
[Alias('d')]
|
|
||||||
[string]$DotNetHome,
|
|
||||||
[Alias('s')]
|
|
||||||
[string]$ToolsSource,
|
|
||||||
[Alias('u')]
|
|
||||||
[switch]$Update,
|
|
||||||
[string]$ConfigFile = $null,
|
|
||||||
[Parameter(ValueFromRemainingArguments = $true)]
|
|
||||||
[string[]]$MSBuildArgs
|
|
||||||
)
|
|
||||||
|
|
||||||
Set-StrictMode -Version 2
|
cd $PSScriptRoot
|
||||||
$ErrorActionPreference = 'Stop'
|
|
||||||
|
|
||||||
#
|
$repoFolder = $PSScriptRoot
|
||||||
# Functions
|
$env:REPO_FOLDER = $repoFolder
|
||||||
#
|
|
||||||
|
|
||||||
function Get-KoreBuild {
|
$koreBuildZip="https://github.com/aspnet/KoreBuild/archive/dev.zip"
|
||||||
|
if ($env:KOREBUILD_ZIP)
|
||||||
|
{
|
||||||
|
$koreBuildZip=$env:KOREBUILD_ZIP
|
||||||
|
}
|
||||||
|
|
||||||
$lockFile = Join-Path $Path 'korebuild-lock.txt'
|
$buildFolder = ".build"
|
||||||
|
$buildFile="$buildFolder\KoreBuild.ps1"
|
||||||
|
|
||||||
if (!(Test-Path $lockFile) -or $Update) {
|
if (!(Test-Path $buildFolder)) {
|
||||||
Get-RemoteFile "$ToolsSource/korebuild/channels/$Channel/latest.txt" $lockFile
|
Write-Host "Downloading KoreBuild from $koreBuildZip"
|
||||||
}
|
|
||||||
|
|
||||||
$version = Get-Content $lockFile | Where-Object { $_ -like 'version:*' } | Select-Object -first 1
|
$tempFolder=$env:TEMP + "\KoreBuild-" + [guid]::NewGuid()
|
||||||
if (!$version) {
|
New-Item -Path "$tempFolder" -Type directory | Out-Null
|
||||||
Write-Error "Failed to parse version from $lockFile. Expected a line that begins with 'version:'"
|
|
||||||
}
|
|
||||||
$version = $version.TrimStart('version:').Trim()
|
|
||||||
$korebuildPath = Join-Paths $DotNetHome ('buildtools', 'korebuild', $version)
|
|
||||||
|
|
||||||
if (!(Test-Path $korebuildPath)) {
|
$localZipFile="$tempFolder\korebuild.zip"
|
||||||
Write-Host -ForegroundColor Magenta "Downloading KoreBuild $version"
|
|
||||||
New-Item -ItemType Directory -Path $korebuildPath | Out-Null
|
DownloadWithRetry -url $koreBuildZip -downloadLocation $localZipFile -retries 6
|
||||||
$remotePath = "$ToolsSource/korebuild/artifacts/$version/korebuild.$version.zip"
|
|
||||||
|
|
||||||
try {
|
|
||||||
$tmpfile = Join-Path ([IO.Path]::GetTempPath()) "KoreBuild-$([guid]::NewGuid()).zip"
|
|
||||||
Get-RemoteFile $remotePath $tmpfile
|
|
||||||
if (Get-Command -Name 'Expand-Archive' -ErrorAction Ignore) {
|
|
||||||
# Use built-in commands where possible as they are cross-plat compatible
|
|
||||||
Expand-Archive -Path $tmpfile -DestinationPath $korebuildPath
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
# Fallback to old approach for old installations of PowerShell
|
|
||||||
Add-Type -AssemblyName System.IO.Compression.FileSystem
|
Add-Type -AssemblyName System.IO.Compression.FileSystem
|
||||||
[System.IO.Compression.ZipFile]::ExtractToDirectory($tmpfile, $korebuildPath)
|
[System.IO.Compression.ZipFile]::ExtractToDirectory($localZipFile, $tempFolder)
|
||||||
}
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
Remove-Item -Recurse -Force $korebuildPath -ErrorAction Ignore
|
|
||||||
throw
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
Remove-Item $tmpfile -ErrorAction Ignore
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $korebuildPath
|
New-Item -Path "$buildFolder" -Type directory | Out-Null
|
||||||
}
|
copy-item "$tempFolder\**\build\*" $buildFolder -Recurse
|
||||||
|
|
||||||
function Join-Paths([string]$path, [string[]]$childPaths) {
|
# Cleanup
|
||||||
$childPaths | ForEach-Object { $path = Join-Path $path $_ }
|
if (Test-Path $tempFolder) {
|
||||||
return $path
|
Remove-Item -Recurse -Force $tempFolder
|
||||||
}
|
|
||||||
|
|
||||||
function Get-RemoteFile([string]$RemotePath, [string]$LocalPath) {
|
|
||||||
if ($RemotePath -notlike 'http*') {
|
|
||||||
Copy-Item $RemotePath $LocalPath
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
$retries = 10
|
|
||||||
while ($retries -gt 0) {
|
|
||||||
$retries -= 1
|
|
||||||
try {
|
|
||||||
Invoke-WebRequest -UseBasicParsing -Uri $RemotePath -OutFile $LocalPath
|
|
||||||
return
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
Write-Verbose "Request failed. $retries retries remaining"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Error "Download failed: '$RemotePath'."
|
|
||||||
}
|
|
||||||
|
|
||||||
#
|
|
||||||
# Main
|
|
||||||
#
|
|
||||||
|
|
||||||
# Load configuration or set defaults
|
|
||||||
|
|
||||||
$Path = Resolve-Path $Path
|
|
||||||
if (!$ConfigFile) { $ConfigFile = Join-Path $Path 'korebuild.json' }
|
|
||||||
|
|
||||||
if (Test-Path $ConfigFile) {
|
|
||||||
try {
|
|
||||||
$config = Get-Content -Raw -Encoding UTF8 -Path $ConfigFile | ConvertFrom-Json
|
|
||||||
if ($config) {
|
|
||||||
if (!($Channel) -and (Get-Member -Name 'channel' -InputObject $config)) { [string] $Channel = $config.channel }
|
|
||||||
if (!($ToolsSource) -and (Get-Member -Name 'toolsSource' -InputObject $config)) { [string] $ToolsSource = $config.toolsSource}
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
Write-Warning "$ConfigFile could not be read. Its settings will be ignored."
|
|
||||||
Write-Warning $Error[0]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$DotNetHome) {
|
&"$buildFile" @args
|
||||||
$DotNetHome = if ($env:DOTNET_HOME) { $env:DOTNET_HOME } `
|
|
||||||
elseif ($env:USERPROFILE) { Join-Path $env:USERPROFILE '.dotnet'} `
|
|
||||||
elseif ($env:HOME) {Join-Path $env:HOME '.dotnet'}`
|
|
||||||
else { Join-Path $PSScriptRoot '.dotnet'}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$Channel) { $Channel = 'dev' }
|
|
||||||
if (!$ToolsSource) { $ToolsSource = 'https://aspnetcore.blob.core.windows.net/buildtools' }
|
|
||||||
|
|
||||||
# Execute
|
|
||||||
|
|
||||||
$korebuildPath = Get-KoreBuild
|
|
||||||
Import-Module -Force -Scope Local (Join-Path $korebuildPath 'KoreBuild.psd1')
|
|
||||||
|
|
||||||
try {
|
|
||||||
Install-Tools $ToolsSource $DotNetHome
|
|
||||||
Invoke-RepositoryBuild $Path @MSBuildArgs
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
Remove-Module 'KoreBuild' -ErrorAction Ignore
|
|
||||||
}
|
|
||||||
|
|||||||
252
build.sh
252
build.sh
@@ -1,220 +1,46 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
repoFolder="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||||
|
cd $repoFolder
|
||||||
|
|
||||||
set -euo pipefail
|
koreBuildZip="https://github.com/aspnet/KoreBuild/archive/dev.zip"
|
||||||
|
if [ ! -z $KOREBUILD_ZIP ]; then
|
||||||
#
|
koreBuildZip=$KOREBUILD_ZIP
|
||||||
# variables
|
|
||||||
#
|
|
||||||
|
|
||||||
RESET="\033[0m"
|
|
||||||
RED="\033[0;31m"
|
|
||||||
YELLOW="\033[0;33m"
|
|
||||||
MAGENTA="\033[0;95m"
|
|
||||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
|
||||||
[ -z "${DOTNET_HOME:-}" ] && DOTNET_HOME="$HOME/.dotnet"
|
|
||||||
config_file="$DIR/korebuild.json"
|
|
||||||
verbose=false
|
|
||||||
update=false
|
|
||||||
repo_path="$DIR"
|
|
||||||
channel=''
|
|
||||||
tools_source=''
|
|
||||||
|
|
||||||
#
|
|
||||||
# Functions
|
|
||||||
#
|
|
||||||
__usage() {
|
|
||||||
echo "Usage: $(basename "${BASH_SOURCE[0]}") [options] [[--] <MSBUILD_ARG>...]"
|
|
||||||
echo ""
|
|
||||||
echo "Arguments:"
|
|
||||||
echo " <MSBUILD_ARG>... Arguments passed to MSBuild. Variable number of arguments allowed."
|
|
||||||
echo ""
|
|
||||||
echo "Options:"
|
|
||||||
echo " --verbose Show verbose output."
|
|
||||||
echo " -c|--channel <CHANNEL> The channel of KoreBuild to download. Overrides the value from the config file.."
|
|
||||||
echo " --config-file <FILE> The path to the configuration file that stores values. Defaults to korebuild.json."
|
|
||||||
echo " -d|--dotnet-home <DIR> The directory where .NET Core tools will be stored. Defaults to '\$DOTNET_HOME' or '\$HOME/.dotnet."
|
|
||||||
echo " --path <PATH> The directory to build. Defaults to the directory containing the script."
|
|
||||||
echo " -s|--tools-source <URL> The base url where build tools can be downloaded. Overrides the value from the config file."
|
|
||||||
echo " -u|--update Update to the latest KoreBuild even if the lock file is present."
|
|
||||||
echo ""
|
|
||||||
echo "Description:"
|
|
||||||
echo " This function will create a file \$DIR/korebuild-lock.txt. This lock file can be committed to source, but does not have to be."
|
|
||||||
echo " When the lockfile is not present, KoreBuild will create one using latest available version from \$channel."
|
|
||||||
|
|
||||||
if [[ "${1:-}" != '--no-exit' ]]; then
|
|
||||||
exit 2
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
get_korebuild() {
|
|
||||||
local version
|
|
||||||
local lock_file="$repo_path/korebuild-lock.txt"
|
|
||||||
if [ ! -f "$lock_file" ] || [ "$update" = true ]; then
|
|
||||||
__get_remote_file "$tools_source/korebuild/channels/$channel/latest.txt" "$lock_file"
|
|
||||||
fi
|
|
||||||
version="$(grep 'version:*' -m 1 "$lock_file")"
|
|
||||||
if [[ "$version" == '' ]]; then
|
|
||||||
__error "Failed to parse version from $lock_file. Expected a line that begins with 'version:'"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
version="$(echo "${version#version:}" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')"
|
|
||||||
local korebuild_path="$DOTNET_HOME/buildtools/korebuild/$version"
|
|
||||||
|
|
||||||
{
|
|
||||||
if [ ! -d "$korebuild_path" ]; then
|
|
||||||
mkdir -p "$korebuild_path"
|
|
||||||
local remote_path="$tools_source/korebuild/artifacts/$version/korebuild.$version.zip"
|
|
||||||
tmpfile="$(mktemp)"
|
|
||||||
echo -e "${MAGENTA}Downloading KoreBuild ${version}${RESET}"
|
|
||||||
if __get_remote_file "$remote_path" "$tmpfile"; then
|
|
||||||
unzip -q -d "$korebuild_path" "$tmpfile"
|
|
||||||
fi
|
|
||||||
rm "$tmpfile" || true
|
|
||||||
fi
|
|
||||||
|
|
||||||
source "$korebuild_path/KoreBuild.sh"
|
|
||||||
} || {
|
|
||||||
if [ -d "$korebuild_path" ]; then
|
|
||||||
echo "Cleaning up after failed installation"
|
|
||||||
rm -rf "$korebuild_path" || true
|
|
||||||
fi
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
__error() {
|
|
||||||
echo -e "${RED}error: $*${RESET}" 1>&2
|
|
||||||
}
|
|
||||||
|
|
||||||
__warn() {
|
|
||||||
echo -e "${YELLOW}warning: $*${RESET}"
|
|
||||||
}
|
|
||||||
|
|
||||||
__machine_has() {
|
|
||||||
hash "$1" > /dev/null 2>&1
|
|
||||||
return $?
|
|
||||||
}
|
|
||||||
|
|
||||||
__get_remote_file() {
|
|
||||||
local remote_path=$1
|
|
||||||
local local_path=$2
|
|
||||||
|
|
||||||
if [[ "$remote_path" != 'http'* ]]; then
|
|
||||||
cp "$remote_path" "$local_path"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
local failed=false
|
|
||||||
if __machine_has wget; then
|
|
||||||
wget --tries 10 --quiet -O "$local_path" "$remote_path" || failed=true
|
|
||||||
else
|
|
||||||
failed=true
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$failed" = true ] && __machine_has curl; then
|
|
||||||
failed=false
|
|
||||||
curl --retry 10 -sSL -f --create-dirs -o "$local_path" "$remote_path" || failed=true
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$failed" = true ]; then
|
|
||||||
__error "Download failed: $remote_path" 1>&2
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
#
|
|
||||||
# main
|
|
||||||
#
|
|
||||||
|
|
||||||
while [[ $# -gt 0 ]]; do
|
|
||||||
case $1 in
|
|
||||||
-\?|-h|--help)
|
|
||||||
__usage --no-exit
|
|
||||||
exit 0
|
|
||||||
;;
|
|
||||||
-c|--channel|-Channel)
|
|
||||||
shift
|
|
||||||
channel="${1:-}"
|
|
||||||
[ -z "$channel" ] && __usage
|
|
||||||
;;
|
|
||||||
--config-file|-ConfigFile)
|
|
||||||
shift
|
|
||||||
config_file="${1:-}"
|
|
||||||
[ -z "$config_file" ] && __usage
|
|
||||||
if [ ! -f "$config_file" ]; then
|
|
||||||
__error "Invalid value for --config-file. $config_file does not exist."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
-d|--dotnet-home|-DotNetHome)
|
|
||||||
shift
|
|
||||||
DOTNET_HOME="${1:-}"
|
|
||||||
[ -z "$DOTNET_HOME" ] && __usage
|
|
||||||
;;
|
|
||||||
--path|-Path)
|
|
||||||
shift
|
|
||||||
repo_path="${1:-}"
|
|
||||||
[ -z "$repo_path" ] && __usage
|
|
||||||
;;
|
|
||||||
-s|--tools-source|-ToolsSource)
|
|
||||||
shift
|
|
||||||
tools_source="${1:-}"
|
|
||||||
[ -z "$tools_source" ] && __usage
|
|
||||||
;;
|
|
||||||
-u|--update|-Update)
|
|
||||||
update=true
|
|
||||||
;;
|
|
||||||
--verbose|-Verbose)
|
|
||||||
verbose=true
|
|
||||||
;;
|
|
||||||
--)
|
|
||||||
shift
|
|
||||||
break
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
break
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
shift
|
|
||||||
done
|
|
||||||
|
|
||||||
if ! __machine_has unzip; then
|
|
||||||
__error 'Missing required command: unzip'
|
|
||||||
exit 1
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! __machine_has curl && ! __machine_has wget; then
|
buildFolder=".build"
|
||||||
__error 'Missing required command. Either wget or curl is required.'
|
buildFile="$buildFolder/KoreBuild.sh"
|
||||||
|
|
||||||
|
if test ! -d $buildFolder; then
|
||||||
|
echo "Downloading KoreBuild from $koreBuildZip"
|
||||||
|
|
||||||
|
tempFolder="/tmp/KoreBuild-$(uuidgen)"
|
||||||
|
mkdir $tempFolder
|
||||||
|
|
||||||
|
localZipFile="$tempFolder/korebuild.zip"
|
||||||
|
|
||||||
|
retries=6
|
||||||
|
until (wget -O $localZipFile $koreBuildZip 2>/dev/null || curl -o $localZipFile --location $koreBuildZip 2>/dev/null)
|
||||||
|
do
|
||||||
|
echo "Failed to download '$koreBuildZip'"
|
||||||
|
if [ "$retries" -le 0 ]; then
|
||||||
exit 1
|
exit 1
|
||||||
|
fi
|
||||||
|
retries=$((retries - 1))
|
||||||
|
echo "Waiting 10 seconds before retrying. Retries left: $retries"
|
||||||
|
sleep 10s
|
||||||
|
done
|
||||||
|
|
||||||
|
unzip -q -d $tempFolder $localZipFile
|
||||||
|
|
||||||
|
mkdir $buildFolder
|
||||||
|
cp -r $tempFolder/**/build/** $buildFolder
|
||||||
|
|
||||||
|
chmod +x $buildFile
|
||||||
|
|
||||||
|
# Cleanup
|
||||||
|
if test -d $tempFolder; then
|
||||||
|
rm -rf $tempFolder
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
[ -z "${config_file:-}" ] && config_file="$repo_path/korebuild.json"
|
$buildFile -r $repoFolder "$@"
|
||||||
if [ -f "$config_file" ]; then
|
|
||||||
if __machine_has jq ; then
|
|
||||||
if jq '.' "$config_file" >/dev/null ; then
|
|
||||||
config_channel="$(jq -r 'select(.channel!=null) | .channel' "$config_file")"
|
|
||||||
config_tools_source="$(jq -r 'select(.toolsSource!=null) | .toolsSource' "$config_file")"
|
|
||||||
else
|
|
||||||
__warn "$config_file is invalid JSON. Its settings will be ignored."
|
|
||||||
fi
|
|
||||||
elif __machine_has python ; then
|
|
||||||
if python -c "import json,codecs;obj=json.load(codecs.open('$config_file', 'r', 'utf-8-sig'))" >/dev/null ; then
|
|
||||||
config_channel="$(python -c "import json,codecs;obj=json.load(codecs.open('$config_file', 'r', 'utf-8-sig'));print(obj['channel'] if 'channel' in obj else '')")"
|
|
||||||
config_tools_source="$(python -c "import json,codecs;obj=json.load(codecs.open('$config_file', 'r', 'utf-8-sig'));print(obj['toolsSource'] if 'toolsSource' in obj else '')")"
|
|
||||||
else
|
|
||||||
__warn "$config_file is invalid JSON. Its settings will be ignored."
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
__warn 'Missing required command: jq or pyton. Could not parse the JSON file. Its settings will be ignored.'
|
|
||||||
fi
|
|
||||||
|
|
||||||
[ ! -z "${config_channel:-}" ] && channel="$config_channel"
|
|
||||||
[ ! -z "${config_tools_source:-}" ] && tools_source="$config_tools_source"
|
|
||||||
fi
|
|
||||||
|
|
||||||
[ -z "$channel" ] && channel='dev'
|
|
||||||
[ -z "$tools_source" ] && tools_source='https://aspnetcore.blob.core.windows.net/buildtools'
|
|
||||||
|
|
||||||
get_korebuild
|
|
||||||
install_tools "$tools_source" "$DOTNET_HOME"
|
|
||||||
invoke_repository_build "$repo_path" "$@"
|
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
<Project>
|
<Project>
|
||||||
|
<Import Project="dependencies.props" />
|
||||||
|
<Import Project="..\version.props" />
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Product>Microsoft ASP.NET Core</Product>
|
<Product>Microsoft ASP.NET Core</Product>
|
||||||
@@ -7,11 +9,14 @@
|
|||||||
<AssemblyOriginatorKeyFile>$(MSBuildThisFileDirectory)Key.snk</AssemblyOriginatorKeyFile>
|
<AssemblyOriginatorKeyFile>$(MSBuildThisFileDirectory)Key.snk</AssemblyOriginatorKeyFile>
|
||||||
<SignAssembly>true</SignAssembly>
|
<SignAssembly>true</SignAssembly>
|
||||||
<PublicSign Condition="'$(OS)' != 'Windows_NT'">true</PublicSign>
|
<PublicSign Condition="'$(OS)' != 'Windows_NT'">true</PublicSign>
|
||||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
<VersionSuffix Condition="'$(VersionSuffix)'!='' AND '$(BuildNumber)' != ''">$(VersionSuffix)-$(BuildNumber)</VersionSuffix>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Internal.AspNetCore.Sdk" Version="$(InternalAspNetCoreSdkPackageVersion)" PrivateAssets="All" />
|
<PackageReference Include="Internal.AspNetCore.Sdk" Version="$(InternalAspNetCoreSdkVersion)" PrivateAssets="All" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup Condition="'$(TargetFrameworkIdentifier)'=='.NETFramework' AND '$(OutputType)'=='library'">
|
||||||
|
<PackageReference Include="NETStandard.Library" Version="$(BundledNETStandardPackageVersion)" />
|
||||||
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -1,23 +1,11 @@
|
|||||||
<Project>
|
<Project>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<InternalAspNetCoreSdkPackageVersion>2.0.2-rc1-15526</InternalAspNetCoreSdkPackageVersion>
|
<AspNetCoreVersion>2.0.0-*</AspNetCoreVersion>
|
||||||
<MicrosoftAspNetCoreDiagnosticsPackageVersion>2.0.1-rtm-105</MicrosoftAspNetCoreDiagnosticsPackageVersion>
|
<AutoMapperVersion>5.0.2</AutoMapperVersion>
|
||||||
<MicrosoftAspNetCoreHostingAbstractionsPackageVersion>2.0.1-rtm-105</MicrosoftAspNetCoreHostingAbstractionsPackageVersion>
|
<CoreFxVersion>4.3.0</CoreFxVersion>
|
||||||
<MicrosoftAspNetCoreHostingPackageVersion>2.0.1-rtm-105</MicrosoftAspNetCoreHostingPackageVersion>
|
<InternalAspNetCoreSdkVersion>2.1.0-*</InternalAspNetCoreSdkVersion>
|
||||||
<MicrosoftAspNetCoreMvcPackageVersion>2.0.1-rtm-105</MicrosoftAspNetCoreMvcPackageVersion>
|
<JsonNetVersion>10.0.1</JsonNetVersion>
|
||||||
<MicrosoftAspNetCoreMvcTagHelpersPackageVersion>2.0.1-rtm-105</MicrosoftAspNetCoreMvcTagHelpersPackageVersion>
|
<NETStandardImplicitPackageVersion>$(BundledNETStandardPackageVersion)</NETStandardImplicitPackageVersion>
|
||||||
<MicrosoftAspNetCoreMvcViewFeaturesPackageVersion>2.0.1-rtm-105</MicrosoftAspNetCoreMvcViewFeaturesPackageVersion>
|
<ThreadingDataflowVersion>4.7.0</ThreadingDataflowVersion>
|
||||||
<MicrosoftAspNetCoreServerIISIntegrationPackageVersion>2.0.1-rtm-105</MicrosoftAspNetCoreServerIISIntegrationPackageVersion>
|
|
||||||
<MicrosoftAspNetCoreServerKestrelPackageVersion>2.0.1-rtm-105</MicrosoftAspNetCoreServerKestrelPackageVersion>
|
|
||||||
<MicrosoftAspNetCoreStaticFilesPackageVersion>2.0.1-rtm-105</MicrosoftAspNetCoreStaticFilesPackageVersion>
|
|
||||||
<MicrosoftExtensionsDependencyInjectionPackageVersion>2.0.0</MicrosoftExtensionsDependencyInjectionPackageVersion>
|
|
||||||
<MicrosoftExtensionsLoggingConsolePackageVersion>2.0.0</MicrosoftExtensionsLoggingConsolePackageVersion>
|
|
||||||
<MicrosoftExtensionsLoggingDebugPackageVersion>2.0.0</MicrosoftExtensionsLoggingDebugPackageVersion>
|
|
||||||
<NewtonsoftJsonRuntimePackageVersion>10.0.1</NewtonsoftJsonRuntimePackageVersion>
|
|
||||||
<MicrosoftNETCoreApp20PackageVersion>2.0.5</MicrosoftNETCoreApp20PackageVersion>
|
|
||||||
<NETStandardLibrary20PackageVersion>2.0.1</NETStandardLibrary20PackageVersion>
|
|
||||||
<SystemThreadingTasksDataflowPackageVersion>4.8.0</SystemThreadingTasksDataflowPackageVersion>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<Import Project="$(DotNetPackageVersionPropsPath)" Condition=" '$(DotNetPackageVersionPropsPath)' != '' " />
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
12
build/repo.targets
Normal file
12
build/repo.targets
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<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>
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
<Project>
|
|
||||||
<Import Project="$(DotNetRestoreSourcePropsPath)" Condition="'$(DotNetRestoreSourcePropsPath)' != ''"/>
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<RestoreSources>$(DotNetRestoreSources)</RestoreSources>
|
|
||||||
<RestoreSources Condition="'$(DotNetBuildOffline)' != 'true' AND '$(AspNetUniverseBuildOffline)' != 'true' ">
|
|
||||||
$(RestoreSources);
|
|
||||||
https://dotnet.myget.org/F/aspnet-2-0-2-october2017-patch/api/v3/index.json;
|
|
||||||
https://dotnet.myget.org/F/aspnetcore-master/api/v3/index.json;
|
|
||||||
https://dotnet.myget.org/F/aspnetcore-tools/api/v3/index.json;
|
|
||||||
</RestoreSources>
|
|
||||||
<RestoreSources Condition="'$(DotNetBuildOffline)' != 'true'">
|
|
||||||
$(RestoreSources);
|
|
||||||
https://api.nuget.org/v3/index.json;
|
|
||||||
</RestoreSources>
|
|
||||||
</PropertyGroup>
|
|
||||||
</Project>
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
version:2.0.5-rtm-10016
|
|
||||||
commithash:02bda79ac9c564229da734a836f258d6c1321eb7
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "https://raw.githubusercontent.com/aspnet/BuildTools/release/2.0/tools/korebuild.schema.json",
|
|
||||||
"channel": "release/2.0"
|
|
||||||
}
|
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<Import Project="..\..\..\build\common.props" />
|
<Import Project="..\..\..\build\dependencies.props" />
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>netcoreapp2.0;net461</TargetFrameworks>
|
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||||
<IsPackable>false</IsPackable>
|
<IsPackable>false</IsPackable>
|
||||||
<OutputType>exe</OutputType>
|
<OutputType>exe</OutputType>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="$(MicrosoftExtensionsDependencyInjectionPackageVersion)" />
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="$(AspNetCoreVersion)" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.AspNetCore.NodeServices;
|
using Microsoft.AspNetCore.NodeServices;
|
||||||
using Microsoft.AspNetCore.SpaServices.Prerendering;
|
|
||||||
|
|
||||||
namespace NodeServicesExamples.Controllers
|
namespace NodeServicesExamples.Controllers
|
||||||
{
|
{
|
||||||
@@ -35,20 +34,6 @@ namespace NodeServicesExamples.Controllers
|
|||||||
return View();
|
return View();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IActionResult> Prerendering([FromServices] ISpaPrerenderer prerenderer)
|
|
||||||
{
|
|
||||||
var result = await prerenderer.RenderToString("./Node/prerenderPage");
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(result.RedirectUrl))
|
|
||||||
{
|
|
||||||
return Redirect(result.RedirectUrl);
|
|
||||||
}
|
|
||||||
|
|
||||||
ViewData["PrerenderedHtml"] = result.Html;
|
|
||||||
ViewData["PrerenderedGlobals"] = result.CreateGlobalsAssignmentScript();
|
|
||||||
return View();
|
|
||||||
}
|
|
||||||
|
|
||||||
public IActionResult Error()
|
public IActionResult Error()
|
||||||
{
|
{
|
||||||
return View("~/Views/Shared/Error.cshtml");
|
return View("~/Views/Shared/Error.cshtml");
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
var createServerRenderer = require('aspnet-prerendering').createServerRenderer;
|
|
||||||
|
|
||||||
module.exports = createServerRenderer(function(params) {
|
|
||||||
return new Promise(function (resolve, reject) {
|
|
||||||
var message = 'The HTML was returned by the prerendering boot function. '
|
|
||||||
+ 'The boot function received the following params:'
|
|
||||||
+ '<pre>' + JSON.stringify(params, null, 4) + '</pre>';
|
|
||||||
|
|
||||||
resolve({
|
|
||||||
html: '<h3>Hello, world!</h3>' + message,
|
|
||||||
globals: { sampleData: { nodeVersion: process.version } }
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,25 +1,25 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
<Import Project="..\..\..\build\common.props" />
|
<Import Project="..\..\..\build\dependencies.props" />
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>netcoreapp2.0;net461</TargetFrameworks>
|
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||||
<TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
|
<TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
|
||||||
<IsPackable>false</IsPackable>
|
<IsPackable>false</IsPackable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\..\..\src\Microsoft.AspNetCore.SpaServices\Microsoft.AspNetCore.SpaServices.csproj" />
|
<ProjectReference Include="..\..\..\src\Microsoft.AspNetCore.NodeServices\Microsoft.AspNetCore.NodeServices.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Diagnostics" Version="$(MicrosoftAspNetCoreDiagnosticsPackageVersion)" />
|
<PackageReference Include="Microsoft.AspNetCore.Diagnostics" Version="$(AspNetCoreVersion)" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Hosting" Version="$(MicrosoftAspNetCoreHostingPackageVersion)" />
|
<PackageReference Include="Microsoft.AspNetCore.Hosting" Version="$(AspNetCoreVersion)" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Server.IISIntegration" Version="$(MicrosoftAspNetCoreServerIISIntegrationPackageVersion)" />
|
<PackageReference Include="Microsoft.AspNetCore.Server.IISIntegration" Version="$(AspNetCoreVersion)" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="$(MicrosoftAspNetCoreMvcPackageVersion)" />
|
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="$(AspNetCoreVersion)" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="$(MicrosoftAspNetCoreServerKestrelPackageVersion)" />
|
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="$(AspNetCoreVersion)" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="$(MicrosoftAspNetCoreStaticFilesPackageVersion)" />
|
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="$(AspNetCoreVersion)" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="$(MicrosoftExtensionsLoggingDebugPackageVersion)" />
|
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="$(AspNetCoreVersion)" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<Target Name="PrepublishScript" BeforeTargets="PrepareForPublish">
|
<Target Name="PrepublishScript" BeforeTargets="PrepareForPublish">
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ namespace NodeServicesExamples
|
|||||||
|
|
||||||
// Enable Node Services
|
// Enable Node Services
|
||||||
services.AddNodeServices();
|
services.AddNodeServices();
|
||||||
services.AddSpaPrerenderer();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||||
|
|||||||
@@ -9,5 +9,4 @@
|
|||||||
<ul>
|
<ul>
|
||||||
<li><a asp-action="ES2015Transpilation">ES2015 transpilation</a></li>
|
<li><a asp-action="ES2015Transpilation">ES2015 transpilation</a></li>
|
||||||
<li><a asp-action="Chart">Server-side chart rendering</a></li>
|
<li><a asp-action="Chart">Server-side chart rendering</a></li>
|
||||||
<li><a asp-action="Prerendering">Server-side SPA prerendering</a></li>
|
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
@@ -1,21 +0,0 @@
|
|||||||
<h1>Server-side prerendering</h1>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
This sample demonstrates how you can invoke a JavaScript module that contains
|
|
||||||
prerendering logic for a Single-Page Application framework.
|
|
||||||
</p>
|
|
||||||
</p>
|
|
||||||
Your prerendering boot function will receive parameters that describe the page
|
|
||||||
being rendered and any data supplied by the .NET code. The return value should be
|
|
||||||
a promise that resolves with data to be injected into the page, such as the
|
|
||||||
rendered HTML and any global data that should be made available to client-side code.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
@Html.Raw(ViewData["PrerenderedHtml"])
|
|
||||||
|
|
||||||
<script>@Html.Raw(ViewData["PrerenderedGlobals"])</script>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
// Demonstrates how client-side code can receive data from the prerendering process
|
|
||||||
console.log('Received Node version from prerendering logic: ' + sampleData.nodeVersion);
|
|
||||||
</script>
|
|
||||||
@@ -2,7 +2,6 @@
|
|||||||
"name": "nodeservicesexamples",
|
"name": "nodeservicesexamples",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"aspnet-prerendering": "^2.0.6",
|
|
||||||
"babel-core": "^6.7.4",
|
"babel-core": "^6.7.4",
|
||||||
"babel-preset-es2015": "^6.6.0",
|
"babel-preset-es2015": "^6.6.0",
|
||||||
"node-chartist": "^1.0.2"
|
"node-chartist": "^1.0.2"
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ 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;
|
||||||
@@ -35,7 +34,6 @@ 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,
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
<Import Project="..\..\..\build\common.props" />
|
<Import Project="..\..\..\build\dependencies.props" />
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>netcoreapp2.0;net461</TargetFrameworks>
|
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||||
<TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
|
<TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
|
||||||
<IsPackable>false</IsPackable>
|
<IsPackable>false</IsPackable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
@@ -13,13 +13,13 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Diagnostics" Version="$(MicrosoftAspNetCoreDiagnosticsPackageVersion)" />
|
<PackageReference Include="Microsoft.AspNetCore.Diagnostics" Version="$(AspNetCoreVersion)" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Hosting" Version="$(MicrosoftAspNetCoreHostingPackageVersion)" />
|
<PackageReference Include="Microsoft.AspNetCore.Hosting" Version="$(AspNetCoreVersion)" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Server.IISIntegration" Version="$(MicrosoftAspNetCoreServerIISIntegrationPackageVersion)" />
|
<PackageReference Include="Microsoft.AspNetCore.Server.IISIntegration" Version="$(AspNetCoreVersion)" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="$(MicrosoftAspNetCoreMvcPackageVersion)" />
|
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="$(AspNetCoreVersion)" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="$(MicrosoftAspNetCoreServerKestrelPackageVersion)" />
|
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="$(AspNetCoreVersion)" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="$(MicrosoftAspNetCoreStaticFilesPackageVersion)" />
|
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="$(AspNetCoreVersion)" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="$(MicrosoftExtensionsLoggingDebugPackageVersion)" />
|
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="$(AspNetCoreVersion)" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<Target Name="PrepublishScript" BeforeTargets="PrepareForPublish">
|
<Target Name="PrepublishScript" BeforeTargets="PrepareForPublish">
|
||||||
|
|||||||
@@ -4,19 +4,26 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Description>Socket-based RPC for Microsoft.AspNetCore.NodeServices.</Description>
|
<Description>Socket-based RPC for Microsoft.AspNetCore.NodeServices.</Description>
|
||||||
<TargetFramework>netstandard2.0</TargetFramework>
|
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||||
<PackageTags>aspnetcore;aspnetcoremvc;nodeservices</PackageTags>
|
<PackageTags>aspnetcore;aspnetcoremvc;nodeservices</PackageTags>
|
||||||
<TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
|
<TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Remove="node_modules\**\*" />
|
|
||||||
<EmbeddedResource Include="Content\**\*" />
|
<EmbeddedResource Include="Content\**\*" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Microsoft.AspNetCore.NodeServices\Microsoft.AspNetCore.NodeServices.csproj" />
|
<ProjectReference Include="..\Microsoft.AspNetCore.NodeServices\Microsoft.AspNetCore.NodeServices.csproj" />
|
||||||
<PackageReference Include="System.Threading.Tasks.Dataflow" Version="$(SystemThreadingTasksDataflowPackageVersion)" />
|
</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)" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<Target Name="PrepublishScript" BeforeTargets="PrepareForPublish" Condition=" '$(IsCrossTargetingBuild)' != 'true' ">
|
<Target Name="PrepublishScript" BeforeTargets="PrepareForPublish" Condition=" '$(IsCrossTargetingBuild)' != 'true' ">
|
||||||
|
|||||||
@@ -18,7 +18,11 @@ 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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,9 @@ 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)
|
||||||
@@ -21,6 +24,7 @@ namespace Microsoft.AspNetCore.NodeServices.Sockets.PhysicalConnections
|
|||||||
{
|
{
|
||||||
return new UnixDomainSocketConnection();
|
return new UnixDomainSocketConnection();
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -10,6 +10,16 @@ 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);
|
||||||
@@ -18,6 +28,7 @@ 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()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -59,7 +59,11 @@ 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,7 +49,6 @@ 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,
|
||||||
|
|||||||
@@ -12,7 +12,11 @@ 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;
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
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;
|
||||||
@@ -43,12 +42,6 @@ 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
|
||||||
@@ -100,10 +93,5 @@ 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; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -55,11 +55,10 @@
|
|||||||
// Limit dependencies to core Node modules. This means the code in this file has to be very low-level and unattractive,
|
// Limit dependencies to core Node modules. This means the code in this file has to be very low-level and unattractive,
|
||||||
// but simplifies things for the consumer of this module.
|
// but simplifies things for the consumer of this module.
|
||||||
__webpack_require__(2);
|
__webpack_require__(2);
|
||||||
__webpack_require__(4);
|
var http = __webpack_require__(3);
|
||||||
var http = __webpack_require__(5);
|
var path = __webpack_require__(4);
|
||||||
var path = __webpack_require__(3);
|
var ArgsUtil_1 = __webpack_require__(5);
|
||||||
var ArgsUtil_1 = __webpack_require__(6);
|
var ExitWhenParentExits_1 = __webpack_require__(6);
|
||||||
var ExitWhenParentExits_1 = __webpack_require__(7);
|
|
||||||
// Webpack doesn't support dynamic requires for files not present at compile time, so grab a direct
|
// Webpack doesn't support dynamic requires for files not present at compile time, so grab a direct
|
||||||
// reference to Node's runtime 'require' function.
|
// reference to Node's runtime 'require' function.
|
||||||
var dynamicRequire = eval('require');
|
var dynamicRequire = eval('require');
|
||||||
@@ -134,73 +133,12 @@
|
|||||||
}
|
}
|
||||||
function respondWithError(res, errorValue) {
|
function respondWithError(res, errorValue) {
|
||||||
res.statusCode = 500;
|
res.statusCode = 500;
|
||||||
res.end(JSON.stringify({
|
res.end(errorValue.stack || errorValue.toString());
|
||||||
errorMessage: errorValue.message || errorValue,
|
|
||||||
errorDetails: errorValue.stack || null
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/***/ },
|
/***/ },
|
||||||
/* 2 */
|
/* 2 */
|
||||||
/***/ function(module, exports, __webpack_require__) {
|
|
||||||
|
|
||||||
"use strict";
|
|
||||||
var path = __webpack_require__(3);
|
|
||||||
var startsWith = function (str, prefix) { return str.substring(0, prefix.length) === prefix; };
|
|
||||||
var appRootDir = process.cwd();
|
|
||||||
function patchedLStat(pathToStatLong, fsReqWrap) {
|
|
||||||
try {
|
|
||||||
// If the lstat completes without errors, we don't modify its behavior at all
|
|
||||||
return origLStat.apply(this, arguments);
|
|
||||||
}
|
|
||||||
catch (ex) {
|
|
||||||
var shouldOverrideError = startsWith(ex.message, 'EPERM') // It's a permissions error
|
|
||||||
&& typeof appRootDirLong === 'string'
|
|
||||||
&& startsWith(appRootDirLong, pathToStatLong) // ... for an ancestor directory
|
|
||||||
&& ex.stack.indexOf('Object.realpathSync ') >= 0; // ... during symlink resolution
|
|
||||||
if (shouldOverrideError) {
|
|
||||||
// Fake the result to give the same result as an 'lstat' on the app root dir.
|
|
||||||
// This stops Node failing to load modules just because it doesn't know whether
|
|
||||||
// ancestor directories are symlinks or not. If there's a genuine file
|
|
||||||
// permissions issue, it will still surface later when Node actually
|
|
||||||
// tries to read the file.
|
|
||||||
return origLStat.call(this, appRootDir, fsReqWrap);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// In any other case, preserve the original error
|
|
||||||
throw ex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
;
|
|
||||||
// It's only necessary to apply this workaround on Windows
|
|
||||||
var appRootDirLong = null;
|
|
||||||
var origLStat = null;
|
|
||||||
if (/^win/.test(process.platform)) {
|
|
||||||
try {
|
|
||||||
// Get the app's root dir in Node's internal "long" format (e.g., \\?\C:\dir\subdir)
|
|
||||||
appRootDirLong = path._makeLong(appRootDir);
|
|
||||||
// Actually apply the patch, being as defensive as possible
|
|
||||||
var bindingFs = process.binding('fs');
|
|
||||||
origLStat = bindingFs.lstat;
|
|
||||||
if (typeof origLStat === 'function') {
|
|
||||||
bindingFs.lstat = patchedLStat;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (ex) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/***/ },
|
|
||||||
/* 3 */
|
|
||||||
/***/ function(module, exports) {
|
|
||||||
|
|
||||||
module.exports = require("path");
|
|
||||||
|
|
||||||
/***/ },
|
|
||||||
/* 4 */
|
|
||||||
/***/ function(module, exports) {
|
/***/ function(module, exports) {
|
||||||
|
|
||||||
// When Node writes to stdout/strerr, we capture that and convert the lines into calls on the
|
// When Node writes to stdout/strerr, we capture that and convert the lines into calls on the
|
||||||
@@ -241,13 +179,19 @@
|
|||||||
|
|
||||||
|
|
||||||
/***/ },
|
/***/ },
|
||||||
/* 5 */
|
/* 3 */
|
||||||
/***/ function(module, exports) {
|
/***/ function(module, exports) {
|
||||||
|
|
||||||
module.exports = require("http");
|
module.exports = require("http");
|
||||||
|
|
||||||
/***/ },
|
/***/ },
|
||||||
/* 6 */
|
/* 4 */
|
||||||
|
/***/ function(module, exports) {
|
||||||
|
|
||||||
|
module.exports = require("path");
|
||||||
|
|
||||||
|
/***/ },
|
||||||
|
/* 5 */
|
||||||
/***/ function(module, exports) {
|
/***/ function(module, exports) {
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
@@ -273,7 +217,7 @@
|
|||||||
|
|
||||||
|
|
||||||
/***/ },
|
/***/ },
|
||||||
/* 7 */
|
/* 6 */
|
||||||
/***/ function(module, exports) {
|
/***/ function(module, exports) {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -42,7 +42,6 @@ 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,
|
||||||
@@ -67,10 +66,8 @@ namespace Microsoft.AspNetCore.NodeServices.HostingModels
|
|||||||
if (!response.IsSuccessStatusCode)
|
if (!response.IsSuccessStatusCode)
|
||||||
{
|
{
|
||||||
// Unfortunately there's no true way to cancel ReadAsStringAsync calls, hence AbandonIfCancelled
|
// Unfortunately there's no true way to cancel ReadAsStringAsync calls, hence AbandonIfCancelled
|
||||||
var responseJson = await response.Content.ReadAsStringAsync().OrThrowOnCancellation(cancellationToken);
|
var responseErrorString = await response.Content.ReadAsStringAsync().OrThrowOnCancellation(cancellationToken);
|
||||||
var responseError = JsonConvert.DeserializeObject<RpcJsonResponse>(responseJson, jsonSerializerSettings);
|
throw new Exception("Call to Node module failed with error: " + responseErrorString);
|
||||||
|
|
||||||
throw new NodeInvocationException(responseError.ErrorMessage, responseError.ErrorDetails);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var responseContentType = response.Content.Headers.ContentType;
|
var responseContentType = response.Content.Headers.ContentType;
|
||||||
@@ -138,13 +135,5 @@ namespace Microsoft.AspNetCore.NodeServices.HostingModels
|
|||||||
_disposed = true;
|
_disposed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma warning disable 649 // These properties are populated via JSON deserialization
|
|
||||||
private class RpcJsonResponse
|
|
||||||
{
|
|
||||||
public string ErrorMessage { get; set; }
|
|
||||||
public string ErrorDetails { get; set; }
|
|
||||||
}
|
|
||||||
#pragma warning restore 649
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,7 +45,6 @@ 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>
|
||||||
@@ -56,7 +55,6 @@ 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,
|
||||||
@@ -69,7 +67,7 @@ namespace Microsoft.AspNetCore.NodeServices.HostingModels
|
|||||||
}
|
}
|
||||||
|
|
||||||
OutputLogger = nodeOutputLogger;
|
OutputLogger = nodeOutputLogger;
|
||||||
_entryPointScript = new StringAsTempFile(entryPointScript, applicationStoppingToken);
|
_entryPointScript = new StringAsTempFile(entryPointScript);
|
||||||
_invocationTimeoutMilliseconds = invocationTimeoutMilliseconds;
|
_invocationTimeoutMilliseconds = invocationTimeoutMilliseconds;
|
||||||
_launchWithDebugging = launchWithDebugging;
|
_launchWithDebugging = launchWithDebugging;
|
||||||
|
|
||||||
@@ -297,7 +295,7 @@ namespace Microsoft.AspNetCore.NodeServices.HostingModels
|
|||||||
|
|
||||||
// Make sure the Node process is finished
|
// Make sure the Node process is finished
|
||||||
// TODO: Is there a more graceful way to end it? Or does this still let it perform any cleanup?
|
// TODO: Is there a more graceful way to end it? Or does this still let it perform any cleanup?
|
||||||
if (_nodeProcess != null && !_nodeProcess.HasExited)
|
if (!_nodeProcess.HasExited)
|
||||||
{
|
{
|
||||||
_nodeProcess.Kill();
|
_nodeProcess.Kill();
|
||||||
}
|
}
|
||||||
@@ -317,7 +315,11 @@ 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)
|
||||||
@@ -381,6 +383,12 @@ 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));
|
||||||
@@ -394,11 +402,9 @@ namespace Microsoft.AspNetCore.NodeServices.HostingModels
|
|||||||
|
|
||||||
private static bool IsDebuggerMessage(string message)
|
private static bool IsDebuggerMessage(string message)
|
||||||
{
|
{
|
||||||
return message.StartsWith("Debugger attached", StringComparison.Ordinal) ||
|
return message.StartsWith("Debugger attached", StringComparison.OrdinalIgnoreCase) ||
|
||||||
message.StartsWith("Debugger listening ", StringComparison.Ordinal) ||
|
message.StartsWith("Debugger listening ", StringComparison.OrdinalIgnoreCase) ||
|
||||||
message.StartsWith("To start debugging", StringComparison.Ordinal) ||
|
message.StartsWith("To start debugging", StringComparison.OrdinalIgnoreCase) ||
|
||||||
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:");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,21 +4,25 @@
|
|||||||
|
|
||||||
<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>netstandard2.0</TargetFramework>
|
<TargetFramework>netcoreapp2.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>
|
||||||
<None Remove="node_modules\**\*" />
|
|
||||||
<EmbeddedResource Include="Content\**\*" />
|
<EmbeddedResource Include="Content\**\*" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Hosting.Abstractions" Version="$(MicrosoftAspNetCoreHostingAbstractionsPackageVersion)" />
|
<PackageReference Include="Microsoft.AspNetCore.Hosting.Abstractions" Version="$(AspNetCoreVersion)" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="$(MicrosoftExtensionsLoggingConsolePackageVersion)" />
|
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="$(AspNetCoreVersion)" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="$(NewtonsoftJsonRuntimePackageVersion)" />
|
<PackageReference Include="Newtonsoft.Json" Version="$(JsonNetVersion)" />
|
||||||
|
</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>
|
</ItemGroup>
|
||||||
|
|
||||||
<Target Name="PrepublishScript" BeforeTargets="PrepareForPublish" Condition=" '$(IsCrossTargetingBuild)' != 'true' ">
|
<Target Name="PrepublishScript" BeforeTargets="PrepareForPublish" Condition=" '$(IsCrossTargetingBuild)' != 'true' ">
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
// Limit dependencies to core Node modules. This means the code in this file has to be very low-level and unattractive,
|
// Limit dependencies to core Node modules. This means the code in this file has to be very low-level and unattractive,
|
||||||
// but simplifies things for the consumer of this module.
|
// but simplifies things for the consumer of this module.
|
||||||
import './Util/PatchModuleResolutionLStat';
|
|
||||||
import './Util/OverrideStdOutputs';
|
import './Util/OverrideStdOutputs';
|
||||||
import * as http from 'http';
|
import * as http from 'http';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
@@ -87,8 +86,5 @@ function readRequestBodyAsJson(request, callback) {
|
|||||||
|
|
||||||
function respondWithError(res: http.ServerResponse, errorValue: any) {
|
function respondWithError(res: http.ServerResponse, errorValue: any) {
|
||||||
res.statusCode = 500;
|
res.statusCode = 500;
|
||||||
res.end(JSON.stringify({
|
res.end(errorValue.stack || errorValue.toString());
|
||||||
errorMessage: errorValue.message || errorValue,
|
|
||||||
errorDetails: errorValue.stack || null
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,48 +0,0 @@
|
|||||||
import * as path from 'path';
|
|
||||||
const startsWith = (str: string, prefix: string) => str.substring(0, prefix.length) === prefix;
|
|
||||||
const appRootDir = process.cwd();
|
|
||||||
|
|
||||||
function patchedLStat(pathToStatLong: string, fsReqWrap?: any) {
|
|
||||||
try {
|
|
||||||
// If the lstat completes without errors, we don't modify its behavior at all
|
|
||||||
return origLStat.apply(this, arguments);
|
|
||||||
} catch(ex) {
|
|
||||||
const shouldOverrideError =
|
|
||||||
startsWith(ex.message, 'EPERM') // It's a permissions error
|
|
||||||
&& typeof appRootDirLong === 'string'
|
|
||||||
&& startsWith(appRootDirLong, pathToStatLong) // ... for an ancestor directory
|
|
||||||
&& ex.stack.indexOf('Object.realpathSync ') >= 0; // ... during symlink resolution
|
|
||||||
|
|
||||||
if (shouldOverrideError) {
|
|
||||||
// Fake the result to give the same result as an 'lstat' on the app root dir.
|
|
||||||
// This stops Node failing to load modules just because it doesn't know whether
|
|
||||||
// ancestor directories are symlinks or not. If there's a genuine file
|
|
||||||
// permissions issue, it will still surface later when Node actually
|
|
||||||
// tries to read the file.
|
|
||||||
return origLStat.call(this, appRootDir, fsReqWrap);
|
|
||||||
} else {
|
|
||||||
// In any other case, preserve the original error
|
|
||||||
throw ex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// It's only necessary to apply this workaround on Windows
|
|
||||||
let appRootDirLong: string = null;
|
|
||||||
let origLStat: Function = null;
|
|
||||||
if (/^win/.test(process.platform)) {
|
|
||||||
try {
|
|
||||||
// Get the app's root dir in Node's internal "long" format (e.g., \\?\C:\dir\subdir)
|
|
||||||
appRootDirLong = (path as any)._makeLong(appRootDir);
|
|
||||||
|
|
||||||
// Actually apply the patch, being as defensive as possible
|
|
||||||
const bindingFs = (process as any).binding('fs');
|
|
||||||
origLStat = bindingFs.lstat;
|
|
||||||
if (typeof origLStat === 'function') {
|
|
||||||
bindingFs.lstat = patchedLStat;
|
|
||||||
}
|
|
||||||
} catch(ex) {
|
|
||||||
// If some future version of Node throws (e.g., to prevent use of process.binding()),
|
|
||||||
// don't apply the patch, but still let the application run.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.NodeServices
|
namespace Microsoft.AspNetCore.NodeServices
|
||||||
{
|
{
|
||||||
@@ -12,21 +11,27 @@ 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>
|
||||||
/// <param name="applicationStoppingToken">A token that indicates when the host application is stopping.</param>
|
public StringAsTempFile(string content)
|
||||||
public StringAsTempFile(string content, CancellationToken applicationStoppingToken)
|
|
||||||
{
|
{
|
||||||
FileName = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
|
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.
|
||||||
_applicationLifetimeRegistration = applicationStoppingToken.Register(EnsureTempFileDeleted);
|
#if NET451
|
||||||
|
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>
|
||||||
@@ -50,7 +55,12 @@ namespace Microsoft.AspNetCore.NodeServices
|
|||||||
if (disposing)
|
if (disposing)
|
||||||
{
|
{
|
||||||
// Dispose managed state
|
// Dispose managed state
|
||||||
_applicationLifetimeRegistration.Dispose();
|
#if NET451
|
||||||
|
AppDomain.CurrentDomain.ProcessExit -= HandleProcessExit;
|
||||||
|
AppDomain.CurrentDomain.DomainUnload -= HandleProcessExit;
|
||||||
|
#else
|
||||||
|
System.Runtime.Loader.AssemblyLoadContext.Default.Unloading -= HandleAssemblyUnloading;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
EnsureTempFileDeleted();
|
EnsureTempFileDeleted();
|
||||||
@@ -71,6 +81,18 @@ 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>
|
||||||
|
|||||||
@@ -4,18 +4,15 @@
|
|||||||
|
|
||||||
<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>netstandard2.0</TargetFramework>
|
<TargetFramework>netcoreapp2.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>
|
||||||
<None Remove="node_modules\**\*" />
|
|
||||||
<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.TagHelpers" Version="$(MicrosoftAspNetCoreMvcTagHelpersPackageVersion)" />
|
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="$(AspNetCoreVersion)" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.ViewFeatures" Version="$(MicrosoftAspNetCoreMvcViewFeaturesPackageVersion)" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<Target Name="PrepublishScript" BeforeTargets="PrepareForPublish" Condition=" '$(IsCrossTargetingBuild)' != 'true' ">
|
<Target Name="PrepublishScript" BeforeTargets="PrepareForPublish" Condition=" '$(IsCrossTargetingBuild)' != 'true' ">
|
||||||
|
|||||||
@@ -1,49 +0,0 @@
|
|||||||
using System.Threading;
|
|
||||||
using Microsoft.AspNetCore.Hosting;
|
|
||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
using Microsoft.AspNetCore.NodeServices;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.SpaServices.Prerendering
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Default implementation of a DI service that provides convenient access to
|
|
||||||
/// server-side prerendering APIs. This is an alternative to prerendering via
|
|
||||||
/// the asp-prerender-module tag helper.
|
|
||||||
/// </summary>
|
|
||||||
internal class DefaultSpaPrerenderer : ISpaPrerenderer
|
|
||||||
{
|
|
||||||
private readonly string _applicationBasePath;
|
|
||||||
private readonly CancellationToken _applicationStoppingToken;
|
|
||||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
|
||||||
private readonly INodeServices _nodeServices;
|
|
||||||
|
|
||||||
public DefaultSpaPrerenderer(
|
|
||||||
INodeServices nodeServices,
|
|
||||||
IApplicationLifetime applicationLifetime,
|
|
||||||
IHostingEnvironment hostingEnvironment,
|
|
||||||
IHttpContextAccessor httpContextAccessor)
|
|
||||||
{
|
|
||||||
_applicationBasePath = hostingEnvironment.ContentRootPath;
|
|
||||||
_applicationStoppingToken = applicationLifetime.ApplicationStopping;
|
|
||||||
_httpContextAccessor = httpContextAccessor;
|
|
||||||
_nodeServices = nodeServices;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<RenderToStringResult> RenderToString(
|
|
||||||
string moduleName,
|
|
||||||
string exportName = null,
|
|
||||||
object customDataParameter = null,
|
|
||||||
int timeoutMilliseconds = default(int))
|
|
||||||
{
|
|
||||||
return Prerenderer.RenderToString(
|
|
||||||
_applicationBasePath,
|
|
||||||
_nodeServices,
|
|
||||||
_applicationStoppingToken,
|
|
||||||
new JavaScriptModuleExport(moduleName) { ExportName = exportName },
|
|
||||||
_httpContextAccessor.HttpContext,
|
|
||||||
customDataParameter,
|
|
||||||
timeoutMilliseconds);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.SpaServices.Prerendering
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Represents a service that can perform server-side prerendering for
|
|
||||||
/// JavaScript-based Single Page Applications. This is an alternative
|
|
||||||
/// to using the 'asp-prerender-module' tag helper.
|
|
||||||
/// </summary>
|
|
||||||
public interface ISpaPrerenderer
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Invokes JavaScript code to perform server-side prerendering for a
|
|
||||||
/// Single-Page Application. This is an alternative to using the
|
|
||||||
/// 'asp-prerender-module' tag helper.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="moduleName">The JavaScript module that exports a prerendering function.</param>
|
|
||||||
/// <param name="exportName">The name of the export from the JavaScript module, if it is not the default export.</param>
|
|
||||||
/// <param name="customDataParameter">An optional JSON-serializable object to pass to the JavaScript prerendering function.</param>
|
|
||||||
/// <param name="timeoutMilliseconds">If specified, the prerendering task will time out after this duration if not already completed.</param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<RenderToStringResult> RenderToString(
|
|
||||||
string moduleName,
|
|
||||||
string exportName = null,
|
|
||||||
object customDataParameter = null,
|
|
||||||
int timeoutMilliseconds = default(int));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +1,13 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Threading;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.AspNetCore.Http.Features;
|
||||||
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||||
using Microsoft.AspNetCore.NodeServices;
|
using Microsoft.AspNetCore.NodeServices;
|
||||||
using Microsoft.AspNetCore.Razor.TagHelpers;
|
using Microsoft.AspNetCore.Razor.TagHelpers;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.SpaServices.Prerendering
|
namespace Microsoft.AspNetCore.SpaServices.Prerendering
|
||||||
{
|
{
|
||||||
@@ -22,7 +24,6 @@ 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>
|
||||||
@@ -35,9 +36,6 @@ 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)
|
||||||
@@ -87,17 +85,31 @@ namespace Microsoft.AspNetCore.SpaServices.Prerendering
|
|||||||
/// <returns>A <see cref="Task"/> representing the operation.</returns>
|
/// <returns>A <see cref="Task"/> representing the operation.</returns>
|
||||||
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
|
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
|
||||||
{
|
{
|
||||||
|
// We want to pass the original, unencoded incoming URL data through to Node, so that
|
||||||
|
// server-side code has the same view of the URL as client-side code (on the client,
|
||||||
|
// location.pathname returns an unencoded string).
|
||||||
|
// The following logic handles special characters in URL paths in the same way that
|
||||||
|
// Node and client-side JS does. For example, the path "/a=b%20c" gets passed through
|
||||||
|
// unchanged (whereas other .NET APIs do change it - Path.Value will return it as
|
||||||
|
// "/a=b c" and Path.ToString() will return it as "/a%3db%20c")
|
||||||
|
var requestFeature = ViewContext.HttpContext.Features.Get<IHttpRequestFeature>();
|
||||||
|
var unencodedPathAndQuery = requestFeature.RawTarget;
|
||||||
|
|
||||||
|
var request = ViewContext.HttpContext.Request;
|
||||||
|
var unencodedAbsoluteUrl = $"{request.Scheme}://{request.Host}{unencodedPathAndQuery}";
|
||||||
|
|
||||||
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
|
||||||
},
|
},
|
||||||
ViewContext.HttpContext,
|
unencodedAbsoluteUrl,
|
||||||
|
unencodedPathAndQuery,
|
||||||
CustomDataParameter,
|
CustomDataParameter,
|
||||||
TimeoutMillisecondsParameter);
|
TimeoutMillisecondsParameter,
|
||||||
|
request.PathBase.ToString());
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(result.RedirectUrl))
|
if (!string.IsNullOrEmpty(result.RedirectUrl))
|
||||||
{
|
{
|
||||||
@@ -116,10 +128,19 @@ namespace Microsoft.AspNetCore.SpaServices.Prerendering
|
|||||||
|
|
||||||
// Also attach any specified globals to the 'window' object. This is useful for transferring
|
// Also attach any specified globals to the 'window' object. This is useful for transferring
|
||||||
// general state between server and client.
|
// general state between server and client.
|
||||||
var globalsScript = result.CreateGlobalsAssignmentScript();
|
if (result.Globals != null)
|
||||||
if (!string.IsNullOrEmpty(globalsScript))
|
|
||||||
{
|
{
|
||||||
output.PostElement.SetHtmlContent($"<script>{globalsScript}</script>");
|
var stringBuilder = new StringBuilder();
|
||||||
|
foreach (var property in result.Globals.Properties())
|
||||||
|
{
|
||||||
|
stringBuilder.AppendFormat("window.{0} = {1};",
|
||||||
|
property.Name,
|
||||||
|
property.Value.ToString(Formatting.None));
|
||||||
|
}
|
||||||
|
if (stringBuilder.Length > 0)
|
||||||
|
{
|
||||||
|
output.PostElement.SetHtmlContent($"<script>{stringBuilder}</script>");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.NodeServices;
|
using Microsoft.AspNetCore.NodeServices;
|
||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
using Microsoft.AspNetCore.Http.Features;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.SpaServices.Prerendering
|
namespace Microsoft.AspNetCore.SpaServices.Prerendering
|
||||||
{
|
{
|
||||||
@@ -12,42 +9,15 @@ namespace Microsoft.AspNetCore.SpaServices.Prerendering
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static class Prerenderer
|
public static class Prerenderer
|
||||||
{
|
{
|
||||||
private static readonly object CreateNodeScriptLock = new object();
|
private static readonly Lazy<StringAsTempFile> NodeScript;
|
||||||
|
|
||||||
private static StringAsTempFile NodeScript;
|
static Prerenderer()
|
||||||
|
|
||||||
internal static Task<RenderToStringResult> RenderToString(
|
|
||||||
string applicationBasePath,
|
|
||||||
INodeServices nodeServices,
|
|
||||||
CancellationToken applicationStoppingToken,
|
|
||||||
JavaScriptModuleExport bootModule,
|
|
||||||
HttpContext httpContext,
|
|
||||||
object customDataParameter,
|
|
||||||
int timeoutMilliseconds)
|
|
||||||
{
|
{
|
||||||
// We want to pass the original, unencoded incoming URL data through to Node, so that
|
NodeScript = new Lazy<StringAsTempFile>(() =>
|
||||||
// server-side code has the same view of the URL as client-side code (on the client,
|
{
|
||||||
// location.pathname returns an unencoded string).
|
var script = EmbeddedResourceReader.Read(typeof(Prerenderer), "/Content/Node/prerenderer.js");
|
||||||
// The following logic handles special characters in URL paths in the same way that
|
return new StringAsTempFile(script); // Will be cleaned up on process exit
|
||||||
// Node and client-side JS does. For example, the path "/a=b%20c" gets passed through
|
});
|
||||||
// unchanged (whereas other .NET APIs do change it - Path.Value will return it as
|
|
||||||
// "/a=b c" and Path.ToString() will return it as "/a%3db%20c")
|
|
||||||
var requestFeature = httpContext.Features.Get<IHttpRequestFeature>();
|
|
||||||
var unencodedPathAndQuery = requestFeature.RawTarget;
|
|
||||||
|
|
||||||
var request = httpContext.Request;
|
|
||||||
var unencodedAbsoluteUrl = $"{request.Scheme}://{request.Host}{unencodedPathAndQuery}";
|
|
||||||
|
|
||||||
return RenderToString(
|
|
||||||
applicationBasePath,
|
|
||||||
nodeServices,
|
|
||||||
applicationStoppingToken,
|
|
||||||
bootModule,
|
|
||||||
unencodedAbsoluteUrl,
|
|
||||||
unencodedPathAndQuery,
|
|
||||||
customDataParameter,
|
|
||||||
timeoutMilliseconds,
|
|
||||||
request.PathBase.ToString());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -55,7 +25,6 @@ namespace Microsoft.AspNetCore.SpaServices.Prerendering
|
|||||||
/// </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>
|
||||||
@@ -66,7 +35,6 @@ 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,
|
||||||
@@ -75,7 +43,7 @@ namespace Microsoft.AspNetCore.SpaServices.Prerendering
|
|||||||
string requestPathBase)
|
string requestPathBase)
|
||||||
{
|
{
|
||||||
return nodeServices.InvokeExportAsync<RenderToStringResult>(
|
return nodeServices.InvokeExportAsync<RenderToStringResult>(
|
||||||
GetNodeScriptFilename(applicationStoppingToken),
|
NodeScript.Value.FileName,
|
||||||
"renderToString",
|
"renderToString",
|
||||||
applicationBasePath,
|
applicationBasePath,
|
||||||
bootModule,
|
bootModule,
|
||||||
@@ -85,19 +53,5 @@ 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text;
|
|
||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
using Microsoft.AspNetCore.NodeServices;
|
|
||||||
using Microsoft.AspNetCore.SpaServices.Prerendering;
|
|
||||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
|
||||||
|
|
||||||
namespace Microsoft.Extensions.DependencyInjection
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Extension methods for setting up prerendering features in an <see cref="IServiceCollection" />.
|
|
||||||
/// </summary>
|
|
||||||
public static class PrerenderingServiceCollectionExtensions
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Configures the dependency injection system to supply an implementation
|
|
||||||
/// of <see cref="ISpaPrerenderer"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="serviceCollection">The <see cref="IServiceCollection"/>.</param>
|
|
||||||
public static void AddSpaPrerenderer(this IServiceCollection serviceCollection)
|
|
||||||
{
|
|
||||||
serviceCollection.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
|
|
||||||
serviceCollection.AddSingleton<ISpaPrerenderer, DefaultSpaPrerenderer>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,4 @@
|
|||||||
using Newtonsoft.Json;
|
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.SpaServices.Prerendering
|
namespace Microsoft.AspNetCore.SpaServices.Prerendering
|
||||||
{
|
{
|
||||||
@@ -32,29 +30,5 @@ namespace Microsoft.AspNetCore.SpaServices.Prerendering
|
|||||||
/// If set, specifies the HTTP status code that should be sent back with the server response.
|
/// If set, specifies the HTTP status code that should be sent back with the server response.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int? StatusCode { get; set; }
|
public int? StatusCode { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Constructs a block of JavaScript code that assigns data from the
|
|
||||||
/// <see cref="Globals"/> property to the global namespace.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>A block of JavaScript code.</returns>
|
|
||||||
public string CreateGlobalsAssignmentScript()
|
|
||||||
{
|
|
||||||
if (Globals == null)
|
|
||||||
{
|
|
||||||
return string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
var stringBuilder = new StringBuilder();
|
|
||||||
|
|
||||||
foreach (var property in Globals.Properties())
|
|
||||||
{
|
|
||||||
stringBuilder.AppendFormat("window.{0} = {1};",
|
|
||||||
property.Name,
|
|
||||||
property.Value.ToString(Formatting.None));
|
|
||||||
}
|
|
||||||
|
|
||||||
return stringBuilder.ToString();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -20,7 +20,7 @@ Behind the scenes, it uses the [`Microsoft.AspNetCore.NodeServices`](https://git
|
|||||||
|
|
||||||
### Installation into existing projects
|
### Installation into existing projects
|
||||||
|
|
||||||
* Install the `Microsoft.AspNetCore.SpaServices` NuGet package
|
* Add `Microsoft.AspNetCore.SpaServices` to the dependencies list in your `project.json` file
|
||||||
* Run `dotnet restore` (or if you use Visual Studio, just wait a moment - it will restore dependencies automatically)
|
* Run `dotnet restore` (or if you use Visual Studio, just wait a moment - it will restore dependencies automatically)
|
||||||
* Install supporting NPM packages for the features you'll be using:
|
* Install supporting NPM packages for the features you'll be using:
|
||||||
* For **server-side prerendering**, install `aspnet-prerendering`
|
* For **server-side prerendering**, install `aspnet-prerendering`
|
||||||
|
|||||||
@@ -28,11 +28,6 @@ namespace Microsoft.AspNetCore.SpaServices.Webpack
|
|||||||
string pathPrefix,
|
string pathPrefix,
|
||||||
ConditionalProxyMiddlewareOptions options)
|
ConditionalProxyMiddlewareOptions options)
|
||||||
{
|
{
|
||||||
if (!pathPrefix.StartsWith("/"))
|
|
||||||
{
|
|
||||||
pathPrefix = "/" + pathPrefix;
|
|
||||||
}
|
|
||||||
|
|
||||||
_next = next;
|
_next = next;
|
||||||
_pathPrefix = pathPrefix;
|
_pathPrefix = pathPrefix;
|
||||||
_options = options;
|
_options = options;
|
||||||
@@ -70,7 +65,7 @@ namespace Microsoft.AspNetCore.SpaServices.Webpack
|
|||||||
|
|
||||||
requestMessage.Headers.Host = _options.Host + ":" + _options.Port;
|
requestMessage.Headers.Host = _options.Host + ":" + _options.Port;
|
||||||
var uriString =
|
var uriString =
|
||||||
$"{_options.Scheme}://{_options.Host}:{_options.Port}{context.Request.Path}{context.Request.QueryString}";
|
$"{_options.Scheme}://{_options.Host}:{_options.Port}{context.Request.PathBase}{context.Request.Path}{context.Request.QueryString}";
|
||||||
requestMessage.RequestUri = new Uri(uriString);
|
requestMessage.RequestUri = new Uri(uriString);
|
||||||
requestMessage.Method = new HttpMethod(context.Request.Method);
|
requestMessage.Method = new HttpMethod(context.Request.Method);
|
||||||
|
|
||||||
@@ -102,17 +97,9 @@ 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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,14 @@
|
|||||||
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
|
||||||
{
|
{
|
||||||
@@ -49,7 +54,7 @@ namespace Microsoft.AspNetCore.Builder
|
|||||||
// middleware). And since this is a dev-time-only feature, it doesn't matter if the default transport isn't
|
// middleware). And since this is a dev-time-only feature, it doesn't matter if the default transport isn't
|
||||||
// as fast as some theoretical future alternative.
|
// as fast as some theoretical future alternative.
|
||||||
var nodeServicesOptions = new NodeServicesOptions(appBuilder.ApplicationServices);
|
var nodeServicesOptions = new NodeServicesOptions(appBuilder.ApplicationServices);
|
||||||
nodeServicesOptions.WatchFileExtensions = new string[] { }; // Don't watch anything
|
nodeServicesOptions.WatchFileExtensions = new string[] {}; // Don't watch anything
|
||||||
if (!string.IsNullOrEmpty(options.ProjectPath))
|
if (!string.IsNullOrEmpty(options.ProjectPath))
|
||||||
{
|
{
|
||||||
nodeServicesOptions.ProjectPath = options.ProjectPath;
|
nodeServicesOptions.ProjectPath = options.ProjectPath;
|
||||||
@@ -68,7 +73,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, nodeServicesOptions.ApplicationStoppingToken); // Will be cleaned up on process exit
|
var nodeScript = new StringAsTempFile(script); // 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
|
||||||
@@ -101,9 +106,9 @@ namespace Microsoft.AspNetCore.Builder
|
|||||||
// plus /__webpack_hmr is proxied with infinite timeout, because it's an EventSource (long-lived request).
|
// plus /__webpack_hmr is proxied with infinite timeout, because it's an EventSource (long-lived request).
|
||||||
foreach (var publicPath in devServerInfo.PublicPaths)
|
foreach (var publicPath in devServerInfo.PublicPaths)
|
||||||
{
|
{
|
||||||
appBuilder.UseProxyToLocalWebpackDevMiddleware(publicPath + hmrEndpoint, devServerInfo.Port, Timeout.InfiniteTimeSpan);
|
|
||||||
appBuilder.UseProxyToLocalWebpackDevMiddleware(publicPath, devServerInfo.Port, TimeSpan.FromSeconds(100));
|
appBuilder.UseProxyToLocalWebpackDevMiddleware(publicPath, devServerInfo.Port, TimeSpan.FromSeconds(100));
|
||||||
}
|
}
|
||||||
|
appBuilder.UseProxyToLocalWebpackDevMiddleware(hmrEndpoint, devServerInfo.Port, Timeout.InfiniteTimeSpan);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void UseProxyToLocalWebpackDevMiddleware(this IApplicationBuilder appBuilder, string publicPath, int proxyToPort, TimeSpan requestTimeout)
|
private static void UseProxyToLocalWebpackDevMiddleware(this IApplicationBuilder appBuilder, string publicPath, int proxyToPort, TimeSpan requestTimeout)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "aspnet-prerendering",
|
"name": "aspnet-prerendering",
|
||||||
"version": "2.0.6",
|
"version": "2.0.5",
|
||||||
"description": "Helpers for server-side rendering of JavaScript applications in ASP.NET Core projects. Works in conjunction with the Microsoft.AspNetCore.SpaServices NuGet package.",
|
"description": "Helpers for server-side rendering of JavaScript applications in ASP.NET Core projects. Works in conjunction with the Microsoft.AspNetCore.SpaServices NuGet package.",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
|
/// <reference path="./PrerenderingInterfaces.d.ts" />
|
||||||
import * as url from 'url';
|
import * as url from 'url';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as domain from 'domain';
|
import * as domain from 'domain';
|
||||||
import { run as domainTaskRun, baseUrl as domainTaskBaseUrl } from 'domain-task/main';
|
import { run as domainTaskRun, baseUrl as domainTaskBaseUrl } from 'domain-task/main';
|
||||||
import { BootFunc, BootFuncParams, BootModuleInfo, RenderToStringCallback, RenderToStringFunc } from './PrerenderingInterfaces';
|
|
||||||
|
|
||||||
const defaultTimeoutMilliseconds = 30 * 1000;
|
const defaultTimeoutMilliseconds = 30 * 1000;
|
||||||
|
|
||||||
|
|||||||
@@ -1,28 +1,26 @@
|
|||||||
export interface RenderToStringFunc {
|
interface RenderToStringFunc {
|
||||||
(callback: RenderToStringCallback, applicationBasePath: string, bootModule: BootModuleInfo, absoluteRequestUrl: string, requestPathAndQuery: string, customDataParameter: any, overrideTimeoutMilliseconds: number, requestPathBase: string): void;
|
(callback: RenderToStringCallback, applicationBasePath: string, bootModule: BootModuleInfo, absoluteRequestUrl: string, requestPathAndQuery: string, customDataParameter: any, overrideTimeoutMilliseconds: number, requestPathBase: string): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RenderToStringCallback {
|
interface RenderToStringCallback {
|
||||||
(error: any, result?: RenderResult): void;
|
(error: any, result?: RenderToStringResult): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RenderToStringResult {
|
interface RenderToStringResult {
|
||||||
html: string;
|
html: string;
|
||||||
statusCode?: number;
|
statusCode?: number;
|
||||||
globals?: { [key: string]: any };
|
globals?: { [key: string]: any };
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RedirectResult {
|
interface RedirectResult {
|
||||||
redirectUrl: string;
|
redirectUrl: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type RenderResult = RenderToStringResult | RedirectResult;
|
interface BootFunc {
|
||||||
|
(params: BootFuncParams): Promise<RenderToStringResult>;
|
||||||
export interface BootFunc {
|
|
||||||
(params: BootFuncParams): Promise<RenderResult>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BootFuncParams {
|
interface BootFuncParams {
|
||||||
location: any; // e.g., Location object containing information '/some/path'
|
location: any; // e.g., Location object containing information '/some/path'
|
||||||
origin: string; // e.g., 'https://example.com:1234'
|
origin: string; // e.g., 'https://example.com:1234'
|
||||||
url: string; // e.g., '/some/path'
|
url: string; // e.g., '/some/path'
|
||||||
@@ -32,7 +30,7 @@ export interface BootFuncParams {
|
|||||||
data: any; // any custom object passed through from .NET
|
data: any; // any custom object passed through from .NET
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BootModuleInfo {
|
interface BootModuleInfo {
|
||||||
moduleName: string;
|
moduleName: string;
|
||||||
exportName?: string;
|
exportName?: string;
|
||||||
webpackConfig?: string;
|
webpackConfig?: string;
|
||||||
@@ -1,2 +1,5 @@
|
|||||||
|
/// <reference path="./PrerenderingInterfaces.d.ts" />
|
||||||
|
|
||||||
export * from './Prerendering';
|
export * from './Prerendering';
|
||||||
export * from './PrerenderingInterfaces';
|
|
||||||
|
export type RenderResult = RenderToStringResult | RedirectResult;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "aspnet-webpack-react",
|
"name": "aspnet-webpack-react",
|
||||||
"version": "3.0.0-beta.1",
|
"version": "2.0.0",
|
||||||
"description": "Helpers for using Webpack with React in ASP.NET Core projects. Works in conjunction with the Microsoft.AspNetCore.SpaServices NuGet package.",
|
"description": "Helpers for using Webpack with React in ASP.NET Core projects. Works in conjunction with the Microsoft.AspNetCore.SpaServices NuGet package.",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "domain-task",
|
"name": "domain-task",
|
||||||
"version": "3.0.3",
|
"version": "3.0.0",
|
||||||
"description": "Tracks outstanding operations for a logical thread of execution",
|
"description": "Tracks outstanding operations for a logical thread of execution",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@@ -18,7 +18,6 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"domain-context": "^0.5.1",
|
"domain-context": "^0.5.1",
|
||||||
"is-absolute-url": "^2.1.0",
|
|
||||||
"isomorphic-fetch": "^2.2.1"
|
"isomorphic-fetch": "^2.2.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@@ -1,17 +1,11 @@
|
|||||||
import * as url from 'url';
|
import * as url from 'url';
|
||||||
import * as domain from 'domain';
|
import * as domain from 'domain';
|
||||||
import * as domainContext from 'domain-context';
|
import * as domainContext from 'domain-context';
|
||||||
import * as isAbsoluteUrl from 'is-absolute-url';
|
|
||||||
import { baseUrl } from './main';
|
import { baseUrl } from './main';
|
||||||
const isomorphicFetch = require('isomorphic-fetch');
|
const isomorphicFetch = require('isomorphic-fetch');
|
||||||
const isNode = typeof process === 'object' && process.versions && !!process.versions.node;
|
const isNode = typeof process === 'object' && process.versions && !!process.versions.node;
|
||||||
const nodeHttps = isNode && require('https');
|
|
||||||
const isHttpsRegex = /^https\:/;
|
|
||||||
|
|
||||||
function issueRequest(baseUrl: string, req: string | Request, init?: RequestInit): Promise<any> {
|
function issueRequest(baseUrl: string, req: string | Request, init?: RequestInit): Promise<any> {
|
||||||
const reqUrl = (req instanceof Request) ? req.url : req;
|
|
||||||
const isRelativeUrl = reqUrl && !isAbsoluteUrl(reqUrl);
|
|
||||||
|
|
||||||
// Resolve relative URLs
|
// Resolve relative URLs
|
||||||
if (baseUrl) {
|
if (baseUrl) {
|
||||||
if (req instanceof Request) {
|
if (req instanceof Request) {
|
||||||
@@ -31,45 +25,9 @@ function issueRequest(baseUrl: string, req: string | Request, init?: RequestInit
|
|||||||
`);
|
`);
|
||||||
}
|
}
|
||||||
|
|
||||||
init = applyHttpsAgentPolicy(init, isRelativeUrl, baseUrl);
|
|
||||||
return isomorphicFetch(req, init);
|
return isomorphicFetch(req, init);
|
||||||
}
|
}
|
||||||
|
|
||||||
function applyHttpsAgentPolicy(init: RequestInit, isRelativeUrl: boolean, baseUrl: string): RequestInit {
|
|
||||||
// HTTPS is awkward in Node because it uses a built-in list of CAs, rather than recognizing
|
|
||||||
// the OS's system-level CA list. There are dozens of issues filed against Node about this,
|
|
||||||
// but still (as of v8.0.0) no resolution besides manually duplicating your CA config.
|
|
||||||
//
|
|
||||||
// The biggest problem for typical isomorphic-SPA development this causes is that if you're
|
|
||||||
// using a self-signed localhost cert in development, Node won't be able to make API calls
|
|
||||||
// to it (e.g., https://github.com/aspnet/JavaScriptServices/issues/1089). Developers could
|
|
||||||
// fix this by either manually configuring the cert in Node (which is extremely inconvenient,
|
|
||||||
// especially if multiple devs on a team have different self-signed localhost certs), or by
|
|
||||||
// disabling cert verification on their API requests.
|
|
||||||
//
|
|
||||||
// Fortunately, 'domain-task/fetch' knows when you're making a relative-URL request to your
|
|
||||||
// own web server (as opposed to an arbitrary request to anywhere else). In this specific case,
|
|
||||||
// there's no real point in cert verification, since the request never even leaves the machine
|
|
||||||
// so a MitM attack isn't meaningful. So by default, when your code is running in Node and
|
|
||||||
// is making a relative-URL request, *and* if you haven't explicitly configured any option
|
|
||||||
// for 'agent' (which would let you set up other HTTPS-handling policies), then we automatically
|
|
||||||
// disable cert verification for that request.
|
|
||||||
if (isNode && isRelativeUrl) {
|
|
||||||
const isHttps = baseUrl && isHttpsRegex.test(baseUrl);
|
|
||||||
if (isHttps) {
|
|
||||||
const hasAgentConfig = init && ('agent' in init);
|
|
||||||
if (!hasAgentConfig) {
|
|
||||||
const agentForRequest = new (nodeHttps.Agent)({ rejectUnauthorized: false });
|
|
||||||
|
|
||||||
init = init || {};
|
|
||||||
(init as any).agent = agentForRequest;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return init;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function fetch(url: string | Request, init?: RequestInit): Promise<any> {
|
export function fetch(url: string | Request, init?: RequestInit): Promise<any> {
|
||||||
// As of domain-task 2.0.0, we no longer auto-add the 'fetch' promise to the current domain task list.
|
// As of domain-task 2.0.0, we no longer auto-add the 'fetch' promise to the current domain task list.
|
||||||
// This is because it's misleading to do so, and can result in race-condition bugs, e.g.,
|
// This is because it's misleading to do so, and can result in race-condition bugs, e.g.,
|
||||||
|
|||||||
@@ -1,25 +1,19 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework Condition="'$(TargetFrameworkOverride)' == ''">netcoreapp2.0</TargetFramework>
|
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||||
<TargetFramework Condition="'$(TargetFrameworkOverride)' != ''">TargetFrameworkOverride</TargetFramework>
|
|
||||||
<TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
|
<TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
|
||||||
<TypeScriptToolsVersion>Latest</TypeScriptToolsVersion>
|
<PackageTargetFallback>$(PackageTargetFallback);portable-net45+win8+wp8+wpa81;</PackageTargetFallback>
|
||||||
<IsPackable>false</IsPackable>
|
<IsPackable>false</IsPackable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup Condition="'$(TargetFrameworkOverride)' == ''">
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0-rtm-26190" />
|
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0-*" />
|
||||||
</ItemGroup>
|
<PackageReference Include="Microsoft.AspNetCore.SpaServices" Version="2.0.0-*" />
|
||||||
<ItemGroup Condition="'$(TargetFrameworkOverride)' != ''">
|
|
||||||
<PackageReference Include="Microsoft.AspNetCore" Version="2.0.0-rtm-26190" />
|
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.0.0-rtm-26190" />
|
|
||||||
<PackageReference Include="Microsoft.AspNetCore.SpaServices" Version="2.0.0-rtm-26190" />
|
|
||||||
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="2.0.0-rtm-26190" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.0-rtm-26190" />
|
<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.0-*" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -27,23 +21,7 @@
|
|||||||
<Content Remove="ClientApp\**" />
|
<Content Remove="ClientApp\**" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<!--/-:cnd:noEmit -->
|
<Target Name="RunWebpack" AfterTargets="ComputeFilesToPublish">
|
||||||
<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" />
|
||||||
|
|||||||
@@ -1,21 +0,0 @@
|
|||||||
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', useFactory: getOriginUrl }
|
|
||||||
]
|
|
||||||
})
|
|
||||||
export class AppModule {
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getOriginUrl() {
|
|
||||||
return location.origin;
|
|
||||||
}
|
|
||||||
22
templates/AngularSpa/ClientApp/app/app.module.client.ts
Normal file
22
templates/AngularSpa/ClientApp/app/app.module.client.ts
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
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 {
|
||||||
|
}
|
||||||
@@ -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 { AppModuleShared } from './app.module.shared';
|
import { sharedConfig } from './app.module.shared';
|
||||||
import { AppComponent } from './components/app/app.component';
|
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
bootstrap: [ AppComponent ],
|
bootstrap: sharedConfig.bootstrap,
|
||||||
|
declarations: sharedConfig.declarations,
|
||||||
imports: [
|
imports: [
|
||||||
ServerModule,
|
ServerModule,
|
||||||
AppModuleShared
|
...sharedConfig.imports
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class AppModule {
|
export class AppModule {
|
||||||
|
|||||||
@@ -1,16 +1,14 @@
|
|||||||
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';
|
||||||
|
|
||||||
@NgModule({
|
export const sharedConfig: NgModule = {
|
||||||
|
bootstrap: [ AppComponent ],
|
||||||
declarations: [
|
declarations: [
|
||||||
AppComponent,
|
AppComponent,
|
||||||
NavMenuComponent,
|
NavMenuComponent,
|
||||||
@@ -19,9 +17,6 @@ import { CounterComponent } from './components/counter/counter.component';
|
|||||||
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 },
|
||||||
@@ -30,6 +25,4 @@ import { CounterComponent } from './components/counter/counter.component';
|
|||||||
{ path: '**', redirectTo: 'home' }
|
{ path: '**', redirectTo: 'home' }
|
||||||
])
|
])
|
||||||
]
|
]
|
||||||
})
|
};
|
||||||
export class AppModuleShared {
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
<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>
|
||||||
|
|||||||
@@ -7,11 +7,14 @@ 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[];
|
||||||
}, error => console.error(error));
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
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.browser';
|
import { AppModule } from './app/app.module.client';
|
||||||
|
|
||||||
if (module['hot']) {
|
if (module['hot']) {
|
||||||
module['hot'].accept();
|
module['hot'].accept();
|
||||||
@@ -11,7 +11,8 @@ 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 => {
|
||||||
@@ -26,7 +27,8 @@ 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();
|
||||||
});
|
});
|
||||||
@@ -2,7 +2,11 @@
|
|||||||
ViewData["Title"] = "Home Page";
|
ViewData["Title"] = "Home Page";
|
||||||
}
|
}
|
||||||
|
|
||||||
<app asp-prerender-module="ClientApp/dist/main-server">Loading...</app>
|
<app asp-prerender-module="ClientApp/dist/main-server"
|
||||||
|
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 {
|
||||||
|
|||||||
3
templates/AngularSpa/global.json
Normal file
3
templates/AngularSpa/global.json
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"sdk": { "version": "sdkVersionInjectedHere" }
|
||||||
|
}
|
||||||
3730
templates/AngularSpa/npm-shrinkwrap.json
generated
3730
templates/AngularSpa/npm-shrinkwrap.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,36 +1,33 @@
|
|||||||
{
|
{
|
||||||
"name": "WebApplicationBasic",
|
"name": "WebApplicationBasic",
|
||||||
"private": true,
|
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "karma start ClientApp/test/karma.conf.js"
|
"test": "karma start ClientApp/test/karma.conf.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular/animations": "4.2.5",
|
"@angular/animations": "4.1.2",
|
||||||
"@angular/common": "4.2.5",
|
"@angular/common": "4.1.2",
|
||||||
"@angular/compiler": "4.2.5",
|
"@angular/compiler": "4.1.2",
|
||||||
"@angular/compiler-cli": "4.2.5",
|
"@angular/core": "4.1.2",
|
||||||
"@angular/core": "4.2.5",
|
"@angular/forms": "4.1.2",
|
||||||
"@angular/forms": "4.2.5",
|
"@angular/http": "4.1.2",
|
||||||
"@angular/http": "4.2.5",
|
"@angular/platform-browser": "4.1.2",
|
||||||
"@angular/platform-browser": "4.2.5",
|
"@angular/platform-browser-dynamic": "4.1.2",
|
||||||
"@angular/platform-browser-dynamic": "4.2.5",
|
"@angular/platform-server": "4.1.2",
|
||||||
"@angular/platform-server": "4.2.5",
|
"@angular/router": "4.1.2",
|
||||||
"@angular/router": "4.2.5",
|
"@types/node": "7.0.18",
|
||||||
"@ngtools/webpack": "1.5.0",
|
|
||||||
"@types/node": "8.0.8",
|
|
||||||
"angular2-template-loader": "0.6.2",
|
"angular2-template-loader": "0.6.2",
|
||||||
"aspnet-prerendering": "^2.0.5",
|
"aspnet-prerendering": "^2.0.5",
|
||||||
"aspnet-webpack": "^1.0.29",
|
"aspnet-webpack": "^1.0.29",
|
||||||
"awesome-typescript-loader": "3.2.1",
|
"awesome-typescript-loader": "3.1.3",
|
||||||
"bootstrap": "3.3.7",
|
"bootstrap": "3.3.7",
|
||||||
"css": "2.2.1",
|
"css": "2.2.1",
|
||||||
"css-loader": "0.28.4",
|
"css-loader": "0.28.1",
|
||||||
"es6-shim": "0.35.3",
|
"es6-shim": "0.35.3",
|
||||||
"event-source-polyfill": "0.0.9",
|
"event-source-polyfill": "0.0.9",
|
||||||
"expose-loader": "0.7.3",
|
"expose-loader": "0.7.3",
|
||||||
"extract-text-webpack-plugin": "2.1.2",
|
"extract-text-webpack-plugin": "2.1.0",
|
||||||
"file-loader": "0.11.2",
|
"file-loader": "0.11.1",
|
||||||
"html-loader": "0.4.5",
|
"html-loader": "0.4.5",
|
||||||
"isomorphic-fetch": "2.2.1",
|
"isomorphic-fetch": "2.2.1",
|
||||||
"jquery": "3.2.1",
|
"jquery": "3.2.1",
|
||||||
@@ -38,24 +35,24 @@
|
|||||||
"preboot": "4.5.2",
|
"preboot": "4.5.2",
|
||||||
"raw-loader": "0.5.1",
|
"raw-loader": "0.5.1",
|
||||||
"reflect-metadata": "0.1.10",
|
"reflect-metadata": "0.1.10",
|
||||||
"rxjs": "5.4.2",
|
"rxjs": "5.4.0",
|
||||||
"style-loader": "0.18.2",
|
"style-loader": "0.17.0",
|
||||||
"to-string-loader": "1.1.5",
|
"to-string-loader": "1.1.5",
|
||||||
"typescript": "2.4.1",
|
"typescript": "2.3.2",
|
||||||
"url-loader": "0.5.9",
|
"url-loader": "0.5.8",
|
||||||
"webpack": "2.5.1",
|
"webpack": "2.5.1",
|
||||||
"webpack-hot-middleware": "2.18.2",
|
"webpack-hot-middleware": "2.18.0",
|
||||||
"webpack-merge": "4.1.0",
|
"webpack-merge": "4.1.0",
|
||||||
"zone.js": "0.8.12"
|
"zone.js": "0.8.10"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/chai": "4.0.1",
|
"@types/chai": "3.5.2",
|
||||||
"@types/jasmine": "2.5.53",
|
"@types/jasmine": "2.5.47",
|
||||||
"chai": "4.0.2",
|
"chai": "3.5.0",
|
||||||
"jasmine-core": "2.6.4",
|
"jasmine-core": "2.6.1",
|
||||||
"karma": "1.7.0",
|
"karma": "1.7.0",
|
||||||
"karma-chai": "0.1.0",
|
"karma-chai": "0.1.0",
|
||||||
"karma-chrome-launcher": "2.2.0",
|
"karma-chrome-launcher": "2.1.1",
|
||||||
"karma-cli": "1.0.1",
|
"karma-cli": "1.0.1",
|
||||||
"karma-jasmine": "1.1.0",
|
"karma-jasmine": "1.1.0",
|
||||||
"karma-webpack": "2.0.3"
|
"karma-webpack": "2.0.3"
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
const path = require('path');
|
const path = require('path');
|
||||||
const webpack = require('webpack');
|
const webpack = require('webpack');
|
||||||
const merge = require('webpack-merge');
|
const merge = require('webpack-merge');
|
||||||
const AotPlugin = require('@ngtools/webpack').AotPlugin;
|
|
||||||
const CheckerPlugin = require('awesome-typescript-loader').CheckerPlugin;
|
const CheckerPlugin = require('awesome-typescript-loader').CheckerPlugin;
|
||||||
|
|
||||||
module.exports = (env) => {
|
module.exports = (env) => {
|
||||||
@@ -17,7 +16,7 @@ module.exports = (env) => {
|
|||||||
},
|
},
|
||||||
module: {
|
module: {
|
||||||
rules: [
|
rules: [
|
||||||
{ test: /\.ts$/, include: /ClientApp/, use: isDevBuild ? ['awesome-typescript-loader?silent=true', 'angular2-template-loader'] : '@ngtools/webpack' },
|
{ test: /\.ts$/, include: /ClientApp/, use: ['awesome-typescript-loader?silent=true', 'angular2-template-loader'] },
|
||||||
{ test: /\.html$/, use: 'html-loader?minimize=false' },
|
{ test: /\.html$/, use: 'html-loader?minimize=false' },
|
||||||
{ test: /\.css$/, use: [ 'to-string-loader', isDevBuild ? 'css-loader' : 'css-loader?minimize' ] },
|
{ test: /\.css$/, use: [ 'to-string-loader', isDevBuild ? 'css-loader' : 'css-loader?minimize' ] },
|
||||||
{ test: /\.(png|jpg|jpeg|gif|svg)$/, use: 'url-loader?limit=25000' }
|
{ test: /\.(png|jpg|jpeg|gif|svg)$/, use: 'url-loader?limit=25000' }
|
||||||
@@ -29,7 +28,7 @@ module.exports = (env) => {
|
|||||||
// Configuration for client-side bundle suitable for running in browsers
|
// Configuration for client-side bundle suitable for running in browsers
|
||||||
const clientBundleOutputDir = './wwwroot/dist';
|
const clientBundleOutputDir = './wwwroot/dist';
|
||||||
const clientBundleConfig = merge(sharedConfig, {
|
const clientBundleConfig = merge(sharedConfig, {
|
||||||
entry: { 'main-client': './ClientApp/boot.browser.ts' },
|
entry: { 'main-client': './ClientApp/boot-client.ts' },
|
||||||
output: { path: path.join(__dirname, clientBundleOutputDir) },
|
output: { path: path.join(__dirname, clientBundleOutputDir) },
|
||||||
plugins: [
|
plugins: [
|
||||||
new webpack.DllReferencePlugin({
|
new webpack.DllReferencePlugin({
|
||||||
@@ -44,19 +43,14 @@ module.exports = (env) => {
|
|||||||
})
|
})
|
||||||
] : [
|
] : [
|
||||||
// Plugins that apply in production builds only
|
// Plugins that apply in production builds only
|
||||||
new webpack.optimize.UglifyJsPlugin(),
|
new webpack.optimize.UglifyJsPlugin()
|
||||||
new AotPlugin({
|
|
||||||
tsConfigPath: './tsconfig.json',
|
|
||||||
entryModule: path.join(__dirname, 'ClientApp/app/app.module.browser#AppModule'),
|
|
||||||
exclude: ['./**/*.server.ts']
|
|
||||||
})
|
|
||||||
])
|
])
|
||||||
});
|
});
|
||||||
|
|
||||||
// Configuration for server-side (prerendering) bundle suitable for running in Node
|
// Configuration for server-side (prerendering) bundle suitable for running in Node
|
||||||
const serverBundleConfig = merge(sharedConfig, {
|
const serverBundleConfig = merge(sharedConfig, {
|
||||||
resolve: { mainFields: ['main'] },
|
resolve: { mainFields: ['main'] },
|
||||||
entry: { 'main-server': './ClientApp/boot.server.ts' },
|
entry: { 'main-server': './ClientApp/boot-server.ts' },
|
||||||
plugins: [
|
plugins: [
|
||||||
new webpack.DllReferencePlugin({
|
new webpack.DllReferencePlugin({
|
||||||
context: __dirname,
|
context: __dirname,
|
||||||
@@ -64,14 +58,7 @@ module.exports = (env) => {
|
|||||||
sourceType: 'commonjs2',
|
sourceType: 'commonjs2',
|
||||||
name: './vendor'
|
name: './vendor'
|
||||||
})
|
})
|
||||||
].concat(isDevBuild ? [] : [
|
],
|
||||||
// Plugins that apply in production builds only
|
|
||||||
new AotPlugin({
|
|
||||||
tsConfigPath: './tsconfig.json',
|
|
||||||
entryModule: path.join(__dirname, 'ClientApp/app/app.module.server#AppModule'),
|
|
||||||
exclude: ['./**/*.browser.ts']
|
|
||||||
})
|
|
||||||
]),
|
|
||||||
output: {
|
output: {
|
||||||
libraryTarget: 'commonjs',
|
libraryTarget: 'commonjs',
|
||||||
path: path.join(__dirname, './ClientApp/dist')
|
path: path.join(__dirname, './ClientApp/dist')
|
||||||
|
|||||||
@@ -2,27 +2,6 @@ const path = require('path');
|
|||||||
const webpack = require('webpack');
|
const webpack = require('webpack');
|
||||||
const ExtractTextPlugin = require('extract-text-webpack-plugin');
|
const ExtractTextPlugin = require('extract-text-webpack-plugin');
|
||||||
const merge = require('webpack-merge');
|
const merge = require('webpack-merge');
|
||||||
const treeShakableModules = [
|
|
||||||
'@angular/animations',
|
|
||||||
'@angular/common',
|
|
||||||
'@angular/compiler',
|
|
||||||
'@angular/core',
|
|
||||||
'@angular/forms',
|
|
||||||
'@angular/http',
|
|
||||||
'@angular/platform-browser',
|
|
||||||
'@angular/platform-browser-dynamic',
|
|
||||||
'@angular/router',
|
|
||||||
'zone.js',
|
|
||||||
];
|
|
||||||
const nonTreeShakableModules = [
|
|
||||||
'bootstrap',
|
|
||||||
'bootstrap/dist/css/bootstrap.css',
|
|
||||||
'es6-promise',
|
|
||||||
'es6-shim',
|
|
||||||
'event-source-polyfill',
|
|
||||||
'jquery',
|
|
||||||
];
|
|
||||||
const allModules = treeShakableModules.concat(nonTreeShakableModules);
|
|
||||||
|
|
||||||
module.exports = (env) => {
|
module.exports = (env) => {
|
||||||
const extractCSS = new ExtractTextPlugin('vendor.css');
|
const extractCSS = new ExtractTextPlugin('vendor.css');
|
||||||
@@ -35,6 +14,26 @@ module.exports = (env) => {
|
|||||||
{ test: /\.(png|woff|woff2|eot|ttf|svg)(\?|$)/, use: 'url-loader?limit=100000' }
|
{ 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: {
|
output: {
|
||||||
publicPath: '/dist/',
|
publicPath: '/dist/',
|
||||||
filename: '[name].js',
|
filename: '[name].js',
|
||||||
@@ -49,11 +48,6 @@ module.exports = (env) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const clientBundleConfig = merge(sharedConfig, {
|
const clientBundleConfig = merge(sharedConfig, {
|
||||||
entry: {
|
|
||||||
// To keep development builds fast, include all vendor dependencies in the vendor bundle.
|
|
||||||
// But for production builds, leave the tree-shakable ones out so the AOT compiler can produce a smaller bundle.
|
|
||||||
vendor: isDevBuild ? allModules : nonTreeShakableModules
|
|
||||||
},
|
|
||||||
output: { path: path.join(__dirname, 'wwwroot', 'dist') },
|
output: { path: path.join(__dirname, 'wwwroot', 'dist') },
|
||||||
module: {
|
module: {
|
||||||
rules: [
|
rules: [
|
||||||
@@ -74,7 +68,6 @@ module.exports = (env) => {
|
|||||||
const serverBundleConfig = merge(sharedConfig, {
|
const serverBundleConfig = merge(sharedConfig, {
|
||||||
target: 'node',
|
target: 'node',
|
||||||
resolve: { mainFields: ['main'] },
|
resolve: { mainFields: ['main'] },
|
||||||
entry: { vendor: allModules.concat(['aspnet-prerendering']) },
|
|
||||||
output: {
|
output: {
|
||||||
path: path.join(__dirname, 'ClientApp', 'dist'),
|
path: path.join(__dirname, 'ClientApp', 'dist'),
|
||||||
libraryTarget: 'commonjs2',
|
libraryTarget: 'commonjs2',
|
||||||
@@ -82,6 +75,7 @@ module.exports = (env) => {
|
|||||||
module: {
|
module: {
|
||||||
rules: [ { test: /\.css(\?|$)/, use: ['to-string-loader', isDevBuild ? 'css-loader' : 'css-loader?minimize' ] } ]
|
rules: [ { test: /\.css(\?|$)/, use: ['to-string-loader', isDevBuild ? 'css-loader' : 'css-loader?minimize' ] } ]
|
||||||
},
|
},
|
||||||
|
entry: { vendor: ['aspnet-prerendering'] },
|
||||||
plugins: [
|
plugins: [
|
||||||
new webpack.DllPlugin({
|
new webpack.DllPlugin({
|
||||||
path: path.join(__dirname, 'ClientApp', 'dist', '[name]-manifest.json'),
|
path: path.join(__dirname, 'ClientApp', 'dist', '[name]-manifest.json'),
|
||||||
|
|||||||
@@ -1,44 +1,22 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework Condition="'$(TargetFrameworkOverride)' == ''">netcoreapp2.0</TargetFramework>
|
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||||
<TargetFramework Condition="'$(TargetFrameworkOverride)' != ''">TargetFrameworkOverride</TargetFramework>
|
|
||||||
<TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
|
<TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
|
||||||
<TypeScriptToolsVersion>Latest</TypeScriptToolsVersion>
|
<PackageTargetFallback>$(PackageTargetFallback);portable-net45+win8+wp8+wpa81;</PackageTargetFallback>
|
||||||
<IsPackable>false</IsPackable>
|
<IsPackable>false</IsPackable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup Condition="'$(TargetFrameworkOverride)' == ''">
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0-rtm-26190" />
|
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0-*" />
|
||||||
</ItemGroup>
|
<PackageReference Include="Microsoft.AspNetCore.SpaServices" Version="2.0.0-*" />
|
||||||
<ItemGroup Condition="'$(TargetFrameworkOverride)' != ''">
|
|
||||||
<PackageReference Include="Microsoft.AspNetCore" Version="2.0.0-rtm-26190" />
|
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.0.0-rtm-26190" />
|
|
||||||
<PackageReference Include="Microsoft.AspNetCore.SpaServices" Version="2.0.0-rtm-26190" />
|
|
||||||
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="2.0.0-rtm-26190" />
|
|
||||||
</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>
|
||||||
|
|
||||||
<!--/-:cnd:noEmit -->
|
<Target Name="RunWebpack" AfterTargets="ComputeFilesToPublish">
|
||||||
<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" />
|
||||||
|
|||||||
3
templates/AureliaSpa/global.json
Normal file
3
templates/AureliaSpa/global.json
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"sdk": { "version": "sdkVersionInjectedHere" }
|
||||||
|
}
|
||||||
2615
templates/AureliaSpa/npm-shrinkwrap.json
generated
2615
templates/AureliaSpa/npm-shrinkwrap.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "WebApplicationBasic",
|
"name": "WebApplicationBasic",
|
||||||
"private": true,
|
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"aurelia-bootstrapper": "^2.0.1",
|
"aurelia-bootstrapper": "^2.0.1",
|
||||||
|
|||||||
@@ -1,44 +1,22 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework Condition="'$(TargetFrameworkOverride)' == ''">netcoreapp2.0</TargetFramework>
|
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||||
<TargetFramework Condition="'$(TargetFrameworkOverride)' != ''">TargetFrameworkOverride</TargetFramework>
|
|
||||||
<TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
|
<TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
|
||||||
<TypeScriptToolsVersion>Latest</TypeScriptToolsVersion>
|
<PackageTargetFallback>$(PackageTargetFallback);portable-net45+win8+wp8+wpa81;</PackageTargetFallback>
|
||||||
<IsPackable>false</IsPackable>
|
<IsPackable>false</IsPackable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup Condition="'$(TargetFrameworkOverride)' == ''">
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0-rtm-26190" />
|
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0-*" />
|
||||||
</ItemGroup>
|
<PackageReference Include="Microsoft.AspNetCore.SpaServices" Version="2.0.0-*" />
|
||||||
<ItemGroup Condition="'$(TargetFrameworkOverride)' != ''">
|
|
||||||
<PackageReference Include="Microsoft.AspNetCore" Version="2.0.0-rtm-26190" />
|
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.0.0-rtm-26190" />
|
|
||||||
<PackageReference Include="Microsoft.AspNetCore.SpaServices" Version="2.0.0-rtm-26190" />
|
|
||||||
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="2.0.0-rtm-26190" />
|
|
||||||
</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>
|
||||||
|
|
||||||
<!--/-:cnd:noEmit -->
|
<Target Name="RunWebpack" AfterTargets="ComputeFilesToPublish">
|
||||||
<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" />
|
||||||
|
|||||||
3
templates/KnockoutSpa/global.json
Normal file
3
templates/KnockoutSpa/global.json
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"sdk": { "version": "sdkVersionInjectedHere" }
|
||||||
|
}
|
||||||
2503
templates/KnockoutSpa/npm-shrinkwrap.json
generated
2503
templates/KnockoutSpa/npm-shrinkwrap.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "WebApplicationBasic",
|
"name": "WebApplicationBasic",
|
||||||
"private": true,
|
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/core-js": "^0.9.34",
|
"@types/core-js": "^0.9.34",
|
||||||
|
|||||||
@@ -62,8 +62,7 @@ export const actionCreators = {
|
|||||||
|
|
||||||
const unloadedState: WeatherForecastsState = { startDateIndex: null, forecasts: [], isLoading: false };
|
const unloadedState: WeatherForecastsState = { startDateIndex: null, forecasts: [], isLoading: false };
|
||||||
|
|
||||||
export const reducer: Reducer<WeatherForecastsState> = (state: WeatherForecastsState, incomingAction: Action) => {
|
export const reducer: Reducer<WeatherForecastsState> = (state: WeatherForecastsState, action: KnownAction) => {
|
||||||
const action = incomingAction as KnownAction;
|
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case 'REQUEST_WEATHER_FORECASTS':
|
case 'REQUEST_WEATHER_FORECASTS':
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -1,21 +1,15 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework Condition="'$(TargetFrameworkOverride)' == ''">netcoreapp2.0</TargetFramework>
|
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||||
<TargetFramework Condition="'$(TargetFrameworkOverride)' != ''">TargetFrameworkOverride</TargetFramework>
|
|
||||||
<TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
|
<TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
|
||||||
<TypeScriptToolsVersion>Latest</TypeScriptToolsVersion>
|
<PackageTargetFallback>$(PackageTargetFallback);portable-net45+win8+wp8+wpa81;</PackageTargetFallback>
|
||||||
<IsPackable>false</IsPackable>
|
<IsPackable>false</IsPackable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup Condition="'$(TargetFrameworkOverride)' == ''">
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0-rtm-26190" />
|
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0-*" />
|
||||||
</ItemGroup>
|
<PackageReference Include="Microsoft.AspNetCore.SpaServices" Version="2.0.0-*" />
|
||||||
<ItemGroup Condition="'$(TargetFrameworkOverride)' != ''">
|
|
||||||
<PackageReference Include="Microsoft.AspNetCore" Version="2.0.0-rtm-26190" />
|
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.0.0-rtm-26190" />
|
|
||||||
<PackageReference Include="Microsoft.AspNetCore.SpaServices" Version="2.0.0-rtm-26190" />
|
|
||||||
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="2.0.0-rtm-26190" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -27,23 +21,7 @@
|
|||||||
<Content Remove="ClientApp\**" />
|
<Content Remove="ClientApp\**" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<!--/-:cnd:noEmit -->
|
<Target Name="RunWebpack" AfterTargets="ComputeFilesToPublish">
|
||||||
<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" />
|
||||||
|
|||||||
3
templates/ReactReduxSpa/global.json
Normal file
3
templates/ReactReduxSpa/global.json
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"sdk": { "version": "sdkVersionInjectedHere" }
|
||||||
|
}
|
||||||
2923
templates/ReactReduxSpa/npm-shrinkwrap.json
generated
2923
templates/ReactReduxSpa/npm-shrinkwrap.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,44 +1,42 @@
|
|||||||
{
|
{
|
||||||
"name": "WebApplicationBasic",
|
"name": "WebApplicationBasic",
|
||||||
"private": true,
|
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/history": "4.6.0",
|
"@types/history": "4.5.1",
|
||||||
"@types/react": "15.0.35",
|
"@types/react": "15.0.24",
|
||||||
"@types/react-dom": "15.5.1",
|
"@types/react-dom": "15.5.0",
|
||||||
"@types/react-redux": "4.4.45",
|
"@types/react-redux": "4.4.40",
|
||||||
"@types/react-router": "4.0.12",
|
"@types/react-router-dom": "4.0.4",
|
||||||
"@types/react-router-dom": "4.0.5",
|
"@types/react-router-redux": "5.0.1",
|
||||||
"@types/react-router-redux": "5.0.3",
|
|
||||||
"@types/webpack": "2.2.15",
|
"@types/webpack": "2.2.15",
|
||||||
"@types/webpack-env": "1.13.0",
|
"@types/webpack-env": "1.13.0",
|
||||||
"aspnet-prerendering": "^2.0.5",
|
"aspnet-prerendering": "^2.0.5",
|
||||||
"aspnet-webpack": "^1.0.29",
|
"aspnet-webpack": "^1.0.29",
|
||||||
"aspnet-webpack-react": "^3.0.0-beta",
|
"aspnet-webpack-react": "^2.0.0",
|
||||||
"awesome-typescript-loader": "3.2.1",
|
"awesome-typescript-loader": "3.1.3",
|
||||||
"bootstrap": "3.3.7",
|
"bootstrap": "3.3.7",
|
||||||
"css-loader": "0.28.4",
|
"css-loader": "0.28.1",
|
||||||
"domain-task": "^3.0.3",
|
"domain-task": "^3.0.0",
|
||||||
"event-source-polyfill": "0.0.9",
|
"event-source-polyfill": "0.0.9",
|
||||||
"extract-text-webpack-plugin": "2.1.2",
|
"extract-text-webpack-plugin": "2.1.0",
|
||||||
"file-loader": "0.11.2",
|
"file-loader": "0.11.1",
|
||||||
"history": "4.6.3",
|
"history": "4.6.1",
|
||||||
"jquery": "3.2.1",
|
"jquery": "3.2.1",
|
||||||
"json-loader": "0.5.4",
|
"json-loader": "0.5.4",
|
||||||
"node-noop": "1.0.0",
|
"node-noop": "1.0.0",
|
||||||
"react": "15.6.1",
|
"react": "15.5.4",
|
||||||
"react-dom": "15.6.1",
|
"react-dom": "15.5.4",
|
||||||
"react-hot-loader": "3.0.0-beta.7",
|
"react-hot-loader": "3.0.0-beta.7",
|
||||||
"react-redux": "5.0.5",
|
"react-redux": "5.0.4",
|
||||||
"react-router-dom": "4.1.1",
|
"react-router-dom": "4.1.1",
|
||||||
"react-router-redux": "5.0.0-alpha.6",
|
"react-router-redux": "5.0.0-alpha.6",
|
||||||
"redux": "3.7.1",
|
"redux": "3.6.0",
|
||||||
"redux-thunk": "2.2.0",
|
"redux-thunk": "2.2.0",
|
||||||
"style-loader": "0.18.2",
|
"style-loader": "0.17.0",
|
||||||
"typescript": "2.4.1",
|
"typescript": "2.3.2",
|
||||||
"url-loader": "0.5.9",
|
"url-loader": "0.5.8",
|
||||||
"webpack": "2.5.1",
|
"webpack": "2.5.1",
|
||||||
"webpack-hot-middleware": "2.18.2",
|
"webpack-hot-middleware": "2.18.0",
|
||||||
"webpack-merge": "4.1.0"
|
"webpack-merge": "4.1.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,44 +1,22 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework Condition="'$(TargetFrameworkOverride)' == ''">netcoreapp2.0</TargetFramework>
|
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||||
<TargetFramework Condition="'$(TargetFrameworkOverride)' != ''">TargetFrameworkOverride</TargetFramework>
|
|
||||||
<TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
|
<TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
|
||||||
<TypeScriptToolsVersion>Latest</TypeScriptToolsVersion>
|
<PackageTargetFallback>$(PackageTargetFallback);portable-net45+win8+wp8+wpa81;</PackageTargetFallback>
|
||||||
<IsPackable>false</IsPackable>
|
<IsPackable>false</IsPackable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup Condition="'$(TargetFrameworkOverride)' == ''">
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0-rtm-26190" />
|
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0-*" />
|
||||||
</ItemGroup>
|
<PackageReference Include="Microsoft.AspNetCore.SpaServices" Version="2.0.0-*" />
|
||||||
<ItemGroup Condition="'$(TargetFrameworkOverride)' != ''">
|
|
||||||
<PackageReference Include="Microsoft.AspNetCore" Version="2.0.0-rtm-26190" />
|
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.0.0-rtm-26190" />
|
|
||||||
<PackageReference Include="Microsoft.AspNetCore.SpaServices" Version="2.0.0-rtm-26190" />
|
|
||||||
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="2.0.0-rtm-26190" />
|
|
||||||
</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>
|
||||||
|
|
||||||
<!--/-:cnd:noEmit -->
|
<Target Name="RunWebpack" AfterTargets="ComputeFilesToPublish">
|
||||||
<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" />
|
||||||
|
|||||||
3
templates/ReactSpa/global.json
Normal file
3
templates/ReactSpa/global.json
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"sdk": { "version": "sdkVersionInjectedHere" }
|
||||||
|
}
|
||||||
3365
templates/ReactSpa/npm-shrinkwrap.json
generated
3365
templates/ReactSpa/npm-shrinkwrap.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,32 +1,31 @@
|
|||||||
{
|
{
|
||||||
"name": "WebApplicationBasic",
|
"name": "WebApplicationBasic",
|
||||||
"private": true,
|
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/history": "4.6.0",
|
"@types/history": "4.5.1",
|
||||||
"@types/react": "15.0.35",
|
"@types/react": "15.0.24",
|
||||||
"@types/react-dom": "15.5.1",
|
"@types/react-dom": "15.5.0",
|
||||||
"@types/react-router": "4.0.12",
|
"@types/react-router": "4.0.9",
|
||||||
"@types/webpack-env": "1.13.0",
|
"@types/webpack-env": "1.13.0",
|
||||||
"aspnet-webpack": "^1.0.29",
|
"aspnet-webpack": "^1.0.29",
|
||||||
"aspnet-webpack-react": "^3.0.0-beta",
|
"aspnet-webpack-react": "^2.0.0",
|
||||||
"awesome-typescript-loader": "3.2.1",
|
"awesome-typescript-loader": "3.1.3",
|
||||||
"bootstrap": "3.3.7",
|
"bootstrap": "3.3.7",
|
||||||
"css-loader": "0.28.4",
|
"css-loader": "0.28.1",
|
||||||
"event-source-polyfill": "0.0.9",
|
"event-source-polyfill": "0.0.9",
|
||||||
"extract-text-webpack-plugin": "2.1.2",
|
"extract-text-webpack-plugin": "2.1.0",
|
||||||
"file-loader": "0.11.2",
|
"file-loader": "0.11.1",
|
||||||
"isomorphic-fetch": "2.2.1",
|
"isomorphic-fetch": "2.2.1",
|
||||||
"jquery": "3.2.1",
|
"jquery": "3.2.1",
|
||||||
"json-loader": "0.5.4",
|
"json-loader": "0.5.4",
|
||||||
"react": "15.6.1",
|
"react": "15.5.4",
|
||||||
"react-dom": "15.6.1",
|
"react-dom": "15.5.4",
|
||||||
"react-hot-loader": "3.0.0-beta.7",
|
"react-hot-loader": "3.0.0-beta.7",
|
||||||
"react-router-dom": "4.1.1",
|
"react-router-dom": "4.1.1",
|
||||||
"style-loader": "0.18.2",
|
"style-loader": "0.17.0",
|
||||||
"typescript": "2.4.1",
|
"typescript": "2.3.2",
|
||||||
"url-loader": "0.5.9",
|
"url-loader": "0.5.8",
|
||||||
"webpack": "2.5.1",
|
"webpack": "2.5.1",
|
||||||
"webpack-hot-middleware": "2.18.2"
|
"webpack-hot-middleware": "2.18.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,44 +1,22 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework Condition="'$(TargetFrameworkOverride)' == ''">netcoreapp2.0</TargetFramework>
|
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||||
<TargetFramework Condition="'$(TargetFrameworkOverride)' != ''">TargetFrameworkOverride</TargetFramework>
|
|
||||||
<TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
|
<TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
|
||||||
<TypeScriptToolsVersion>Latest</TypeScriptToolsVersion>
|
<PackageTargetFallback>$(PackageTargetFallback);portable-net45+win8+wp8+wpa81;</PackageTargetFallback>
|
||||||
<IsPackable>false</IsPackable>
|
<IsPackable>false</IsPackable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup Condition="'$(TargetFrameworkOverride)' == ''">
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0-rtm-26190" />
|
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0-*" />
|
||||||
</ItemGroup>
|
<PackageReference Include="Microsoft.AspNetCore.SpaServices" Version="2.0.0-*" />
|
||||||
<ItemGroup Condition="'$(TargetFrameworkOverride)' != ''">
|
|
||||||
<PackageReference Include="Microsoft.AspNetCore" Version="2.0.0-rtm-26190" />
|
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.0.0-rtm-26190" />
|
|
||||||
<PackageReference Include="Microsoft.AspNetCore.SpaServices" Version="2.0.0-rtm-26190" />
|
|
||||||
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="2.0.0-rtm-26190" />
|
|
||||||
</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>
|
||||||
|
|
||||||
<!--/-:cnd:noEmit -->
|
<Target Name="RunWebpack" AfterTargets="ComputeFilesToPublish">
|
||||||
<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" />
|
||||||
|
|||||||
3
templates/VueSpa/global.json
Normal file
3
templates/VueSpa/global.json
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"sdk": { "version": "sdkVersionInjectedHere" }
|
||||||
|
}
|
||||||
2603
templates/VueSpa/npm-shrinkwrap.json
generated
2603
templates/VueSpa/npm-shrinkwrap.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "WebApplicationBasic",
|
"name": "WebApplicationBasic",
|
||||||
"private": true,
|
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/requirejs": "^2.1.28",
|
"@types/requirejs": "^2.1.28",
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ const bundleOutputDir = './wwwroot/dist';
|
|||||||
module.exports = (env) => {
|
module.exports = (env) => {
|
||||||
const isDevBuild = !(env && env.prod);
|
const isDevBuild = !(env && env.prod);
|
||||||
|
|
||||||
|
const bundleOutputDir = './wwwroot/dist';
|
||||||
return [{
|
return [{
|
||||||
stats: { modules: false },
|
stats: { modules: false },
|
||||||
context: __dirname,
|
context: __dirname,
|
||||||
|
|||||||
@@ -11,29 +11,26 @@ 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}';
|
|
||||||
|
|
||||||
const dotNetPackages = {
|
// To support the "dotnet new" templates, we want to bundle prebuilt dist dev-mode files, because "dotnet new" can't auto-run
|
||||||
builtIn: 'Microsoft.DotNet.Web.Spa.ProjectTemplates',
|
// webpack on project creation. Note that these script entries are *not* the same as the project's usual prepublish
|
||||||
extra: 'Microsoft.AspNetCore.SpaTemplates'
|
// scripts, because here we want dev-mode builds (e.g., to support HMR), not prod-mode builds.
|
||||||
|
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
|
||||||
@@ -46,7 +43,7 @@ function writeFileEnsuringDirExists(root: string, filename: string, contents: st
|
|||||||
fs.writeFileSync(fullPath, contents);
|
fs.writeFileSync(fullPath, contents);
|
||||||
}
|
}
|
||||||
|
|
||||||
function listFilesExcludingGitignored(root: string): string[] {
|
function listFilesExcludingGitignored(root: string, forceInclusion: RegExp): 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).
|
||||||
@@ -55,23 +52,21 @@ function listFilesExcludingGitignored(root: string): string[] {
|
|||||||
? 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));
|
.filter(fn => gitignoreEvaluator.accepts(fn) || (forceInclusion && forceInclusion.test(fn)));
|
||||||
}
|
}
|
||||||
|
|
||||||
function applyContentReplacements(sourceContent: Buffer, contentReplacements: { from: RegExp, to: string }[]) {
|
function writeTemplate(sourceRoot: string, destRoot: string, contentReplacements: { from: RegExp, to: string }[], filenameReplacements: { from: RegExp, to: string }[], forceInclusion: RegExp) {
|
||||||
|
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);
|
||||||
});
|
});
|
||||||
|
|
||||||
return new Buffer(sourceText, 'utf8');
|
sourceContent = 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
|
||||||
@@ -91,22 +86,6 @@ 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);
|
||||||
@@ -116,11 +95,12 @@ function buildYeomanNpmPackage(outputRoot: string) {
|
|||||||
{ from: /.*\.csproj$/, to: 'tokenreplace-namePascalCase.csproj' }
|
{ from: /.*\.csproj$/, to: 'tokenreplace-namePascalCase.csproj' }
|
||||||
];
|
];
|
||||||
const contentReplacements = [
|
const contentReplacements = [
|
||||||
// Currently, there are none
|
// global.json items
|
||||||
|
{ 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);
|
writeTemplate(templateConfig.dir, outputDir, contentReplacements, filenameReplacements, commonForceInclusionRegex);
|
||||||
});
|
});
|
||||||
|
|
||||||
// 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)
|
||||||
@@ -132,17 +112,7 @@ function buildYeomanNpmPackage(outputRoot: string) {
|
|||||||
rimraf.sync(tempRoot);
|
rimraf.sync(tempRoot);
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildDotNetNewNuGetPackages(outputDir: string) {
|
function buildDotNetNewNuGetPackage() {
|
||||||
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);
|
||||||
|
|
||||||
@@ -155,13 +125,8 @@ function buildDotNetNewNuGetPackage(packageId: string) {
|
|||||||
];
|
];
|
||||||
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);
|
writeTemplate(templateConfig.dir, templateOutputDir, contentReplacements, filenameReplacements, commonForceInclusionRegex);
|
||||||
|
|
||||||
// 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');
|
||||||
@@ -169,10 +134,10 @@ function buildDotNetNewNuGetPackage(packageId: string) {
|
|||||||
|
|
||||||
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: `${packageId}.${templateConfig.dotNetNewId}`,
|
groupIdentity: `Microsoft.AspNetCore.SpaTemplates.${templateConfig.dotNetNewId}`,
|
||||||
identity: `${packageId}.${templateConfig.dotNetNewId}.CSharp`,
|
identity: `Microsoft.AspNetCore.SpaTemplates.${templateConfig.dotNetNewId}.CSharp`,
|
||||||
name: `ASP.NET Core with ${templateConfig.displayName}`,
|
name: `MVC 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()}`,
|
||||||
@@ -183,99 +148,46 @@ function buildDotNetNewNuGetPackage(packageId: string) {
|
|||||||
exclude: ['.template.config/**']
|
exclude: ['.template.config/**']
|
||||||
}],
|
}],
|
||||||
symbols: {
|
symbols: {
|
||||||
TargetFrameworkOverride: {
|
sdkVersion: {
|
||||||
type: 'parameter',
|
type: 'bind',
|
||||||
description: 'Overrides the target framework',
|
binding: 'dotnet-cli-version',
|
||||||
replaces: 'TargetFrameworkOverride',
|
replaces: 'sdkVersionInjectedHere'
|
||||||
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: 'netcoreapp2.0',
|
choice: "netcoreapp1.1",
|
||||||
description: 'Target netcoreapp2.0'
|
description: "Target netcoreapp1.1"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
replaces: 'netcoreapp2.0',
|
defaultValue: "netcoreapp1.1"
|
||||||
defaultValue: 'netcoreapp2.0'
|
|
||||||
},
|
|
||||||
HostIdentifier: {
|
|
||||||
type: 'bind',
|
|
||||||
binding: 'HostIdentifier'
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
tags: { language: 'C#', type: 'project' },
|
tags: { language: 'C#', type: 'project' },
|
||||||
postActions: [
|
|
||||||
/*
|
|
||||||
// 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({
|
||||||
$schema: 'http://json.schemastore.org/dotnetcli.host',
|
symbolInfo: {}
|
||||||
symbolInfo: {
|
|
||||||
TargetFrameworkOverride: {
|
|
||||||
isHidden: 'true',
|
|
||||||
longName: 'target-framework-override',
|
|
||||||
shortName: ''
|
|
||||||
},
|
|
||||||
Framework: {
|
|
||||||
longName: 'framework'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, 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({
|
||||||
$schema: 'http://json.schemastore.org/vs-2017.3.host',
|
name: { text: templateConfig.displayName },
|
||||||
name: { text: templateConfig.displayName, package: webToolsVSPackageGuid, id: localisedNameId.toString() },
|
description: { text: `Web application built with MVC ASP.NET Core and ${templateConfig.displayName}` },
|
||||||
description: { text: `A project template for creating an ASP.NET Core application with ${templateConfig.displayName}`, package: webToolsVSPackageGuid, id: localisedDescId.toString() },
|
order: 2000,
|
||||||
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));
|
||||||
});
|
});
|
||||||
|
|
||||||
// Create the .nuspec file
|
|
||||||
const yeomanPackageVersion = JSON.parse(fs.readFileSync(path.join(yeomanGeneratorSource, 'package.json'), 'utf8')).version;
|
|
||||||
const nuspecContentTemplate = fs.readFileSync(`./src/dotnetnew/${ packageId }.nuspec`);
|
|
||||||
writeFileEnsuringDirExists(outputRoot,
|
|
||||||
`${ packageId }.nuspec`,
|
|
||||||
applyContentReplacements(nuspecContentTemplate, [
|
|
||||||
{ from: /\{yeomanversion\}/g, to: yeomanPackageVersion },
|
|
||||||
{ from: /\{buildnumber\}/g, to: getBuildNumber() },
|
|
||||||
])
|
|
||||||
);
|
|
||||||
|
|
||||||
// Invoke NuGet to create the final package
|
// Invoke NuGet to create the final package
|
||||||
|
const yeomanPackageVersion = JSON.parse(fs.readFileSync(path.join(yeomanGeneratorSource, 'package.json'), 'utf8')).version;
|
||||||
|
writeTemplate('./src/dotnetnew', outputRoot, [
|
||||||
|
{ from: /\{version\}/g, to: yeomanPackageVersion },
|
||||||
|
], [], null);
|
||||||
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) {
|
||||||
@@ -292,7 +204,21 @@ function buildDotNetNewNuGetPackage(packageId: string) {
|
|||||||
return glob.sync(path.join(outputRoot, './*.nupkg'))[0];
|
return glob.sync(path.join(outputRoot, './*.nupkg'))[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
function runPrepublishScripts(rootDir: string, scripts: string[]) {
|
function runAllPrepublishScripts() {
|
||||||
|
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 }`);
|
||||||
@@ -307,8 +233,12 @@ 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);
|
||||||
buildDotNetNewNuGetPackages(artifactsDir);
|
const dotNetNewNupkgPath = buildDotNetNewNuGetPackage();
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
|||||||
@@ -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>{yeomanversion}</version>
|
<version>{version}</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>
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
<?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>
|
|
||||||
@@ -6,6 +6,7 @@ import * as glob from 'glob';
|
|||||||
import * as semver from 'semver';
|
import * as semver from 'semver';
|
||||||
import * as chalk from 'chalk';
|
import * as chalk from 'chalk';
|
||||||
import { execSync } from 'child_process';
|
import { execSync } from 'child_process';
|
||||||
|
import npmWhich = require('npm-which');
|
||||||
const yosay = require('yosay');
|
const yosay = require('yosay');
|
||||||
const toPascalCase = require('to-pascal-case');
|
const toPascalCase = require('to-pascal-case');
|
||||||
const isWindows = /^win/.test(process.platform);
|
const isWindows = /^win/.test(process.platform);
|
||||||
@@ -164,6 +165,15 @@ class MyGenerator extends yeoman.Base {
|
|||||||
}
|
}
|
||||||
|
|
||||||
installingDeps() {
|
installingDeps() {
|
||||||
|
// If available, restore dependencies using Yarn instead of NPM
|
||||||
|
const yarnPath = getPathToExecutable('yarn');
|
||||||
|
if (!!yarnPath) {
|
||||||
|
this.log('Will restore NPM dependencies using \'yarn\' installed at ' + yarnPath);
|
||||||
|
this.npmInstall = (pkgs, options, cb) => {
|
||||||
|
return (this as any).runInstall(yarnPath, pkgs, options, cb);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
this.installDependencies({
|
this.installDependencies({
|
||||||
npm: true,
|
npm: true,
|
||||||
bower: false,
|
bower: false,
|
||||||
@@ -176,6 +186,14 @@ class MyGenerator extends yeoman.Base {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getPathToExecutable(executableName: string) {
|
||||||
|
try {
|
||||||
|
return npmWhich(__dirname).sync(executableName);
|
||||||
|
} catch(ex) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function assertNpmVersionIsAtLeast(minVersion: string) {
|
function assertNpmVersionIsAtLeast(minVersion: string) {
|
||||||
const runningVersion = execSync('npm -v').toString();
|
const runningVersion = execSync('npm -v').toString();
|
||||||
if (!semver.gte(runningVersion, minVersion, /* loose */ true)) {
|
if (!semver.gte(runningVersion, minVersion, /* loose */ true)) {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "generator-aspnetcore-spa",
|
"name": "generator-aspnetcore-spa",
|
||||||
"version": "0.9.4",
|
"version": "0.9.3",
|
||||||
"description": "Single-Page App templates for ASP.NET Core",
|
"description": "Single-Page App templates for ASP.NET Core",
|
||||||
"author": "Microsoft",
|
"author": "Microsoft",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
@@ -21,6 +21,7 @@
|
|||||||
"chalk": "^1.1.3",
|
"chalk": "^1.1.3",
|
||||||
"glob": "^7.0.3",
|
"glob": "^7.0.3",
|
||||||
"node-uuid": "^1.4.7",
|
"node-uuid": "^1.4.7",
|
||||||
|
"npm-which": "^3.0.1",
|
||||||
"to-pascal-case": "^1.0.0",
|
"to-pascal-case": "^1.0.0",
|
||||||
"yeoman-generator": "^0.20.2",
|
"yeoman-generator": "^0.20.2",
|
||||||
"yeoman-option-or-prompt": "^1.0.2",
|
"yeoman-option-or-prompt": "^1.0.2",
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
<Project>
|
<!-- This file may be overwritten by automation. Only values allowed here are VersionPrefix and VersionSuffix. -->
|
||||||
|
<Project>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<VersionPrefix>2.0.4</VersionPrefix>
|
<VersionPrefix>2.0.0</VersionPrefix>
|
||||||
<VersionSuffix>rtm</VersionSuffix>
|
<VersionSuffix>preview2</VersionSuffix>
|
||||||
<PackageVersion Condition="'$(IsFinalBuild)' == 'true' AND '$(VersionSuffix)' == 'rtm' ">$(VersionPrefix)</PackageVersion>
|
|
||||||
<PackageVersion Condition="'$(IsFinalBuild)' == 'true' AND '$(VersionSuffix)' != 'rtm' ">$(VersionPrefix)-$(VersionSuffix)-final</PackageVersion>
|
|
||||||
<VersionSuffix Condition="'$(VersionSuffix)' != '' And '$(BuildNumber)' != ''">$(VersionSuffix)-$(BuildNumber)</VersionSuffix>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
Reference in New Issue
Block a user