mirror of
https://github.com/aspnet/JavaScriptServices.git
synced 2025-12-23 01:58:29 +00:00
Compare commits
72 Commits
rel/2.0.0
...
angular-an
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c0c47e3def | ||
|
|
e5f1299239 | ||
|
|
c922eee1d6 | ||
|
|
8b243e8cc7 | ||
|
|
28920c7691 | ||
|
|
412ec1b427 | ||
|
|
c62a3b491c | ||
|
|
559832bb6d | ||
|
|
45d645931b | ||
|
|
8d6119f31d | ||
|
|
0291686b20 | ||
|
|
7c52be5e42 | ||
|
|
900e9ca835 | ||
|
|
ad758b1060 | ||
|
|
cd9ad38a99 | ||
|
|
e057cb35ec | ||
|
|
eea2066a6d | ||
|
|
d6ae8829b6 | ||
|
|
a94ac6f37e | ||
|
|
a40adab38d | ||
|
|
c2a284d5b8 | ||
|
|
fc398d602a | ||
|
|
90c59ff4e7 | ||
|
|
a7e715c88f | ||
|
|
6dddc9d01d | ||
|
|
5ed1a35ce0 | ||
|
|
680ba7497a | ||
|
|
287c10fd2e | ||
|
|
63f7ac9330 | ||
|
|
d2858beaa1 | ||
|
|
5f1450c9ba | ||
|
|
c83605baff | ||
|
|
fc12d722b8 | ||
|
|
372e597f34 | ||
|
|
3715ec7c3f | ||
|
|
d2eaa36372 | ||
|
|
169ef12cd8 | ||
|
|
f5d58f3f2e | ||
|
|
c2e4d4f261 | ||
|
|
50481fe23f | ||
|
|
895a61160e | ||
|
|
b8b769aa74 | ||
|
|
c4aad6bcab | ||
|
|
27f1d07d21 | ||
|
|
0cb14a3c68 | ||
|
|
2457b4ee5d | ||
|
|
789ea5a320 | ||
|
|
a902874754 | ||
|
|
f43ea777eb | ||
|
|
c79db4e8e2 | ||
|
|
d1198aeab2 | ||
|
|
9528dd7432 | ||
|
|
b8c006a3e9 | ||
|
|
a9ddf1413f | ||
|
|
8b37dc8561 | ||
|
|
7b07fb66eb | ||
|
|
cdb04c74f9 | ||
|
|
a74941e3c8 | ||
|
|
a0a710a0df | ||
|
|
d5f5ad7fdc | ||
|
|
dab0faea66 | ||
|
|
2df0febfba | ||
|
|
e65ecebac6 | ||
|
|
bb0727c34c | ||
|
|
4903e12373 | ||
|
|
56c806b34e | ||
|
|
8acba88160 | ||
|
|
b434eefd83 | ||
|
|
44360b6955 | ||
|
|
72b1e627b0 | ||
|
|
781c5dc37c | ||
|
|
c2f63f21fd |
11
.gitignore
vendored
11
.gitignore
vendored
@@ -29,14 +29,7 @@ nuget.exe
|
||||
npm-debug.log
|
||||
/.build/
|
||||
|
||||
# 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
|
||||
# file which gets renamed after the files are copied. And so any files that need to be excluded in the source
|
||||
# repo have to be excluded here.
|
||||
/templates/*/node_modules/
|
||||
/templates/*/wwwroot/dist/
|
||||
/templates/*/ClientApp/dist/
|
||||
/templates/*/yarn.lock
|
||||
.vscode/
|
||||
|
||||
/templates/*/Properties/launchSettings.json
|
||||
global.json
|
||||
korebuild-lock.txt
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
<configuration>
|
||||
<packageSources>
|
||||
<clear />
|
||||
<add key="AspNetCore" value="https://dotnet.myget.org/F/aspnetcore-ci-release/api/v3/index.json" />
|
||||
<add key="AspNetCore" value="https://dotnet.myget.org/F/aspnetcore-ci-dev/api/v3/index.json" />
|
||||
<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>
|
||||
</configuration>
|
||||
</configuration>
|
||||
12
appveyor.yml
12
appveyor.yml
@@ -1,10 +1,10 @@
|
||||
init:
|
||||
init:
|
||||
- git config --global core.autocrlf true
|
||||
install:
|
||||
- ps: Install-Product node 6.9.2 x64
|
||||
# .NET Core SDK binaries
|
||||
# Download .NET Core 2.0 Preview 3 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://dotnetcli.azureedge.net/dotnet/Sdk/2.0.0-preview3-006857/dotnet-sdk-2.0.0-preview3-006857-win-x64.zip"
|
||||
- ps: $env:DOTNET_INSTALL_DIR = "$pwd\.dotnetsdk"
|
||||
- ps: mkdir $env:DOTNET_INSTALL_DIR -Force | Out-Null
|
||||
- ps: $tempFileCurrent = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName())
|
||||
@@ -18,13 +18,15 @@ build_script:
|
||||
- npm run build
|
||||
- ps: Pop-Location
|
||||
artifacts:
|
||||
- path: templates\package-builder\dist\artifacts\generator-aspnetcore-spa.tar.gz
|
||||
name: generator-aspnetcore-spa
|
||||
- path: templates\package-builder\dist\artifacts\*.nupkg
|
||||
- path: templates\package-builder\artifacts\*.nupkg
|
||||
name: Microsoft.AspNetCore.SpaTemplates
|
||||
type: NuGetPackage
|
||||
# - ps: .\build.ps1
|
||||
clone_depth: 1
|
||||
environment:
|
||||
global:
|
||||
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
|
||||
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
||||
test_script:
|
||||
- dotnet restore
|
||||
- ps: Push-Location
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
@ECHO OFF
|
||||
PowerShell -NoProfile -NoLogo -ExecutionPolicy unrestricted -Command "[System.Threading.Thread]::CurrentThread.CurrentCulture = ''; [System.Threading.Thread]::CurrentThread.CurrentUICulture = '';& '%~dp0build.ps1' %*; exit $LASTEXITCODE"
|
||||
PowerShell -NoProfile -NoLogo -ExecutionPolicy unrestricted -Command "[System.Threading.Thread]::CurrentThread.CurrentCulture = ''; [System.Threading.Thread]::CurrentThread.CurrentUICulture = '';& '%~dp0build.ps1' %*; exit $LASTEXITCODE"
|
||||
|
||||
218
build.ps1
218
build.ps1
@@ -1,67 +1,177 @@
|
||||
$ErrorActionPreference = "Stop"
|
||||
#!/usr/bin/env powershell
|
||||
#requires -version 4
|
||||
|
||||
function DownloadWithRetry([string] $url, [string] $downloadLocation, [int] $retries)
|
||||
{
|
||||
while($true)
|
||||
{
|
||||
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
|
||||
<#
|
||||
.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.xml.
|
||||
|
||||
.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 XML file. It is optional, and the configuration values in it are optional as well.
|
||||
|
||||
.EXAMPLE
|
||||
Example config file:
|
||||
```xml
|
||||
<!-- version.xml -->
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<KoreBuildChannel>dev</KoreBuildChannel>
|
||||
<KoreBuildToolsSource>https://aspnetcore.blob.core.windows.net/buildtools</KoreBuildToolsSource>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
```
|
||||
#>
|
||||
[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 = (Join-Path $PSScriptRoot 'version.xml'),
|
||||
[Parameter(ValueFromRemainingArguments = $true)]
|
||||
[string[]]$MSBuildArgs
|
||||
)
|
||||
|
||||
Set-StrictMode -Version 2
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
#
|
||||
# Functions
|
||||
#
|
||||
|
||||
function Get-KoreBuild {
|
||||
|
||||
$lockFile = Join-Path $Path 'korebuild-lock.txt'
|
||||
|
||||
if (!(Test-Path $lockFile) -or $Update) {
|
||||
Get-RemoteFile "$ToolsSource/korebuild/channels/$Channel/latest.txt" $lockFile
|
||||
}
|
||||
|
||||
$version = Get-Content $lockFile | Where-Object { $_ -like 'version:*' } | Select-Object -first 1
|
||||
if (!$version) {
|
||||
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)) {
|
||||
Write-Host -ForegroundColor Magenta "Downloading KoreBuild $version"
|
||||
New-Item -ItemType Directory -Path $korebuildPath | Out-Null
|
||||
$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
|
||||
{
|
||||
$exception = $_.Exception
|
||||
throw $exception
|
||||
else {
|
||||
# Fallback to old approach for old installations of PowerShell
|
||||
Add-Type -AssemblyName System.IO.Compression.FileSystem
|
||||
[System.IO.Compression.ZipFile]::ExtractToDirectory($tmpfile, $korebuildPath)
|
||||
}
|
||||
}
|
||||
catch {
|
||||
Remove-Item -Recurse -Force $korebuildPath -ErrorAction Ignore
|
||||
throw
|
||||
}
|
||||
finally {
|
||||
Remove-Item $tmpfile -ErrorAction Ignore
|
||||
}
|
||||
}
|
||||
|
||||
return $korebuildPath
|
||||
}
|
||||
|
||||
cd $PSScriptRoot
|
||||
|
||||
$repoFolder = $PSScriptRoot
|
||||
$env:REPO_FOLDER = $repoFolder
|
||||
|
||||
$koreBuildZip="https://github.com/aspnet/KoreBuild/archive/rel/2.0.0.zip"
|
||||
if ($env:KOREBUILD_ZIP)
|
||||
{
|
||||
$koreBuildZip=$env:KOREBUILD_ZIP
|
||||
function Join-Paths([string]$path, [string[]]$childPaths) {
|
||||
$childPaths | ForEach-Object { $path = Join-Path $path $_ }
|
||||
return $path
|
||||
}
|
||||
|
||||
$buildFolder = ".build"
|
||||
$buildFile="$buildFolder\KoreBuild.ps1"
|
||||
|
||||
if (!(Test-Path $buildFolder)) {
|
||||
Write-Host "Downloading KoreBuild from $koreBuildZip"
|
||||
|
||||
$tempFolder=$env:TEMP + "\KoreBuild-" + [guid]::NewGuid()
|
||||
New-Item -Path "$tempFolder" -Type directory | Out-Null
|
||||
|
||||
$localZipFile="$tempFolder\korebuild.zip"
|
||||
|
||||
DownloadWithRetry -url $koreBuildZip -downloadLocation $localZipFile -retries 6
|
||||
|
||||
Add-Type -AssemblyName System.IO.Compression.FileSystem
|
||||
[System.IO.Compression.ZipFile]::ExtractToDirectory($localZipFile, $tempFolder)
|
||||
|
||||
New-Item -Path "$buildFolder" -Type directory | Out-Null
|
||||
copy-item "$tempFolder\**\build\*" $buildFolder -Recurse
|
||||
|
||||
# Cleanup
|
||||
if (Test-Path $tempFolder) {
|
||||
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'."
|
||||
}
|
||||
|
||||
&"$buildFile" @args
|
||||
#
|
||||
# Main
|
||||
#
|
||||
|
||||
# Load configuration or set defaults
|
||||
|
||||
if (Test-Path $ConfigFile) {
|
||||
[xml] $config = Get-Content $ConfigFile
|
||||
if (!($Channel)) { [string] $Channel = Select-Xml -Xml $config -XPath '/Project/PropertyGroup/KoreBuildChannel' }
|
||||
if (!($ToolsSource)) { [string] $ToolsSource = Select-Xml -Xml $config -XPath '/Project/PropertyGroup/KoreBuildToolsSource' }
|
||||
}
|
||||
|
||||
if (!$DotNetHome) {
|
||||
$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
|
||||
}
|
||||
|
||||
227
build.sh
227
build.sh
@@ -1,46 +1,199 @@
|
||||
#!/usr/bin/env bash
|
||||
repoFolder="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
cd $repoFolder
|
||||
|
||||
koreBuildZip="https://github.com/aspnet/KoreBuild/archive/rel/2.0.0.zip"
|
||||
if [ ! -z $KOREBUILD_ZIP ]; then
|
||||
koreBuildZip=$KOREBUILD_ZIP
|
||||
fi
|
||||
set -euo pipefail
|
||||
|
||||
buildFolder=".build"
|
||||
buildFile="$buildFolder/KoreBuild.sh"
|
||||
#
|
||||
# variables
|
||||
#
|
||||
|
||||
if test ! -d $buildFolder; then
|
||||
echo "Downloading KoreBuild from $koreBuildZip"
|
||||
RESET="\033[0m"
|
||||
RED="\033[0;31m"
|
||||
MAGENTA="\033[0;95m"
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
[ -z "${DOTNET_HOME:-}" ] && DOTNET_HOME="$HOME/.dotnet"
|
||||
config_file="$DIR/version.xml"
|
||||
verbose=false
|
||||
update=false
|
||||
repo_path="$DIR"
|
||||
channel=''
|
||||
tools_source=''
|
||||
|
||||
tempFolder="/tmp/KoreBuild-$(uuidgen)"
|
||||
mkdir $tempFolder
|
||||
#
|
||||
# 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> TThe path to the configuration file that stores values. Defaults to version.xml."
|
||||
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."
|
||||
|
||||
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
|
||||
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
|
||||
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}$*${RESET}" 1>&2
|
||||
}
|
||||
|
||||
__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
|
||||
}
|
||||
|
||||
__read_dom () { local IFS=\> ; read -r -d \< ENTITY CONTENT ;}
|
||||
|
||||
#
|
||||
# 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
|
||||
;;
|
||||
-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
|
||||
|
||||
$buildFile -r $repoFolder "$@"
|
||||
if ! __machine_has curl && ! __machine_has wget; then
|
||||
__error 'Missing required command. Either wget or curl is required.'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -f "$config_file" ]; then
|
||||
comment=false
|
||||
while __read_dom; do
|
||||
if [ "$comment" = true ]; then [[ $CONTENT == *'-->'* ]] && comment=false ; continue; fi
|
||||
if [[ $ENTITY == '!--'* ]]; then comment=true; continue; fi
|
||||
if [ -z "$channel" ] && [[ $ENTITY == "KoreBuildChannel" ]]; then channel=$CONTENT; fi
|
||||
if [ -z "$tools_source" ] && [[ $ENTITY == "KoreBuildToolsSource" ]]; then tools_source=$CONTENT; fi
|
||||
done < "$config_file"
|
||||
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,6 +1,6 @@
|
||||
<Project>
|
||||
<Import Project="dependencies.props" />
|
||||
<Import Project="..\version.props" />
|
||||
<Import Project="..\version.xml" />
|
||||
|
||||
<PropertyGroup>
|
||||
<Product>Microsoft ASP.NET Core</Product>
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<AspNetCoreVersion>2.0.0-*</AspNetCoreVersion>
|
||||
<AutoMapperVersion>5.0.2</AutoMapperVersion>
|
||||
<InternalAspNetCoreSdkVersion>2.0.1-*</InternalAspNetCoreSdkVersion>
|
||||
<AspNetCoreVersion>2.1.0-*</AspNetCoreVersion>
|
||||
<InternalAspNetCoreSdkVersion>2.1.1-*</InternalAspNetCoreSdkVersion>
|
||||
<JsonNetVersion>10.0.1</JsonNetVersion>
|
||||
<NETStandardImplicitPackageVersion>2.0.0-*</NETStandardImplicitPackageVersion>
|
||||
<NETStandardLibraryNETFrameworkVersion>2.0.0-*</NETStandardLibraryNETFrameworkVersion>
|
||||
|
||||
@@ -21,7 +21,6 @@ namespace ConsoleApplication
|
||||
// Since .NET Core 1.1, the HTTP hosting model has become basically as fast as the Socket hosting model
|
||||
//options.UseSocketHosting();
|
||||
|
||||
options.ProjectPath = Directory.GetCurrentDirectory();
|
||||
options.WatchFileExtensions = new string[] {}; // Don't watch anything
|
||||
});
|
||||
var serviceProvider = services.BuildServiceProvider();
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using Microsoft.AspNetCore.NodeServices.HostingModels;
|
||||
using Microsoft.Extensions.Logging;
|
||||
@@ -34,14 +35,18 @@ namespace Microsoft.AspNetCore.NodeServices
|
||||
InvocationTimeoutMilliseconds = DefaultInvocationTimeoutMilliseconds;
|
||||
WatchFileExtensions = (string[])DefaultWatchFileExtensions.Clone();
|
||||
|
||||
// In an ASP.NET environment, we can use the IHostingEnvironment data to auto-populate a few
|
||||
// things that you'd otherwise have to specify manually
|
||||
var hostEnv = serviceProvider.GetService<IHostingEnvironment>();
|
||||
if (hostEnv != null)
|
||||
{
|
||||
// In an ASP.NET environment, we can use the IHostingEnvironment data to auto-populate a few
|
||||
// things that you'd otherwise have to specify manually
|
||||
ProjectPath = hostEnv.ContentRootPath;
|
||||
EnvironmentVariables["NODE_ENV"] = hostEnv.IsDevelopment() ? "development" : "production"; // De-facto standard values for Node
|
||||
}
|
||||
else
|
||||
{
|
||||
ProjectPath = Directory.GetCurrentDirectory();
|
||||
}
|
||||
|
||||
var applicationLifetime = serviceProvider.GetService<IApplicationLifetime>();
|
||||
if (applicationLifetime != null)
|
||||
|
||||
@@ -50,6 +50,7 @@ namespace Microsoft.AspNetCore.NodeServices.HostingModels
|
||||
options.DebuggingPort)
|
||||
{
|
||||
_client = new HttpClient();
|
||||
_client.Timeout = TimeSpan.FromMilliseconds(options.InvocationTimeoutMilliseconds + 1000);
|
||||
}
|
||||
|
||||
private static string MakeCommandLineOptions(int port)
|
||||
|
||||
@@ -317,7 +317,7 @@ module.exports = {
|
||||
|
||||
## Hosting models
|
||||
|
||||
NodeServices has a pluggable hosting/transport mechanism, because it is an abstraction over various possible ways to invoke Node.js from .NET. This allows more high-level facilities (e.g., for Angular prerendering) to be agnostic to the details of launching Node and communicating it - those high-level facilities can just trust that *somehow* we can invoke code in Node for them.
|
||||
NodeServices has a pluggable hosting/transport mechanism, because it is an abstraction over various possible ways to invoke Node.js from .NET. This allows more high-level facilities (e.g., for Angular prerendering) to be agnostic to the details of launching Node and communicating with it - those high-level facilities can just trust that *somehow* we can invoke code in Node for them.
|
||||
|
||||
Using this abstraction, we could run Node inside the .NET process, in a separate process on the same machine, or even on a different machine altogether. At the time of writing, all the built-in hosting mechanisms work by launching Node as a separate process on the same machine as your .NET code.
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.Threading;
|
||||
using Microsoft.AspNetCore.NodeServices;
|
||||
using Microsoft.AspNetCore.SpaServices.Webpack;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
|
||||
namespace Microsoft.AspNetCore.Builder
|
||||
{
|
||||
@@ -14,6 +15,12 @@ namespace Microsoft.AspNetCore.Builder
|
||||
{
|
||||
private const string DefaultConfigFile = "webpack.config.js";
|
||||
|
||||
private static readonly JsonSerializerSettings jsonSerializerSettings = new JsonSerializerSettings
|
||||
{
|
||||
ContractResolver = new CamelCasePropertyNamesContractResolver(),
|
||||
TypeNameHandling = TypeNameHandling.None
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Enables Webpack dev middleware support. This hosts an instance of the Webpack compiler in memory
|
||||
/// in your application so that you can always serve up-to-date Webpack-built resources without having
|
||||
@@ -87,7 +94,7 @@ namespace Microsoft.AspNetCore.Builder
|
||||
};
|
||||
var devServerInfo =
|
||||
nodeServices.InvokeExportAsync<WebpackDevServerInfo>(nodeScript.FileName, "createWebpackDevServer",
|
||||
JsonConvert.SerializeObject(devServerOptions)).Result;
|
||||
JsonConvert.SerializeObject(devServerOptions, jsonSerializerSettings)).Result;
|
||||
|
||||
// If we're talking to an older version of aspnet-webpack, it will return only a single PublicPath,
|
||||
// not an array of PublicPaths. Handle that scenario.
|
||||
|
||||
5
src/Microsoft.AspNetCore.SpaServices/npm/aspnet-angular/.gitignore
vendored
Normal file
5
src/Microsoft.AspNetCore.SpaServices/npm/aspnet-angular/.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
/node_modules/
|
||||
**/*.js
|
||||
**/*.d.ts
|
||||
**/*.metadata.json
|
||||
/compiled
|
||||
@@ -0,0 +1,3 @@
|
||||
!/*.js
|
||||
!/*.d.ts
|
||||
/compiled
|
||||
@@ -0,0 +1,12 @@
|
||||
Copyright (c) .NET Foundation. All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||
these files except in compliance with the License. You may obtain a copy of the
|
||||
License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
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
|
||||
CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations under the License.
|
||||
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"name": "aspnet-angular",
|
||||
"version": "0.1.1",
|
||||
"description": "Helpers for using Angular in ASP.NET Core projects",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"prepublish": "rimraf *.d.ts && ngc && echo 'Finished building NPM package \"aspnet-angular\"'",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/aspnet/JavaScriptServices.git"
|
||||
},
|
||||
"author": "Microsoft",
|
||||
"license": "Apache-2.0",
|
||||
"bugs": {
|
||||
"url": "https://github.com/aspnet/JavaScriptServices/issues"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular/common": "^4.3.2",
|
||||
"@angular/compiler": "^4.3.2",
|
||||
"@angular/compiler-cli": "^4.3.2",
|
||||
"@angular/core": "^4.3.2",
|
||||
"@angular/http": "^4.3.2",
|
||||
"@angular/platform-browser": "^4.3.2",
|
||||
"rimraf": "^2.6.1",
|
||||
"rxjs": "^5.4.2",
|
||||
"zone.js": "^0.8.16"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/core": "^4.2.5 || ^5.0.0-beta"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
import { Provider, NgModule, Inject } from '@angular/core';
|
||||
import { Headers, Http, ResponseOptions, RequestOptionsArgs, Response } from '@angular/http';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import 'rxjs/add/observable/of';
|
||||
import 'rxjs/add/operator/map';
|
||||
const globalSerializedStateKey = 'HTTP_STATE_TRANSFER';
|
||||
const backingStoreDIToken = 'HTTP_STATE_BACKING_STORE';
|
||||
|
||||
export interface CacheOptions {
|
||||
permanent: boolean;
|
||||
}
|
||||
|
||||
export interface CachedHttpResponse {
|
||||
headers: { [name: string]: any } | null;
|
||||
status: number;
|
||||
statusText: string | null;
|
||||
text: string;
|
||||
url: string;
|
||||
}
|
||||
|
||||
export type BackingStore = { [key: string]: CachedHttpResponse };
|
||||
|
||||
export class HttpWithStateTransfer {
|
||||
private backingStore: BackingStore;
|
||||
private http: Http;
|
||||
|
||||
constructor(@Inject(Http) http: Http, @Inject(backingStoreDIToken) backingStore: BackingStore) {
|
||||
this.http = http;
|
||||
this.backingStore = backingStore;
|
||||
}
|
||||
|
||||
public stateForTransfer(): any {
|
||||
return { [globalSerializedStateKey]: this.backingStore };
|
||||
}
|
||||
|
||||
public get(url: string, options?: CacheOptions, requestOptions?: RequestOptionsArgs): Observable<Response> {
|
||||
return this.getCachedResponse(/* cacheKey */ url, () => this.http.get(url, requestOptions), options);
|
||||
}
|
||||
|
||||
private getCachedResponse(cacheKey: string, provider: () => Observable<Response>, options?: CacheOptions): Observable<Response> {
|
||||
// By default, the cache is only used for the *first* client-side read. So, we're only performing
|
||||
// a one-time transfer of server-side response to the client. If you want to keep and reuse cached
|
||||
// responses continually during server-side and client-side execution, set 'permanent' to 'true.
|
||||
const isClient = typeof window !== 'undefined';
|
||||
const isPermanent = options && options.permanent;
|
||||
|
||||
const allowReadFromCache = isClient || isPermanent;
|
||||
if (allowReadFromCache && this.backingStore.hasOwnProperty(cacheKey)) {
|
||||
const cachedValue = this.backingStore[cacheKey];
|
||||
if (!isPermanent) {
|
||||
delete this.backingStore[cacheKey];
|
||||
}
|
||||
return Observable.of(new Response(new ResponseOptions({
|
||||
body: cachedValue.text,
|
||||
headers: new Headers(cachedValue.headers),
|
||||
status: cachedValue.status,
|
||||
url: cachedValue.url
|
||||
})));
|
||||
}
|
||||
|
||||
return provider()
|
||||
.map(response => {
|
||||
const allowWriteToCache = !isClient || isPermanent;
|
||||
if (allowWriteToCache) {
|
||||
this.backingStore[cacheKey] = {
|
||||
headers: response.headers ? response.headers.toJSON() : null,
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
text: response.text(),
|
||||
url: response.url
|
||||
};
|
||||
}
|
||||
|
||||
return response;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function defaultBackingStoreFactory() {
|
||||
const transferredData = typeof window !== 'undefined' ? (window as any)[globalSerializedStateKey] : null;
|
||||
return transferredData || {};
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
providers: [
|
||||
// The backing store is a separate DI service so you could override exactly how it gets
|
||||
// transferred from server to client
|
||||
{ provide: backingStoreDIToken, useFactory: defaultBackingStoreFactory },
|
||||
|
||||
{ provide: HttpWithStateTransfer, useClass: HttpWithStateTransfer },
|
||||
]
|
||||
})
|
||||
export class HttpWithStateTransferModule {
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * from './HttpWithStateTransfer';
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"experimentalDecorators": true,
|
||||
"moduleResolution": "node",
|
||||
"module": "commonjs",
|
||||
"target": "es5",
|
||||
"declaration": true,
|
||||
"outDir": ".",
|
||||
"lib": ["es2015", "dom"]
|
||||
},
|
||||
"files": [
|
||||
"src/index.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
],
|
||||
"angularCompilerOptions": {
|
||||
"genDir": "compiled"
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "aspnet-prerendering",
|
||||
"version": "2.0.6",
|
||||
"version": "3.0.1",
|
||||
"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",
|
||||
"scripts": {
|
||||
@@ -17,7 +17,7 @@
|
||||
"url": "https://github.com/aspnet/JavaScriptServices.git"
|
||||
},
|
||||
"dependencies": {
|
||||
"domain-task": "^2.0.2"
|
||||
"domain-task": "^3.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^6.0.42",
|
||||
|
||||
@@ -26,6 +26,7 @@ export function createServerRenderer(bootFunc: BootFunc): RenderToStringFunc {
|
||||
domainTasks: domainTaskCompletionPromise,
|
||||
data: customDataParameter
|
||||
};
|
||||
const absoluteBaseUrl = params.origin + params.baseUrl; // Should be same value as page's <base href>
|
||||
|
||||
// Open a new domain that can track all the async tasks involved in the app's execution
|
||||
domainTaskRun(/* code to run */ () => {
|
||||
@@ -35,7 +36,7 @@ export function createServerRenderer(bootFunc: BootFunc): RenderToStringFunc {
|
||||
bindPromiseContinuationsToDomain(domainTaskCompletionPromise, domain['active']);
|
||||
|
||||
// Make the base URL available to the 'domain-tasks/fetch' helper within this execution context
|
||||
domainTaskBaseUrl(absoluteRequestUrl);
|
||||
domainTaskBaseUrl(absoluteBaseUrl);
|
||||
|
||||
// Begin rendering, and apply a timeout
|
||||
const bootFuncPromise = bootFunc(params);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "aspnet-webpack-react",
|
||||
"version": "3.0.0-beta.1",
|
||||
"version": "3.0.0",
|
||||
"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",
|
||||
"scripts": {
|
||||
|
||||
1700
src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack/package-lock.json
generated
Normal file
1700
src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "aspnet-webpack",
|
||||
"version": "1.0.29",
|
||||
"version": "2.0.1",
|
||||
"description": "Helpers for using Webpack in ASP.NET Core projects. Works in conjunction with the Microsoft.AspNetCore.SpaServices NuGet package.",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
|
||||
@@ -5,11 +5,11 @@ import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as querystring from 'querystring';
|
||||
import { requireNewCopy } from './RequireNewCopy';
|
||||
import { hasSufficientPermissions } from './WebpackTestPermissions';
|
||||
|
||||
export type CreateDevServerResult = {
|
||||
Port: number,
|
||||
PublicPaths: string[],
|
||||
PublicPath: string // For backward compatibility with older verions of Microsoft.AspNetCore.SpaServices. Will be removed soon.
|
||||
PublicPaths: string[]
|
||||
};
|
||||
|
||||
export interface CreateDevServerCallback {
|
||||
@@ -108,7 +108,7 @@ function attachWebpackDevMiddleware(app: any, webpackConfig: webpack.Configurati
|
||||
const compiler = webpack(webpackConfig);
|
||||
app.use(require('webpack-dev-middleware')(compiler, {
|
||||
noInfo: true,
|
||||
publicPath: webpackConfig.output.publicPath,
|
||||
publicPath: ensureLeadingSlash(webpackConfig.output.publicPath),
|
||||
watchOptions: webpackConfig.watchOptions
|
||||
}));
|
||||
|
||||
@@ -195,6 +195,14 @@ function copyRecursiveToRealFsSync(from: typeof fs, rootDir: string, exclude: Re
|
||||
});
|
||||
}
|
||||
|
||||
function ensureLeadingSlash(value: string) {
|
||||
if (value !== null && value.substring(0, 1) !== '/') {
|
||||
value = '/' + value;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
function pathJoinSafe(rootPath: string, filePath: string) {
|
||||
// On Windows, MemoryFileSystem's readdirSync output produces directory entries like 'C:'
|
||||
// which then trigger errors if you call statSync for them. Avoid this by detecting drive
|
||||
@@ -216,6 +224,16 @@ function beginWebpackWatcher(webpackConfig: webpack.Configuration) {
|
||||
export function createWebpackDevServer(callback: CreateDevServerCallback, optionsJson: string) {
|
||||
const options: CreateDevServerOptions = JSON.parse(optionsJson);
|
||||
|
||||
// See the large comment in WebpackTestPermissions.ts for details about this
|
||||
if (!hasSufficientPermissions()) {
|
||||
console.log('WARNING: Webpack dev middleware is not enabled because the server process does not have sufficient permissions. You should either remove the UseWebpackDevMiddleware call from your code, or to make it work, give your server process user account permission to write to your application directory and to read all ancestor-level directories.');
|
||||
callback(null, {
|
||||
Port: 0,
|
||||
PublicPaths: []
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Read the webpack config's export, and normalize it into the more general 'array of configs' format
|
||||
let webpackConfigExport: WebpackConfigFileExport = requireNewCopy(options.webpackConfigPath);
|
||||
if (webpackConfigExport instanceof Function) {
|
||||
@@ -257,22 +275,32 @@ export function createWebpackDevServer(callback: CreateDevServerCallback, option
|
||||
if (!publicPath) {
|
||||
throw new Error('To use the Webpack dev server, you must specify a value for \'publicPath\' on the \'output\' section of your webpack config (for any configuration that targets browsers)');
|
||||
}
|
||||
normalizedPublicPaths.push(removeTrailingSlash(publicPath));
|
||||
const publicPathNoTrailingSlash = removeTrailingSlash(publicPath);
|
||||
normalizedPublicPaths.push(publicPathNoTrailingSlash);
|
||||
|
||||
// Newer versions of Microsoft.AspNetCore.SpaServices will explicitly pass an HMR endpoint URL
|
||||
// (because it's relative to the app's URL space root, which the client doesn't otherwise know).
|
||||
// For back-compatibility, fall back on connecting directly to the underlying HMR server (though
|
||||
// that won't work if the app is hosted on HTTPS because of the mixed-content rule, and we can't
|
||||
// run the HMR server itself on HTTPS because in general it has no valid cert).
|
||||
const hmrClientEndpoint = options.hotModuleReplacementEndpointUrl // The URL that we'll proxy (e.g., /__asp_webpack_hmr)
|
||||
|| `http://localhost:${listener.address().port}/__webpack_hmr`; // Fall back on absolute URL to bypass proxying
|
||||
const hmrServerEndpoint = options.hotModuleReplacementEndpointUrl
|
||||
|| '/__webpack_hmr'; // URL is relative to webpack dev server root
|
||||
// This is the URL the client will connect to, except that since it's a relative URL
|
||||
// (no leading slash), Webpack will resolve it against the runtime <base href> URL
|
||||
// plus it also adds the publicPath
|
||||
const hmrClientEndpoint = removeLeadingSlash(options.hotModuleReplacementEndpointUrl);
|
||||
|
||||
// This is the URL inside the Webpack middleware Node server that we'll proxy to.
|
||||
// We have to prefix with the public path because Webpack will add the publicPath
|
||||
// when it resolves hmrClientEndpoint as a relative URL.
|
||||
const hmrServerEndpoint = ensureLeadingSlash(publicPathNoTrailingSlash + options.hotModuleReplacementEndpointUrl);
|
||||
|
||||
// We always overwrite the 'path' option as it needs to match what the .NET side is expecting
|
||||
const hmrClientOptions = options.suppliedOptions.HotModuleReplacementClientOptions || <StringMap<string>>{};
|
||||
hmrClientOptions['path'] = hmrClientEndpoint;
|
||||
|
||||
const dynamicPublicPathKey = 'dynamicPublicPath';
|
||||
if (!(dynamicPublicPathKey in hmrClientOptions)) {
|
||||
// dynamicPublicPath default to true, so we can work with nonempty pathbases (virtual directories)
|
||||
hmrClientOptions[dynamicPublicPathKey] = true;
|
||||
} else {
|
||||
// ... but you can set it to any other value explicitly if you want (e.g., false)
|
||||
hmrClientOptions[dynamicPublicPathKey] = JSON.parse(hmrClientOptions[dynamicPublicPathKey]);
|
||||
}
|
||||
|
||||
attachWebpackDevMiddleware(app, webpackConfig, enableHotModuleReplacement, enableReactHotModuleReplacement, hmrClientOptions, hmrServerEndpoint);
|
||||
}
|
||||
});
|
||||
@@ -280,11 +308,7 @@ export function createWebpackDevServer(callback: CreateDevServerCallback, option
|
||||
// Tell the ASP.NET app what addresses we're listening on, so that it can proxy requests here
|
||||
callback(null, {
|
||||
Port: listener.address().port,
|
||||
PublicPaths: normalizedPublicPaths,
|
||||
|
||||
// For back-compatibility with older versions of Microsoft.AspNetCore.SpaServices, in the case where
|
||||
// you have exactly one webpackConfigArray entry. This will be removed soon.
|
||||
PublicPath: normalizedPublicPaths[0]
|
||||
PublicPaths: normalizedPublicPaths
|
||||
});
|
||||
} catch (ex) {
|
||||
callback(ex.stack, null);
|
||||
@@ -292,6 +316,14 @@ export function createWebpackDevServer(callback: CreateDevServerCallback, option
|
||||
});
|
||||
}
|
||||
|
||||
function removeLeadingSlash(str: string) {
|
||||
if (str.indexOf('/') === 0) {
|
||||
str = str.substring(1);
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
function removeTrailingSlash(str: string) {
|
||||
if (str.lastIndexOf('/') === str.length - 1) {
|
||||
str = str.substring(0, str.length - 1);
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
const isWindows = /^win/.test(process.platform);
|
||||
|
||||
// On Windows, Node (still as of v8.1.3) has an issue whereby, when locating JavaScript modules
|
||||
// on disk, it walks up the directory hierarchy to the disk root, testing whether each directory
|
||||
// is a symlink or not. This fails with an exception if the process doesn't have permission to
|
||||
// read those directories. This is a problem when hosting in full IIS, because in typical cases
|
||||
// the process does not have read permission for higher-level directories.
|
||||
//
|
||||
// NodeServices itself works around this by injecting a patched version of Node's 'lstat' API that
|
||||
// suppresses these irrelevant errors during module loads. This covers most scenarios, but isn't
|
||||
// enough to make Webpack dev middleware work, because typical Webpack configs use loaders such as
|
||||
// 'awesome-typescript-loader', which works by forking a child process to do some of its work. The
|
||||
// child process does not get the patched 'lstat', and hence fails. It's an especially bad failure,
|
||||
// because the Webpack compiler doesn't even surface the exception - it just never completes the
|
||||
// compilation process, causing the application to hang indefinitely.
|
||||
//
|
||||
// Additionally, Webpack dev middleware will want to write its output to disk, which is also going
|
||||
// to fail in a typical IIS process, because you won't have 'write' permission to the app dir by
|
||||
// default. We have to actually write the build output to disk (and not purely keep it in the in-
|
||||
// memory file system) because the server-side prerendering Node instance is a separate process
|
||||
// that only knows about code changes when it sees the compiled files on disk change.
|
||||
//
|
||||
// In the future, we'll hopefully get Node to fix its underlying issue, and figure out whether VS
|
||||
// could give 'write' access to the app dir when launching sites in IIS. But until then, disable
|
||||
// Webpack dev middleware if we detect the server process doesn't have the necessary permissions.
|
||||
|
||||
export function hasSufficientPermissions() {
|
||||
if (isWindows) {
|
||||
return canReadDirectoryAndAllAncestors(process.cwd());
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function canReadDirectoryAndAllAncestors(dir: string): boolean {
|
||||
if (!canReadDirectory(dir)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const parentDir = path.resolve(dir, '..');
|
||||
if (parentDir === dir) {
|
||||
// There are no more parent directories - we've reached the disk root
|
||||
return true;
|
||||
} else {
|
||||
return canReadDirectoryAndAllAncestors(parentDir);
|
||||
}
|
||||
}
|
||||
|
||||
function canReadDirectory(dir: string): boolean {
|
||||
try {
|
||||
fs.statSync(dir);
|
||||
return true;
|
||||
} catch(ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
import { Component, Inject } from '@angular/core';
|
||||
import { Http } from '@angular/http';
|
||||
|
||||
@Component({
|
||||
selector: 'fetchdata',
|
||||
templateUrl: './fetchdata.component.html'
|
||||
})
|
||||
export class FetchDataComponent {
|
||||
public forecasts: WeatherForecast[];
|
||||
|
||||
constructor(http: Http, @Inject('ORIGIN_URL') originUrl: string) {
|
||||
http.get(originUrl + '/api/SampleData/WeatherForecasts').subscribe(result => {
|
||||
this.forecasts = result.json() as WeatherForecast[];
|
||||
}, error => console.error(error));
|
||||
}
|
||||
}
|
||||
|
||||
interface WeatherForecast {
|
||||
dateFormatted: string;
|
||||
temperatureC: number;
|
||||
temperatureF: number;
|
||||
summary: string;
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>Microsoft.AspNetCore.SpaTemplates</id>
|
||||
<version>{yeomanversion}</version>
|
||||
<version>0.0.0</version>
|
||||
<description>Single Page Application templates for ASP.NET Core</description>
|
||||
<authors>Microsoft</authors>
|
||||
<language>en-US</language>
|
||||
@@ -14,4 +14,10 @@
|
||||
<packageType name="Template" />
|
||||
</packageTypes>
|
||||
</metadata>
|
||||
<files>
|
||||
<file
|
||||
src="**/*"
|
||||
exclude="*/node_modules/**;*/bin/**;*/obj/**;*/ClientApp/dist/**;*/wwwroot/dist/**"
|
||||
target="Content" />
|
||||
</files>
|
||||
</package>
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"$schema": "http://json.schemastore.org/dotnetcli.host",
|
||||
"symbolInfo": {
|
||||
"TargetFrameworkOverride": {
|
||||
"isHidden": "true",
|
||||
"longName": "target-framework-override",
|
||||
"shortName": ""
|
||||
},
|
||||
"Framework": {
|
||||
"longName": "framework"
|
||||
},
|
||||
"skipRestore": {
|
||||
"longName": "no-restore",
|
||||
"shortName": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
@@ -0,0 +1,87 @@
|
||||
{
|
||||
"author": "Microsoft",
|
||||
"classifications": [
|
||||
"Web",
|
||||
"MVC",
|
||||
"SPA"
|
||||
],
|
||||
"groupIdentity": "Microsoft.AspNetCore.SpaTemplates.Aurelia",
|
||||
"identity": "Microsoft.AspNetCore.SpaTemplates.Aurelia.CSharp",
|
||||
"name": "ASP.NET Core with Aurelia",
|
||||
"preferNameDirectory": true,
|
||||
"primaryOutputs": [
|
||||
{
|
||||
"path": "AureliaSpa.csproj"
|
||||
}
|
||||
],
|
||||
"shortName": "aurelia",
|
||||
"sourceName": "AureliaSpa",
|
||||
"sources": [
|
||||
{
|
||||
"source": "./",
|
||||
"target": "./",
|
||||
"exclude": [
|
||||
".template.config/**"
|
||||
]
|
||||
}
|
||||
],
|
||||
"symbols": {
|
||||
"TargetFrameworkOverride": {
|
||||
"type": "parameter",
|
||||
"description": "Overrides the target framework",
|
||||
"replaces": "TargetFrameworkOverride",
|
||||
"datatype": "string",
|
||||
"defaultValue": ""
|
||||
},
|
||||
"Framework": {
|
||||
"type": "parameter",
|
||||
"description": "The target framework for the project.",
|
||||
"datatype": "choice",
|
||||
"choices": [
|
||||
{
|
||||
"choice": "netcoreapp2.0",
|
||||
"description": "Target netcoreapp2.0"
|
||||
}
|
||||
],
|
||||
"replaces": "netcoreapp2.0",
|
||||
"defaultValue": "netcoreapp2.0"
|
||||
},
|
||||
"HostIdentifier": {
|
||||
"type": "bind",
|
||||
"binding": "HostIdentifier"
|
||||
},
|
||||
"skipRestore": {
|
||||
"type": "parameter",
|
||||
"datatype": "bool",
|
||||
"description": "If specified, skips the automatic restore of the project on create.",
|
||||
"defaultValue": "false"
|
||||
}
|
||||
},
|
||||
"tags": {
|
||||
"language": "C#",
|
||||
"type": "project"
|
||||
},
|
||||
"postActions": [
|
||||
{
|
||||
"condition": "(!skipRestore)",
|
||||
"description": "Restore NuGet packages required by this project.",
|
||||
"manualInstructions": [
|
||||
{
|
||||
"text": "Run 'dotnet restore'"
|
||||
}
|
||||
],
|
||||
"actionId": "210D431B-A78B-4D2F-B762-4ED3E3EA9025",
|
||||
"continueOnError": true
|
||||
},
|
||||
{
|
||||
"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\""
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"$schema": "http://json.schemastore.org/vs-2017.3.host",
|
||||
"name": {
|
||||
"text": "Aurelia",
|
||||
"package": "{0CD94836-1526-4E85-87D3-FB5274C5AFC9}",
|
||||
"id": "1200"
|
||||
},
|
||||
"description": {
|
||||
"text": "A project template for creating an ASP.NET Core application with Aurelia",
|
||||
"package": "{0CD94836-1526-4E85-87D3-FB5274C5AFC9}",
|
||||
"id": "1201"
|
||||
},
|
||||
"order": 301,
|
||||
"icon": "icon.png",
|
||||
"learnMoreLink": "https://github.com/aspnet/JavaScriptServices",
|
||||
"uiFilters": [
|
||||
"oneaspnet"
|
||||
],
|
||||
"minFullFrameworkVersion": "4.6.1"
|
||||
}
|
||||
@@ -9,17 +9,17 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFrameworkOverride)' == ''">
|
||||
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0-rtm-26190" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0" />
|
||||
</ItemGroup>
|
||||
<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" />
|
||||
<PackageReference Include="Microsoft.AspNetCore" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.SpaServices" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="2.0.0" />
|
||||
</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>
|
||||
|
||||
<!--/-:cnd:noEmit -->
|
||||
@@ -6,7 +6,7 @@ export class Fetchdata {
|
||||
public forecasts: WeatherForecast[];
|
||||
|
||||
constructor(http: HttpClient) {
|
||||
http.fetch('/api/SampleData/WeatherForecasts')
|
||||
http.fetch('api/SampleData/WeatherForecasts')
|
||||
.then(result => result.json() as Promise<WeatherForecast[]>)
|
||||
.then(data => {
|
||||
this.forecasts = data;
|
||||
@@ -9,7 +9,7 @@
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="#/home">WebApplicationBasic</a>
|
||||
<a class="navbar-brand" href="#/home">AureliaSpa</a>
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
<div class="navbar-collapse collapse">
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'isomorphic-fetch';
|
||||
import { Aurelia, PLATFORM } from 'aurelia-framework';
|
||||
import { HttpClient } from 'aurelia-fetch-client';
|
||||
import 'bootstrap/dist/css/bootstrap.css';
|
||||
import 'bootstrap';
|
||||
declare const IS_DEV_BUILD: boolean; // The value is supplied by Webpack during the build
|
||||
@@ -11,5 +12,10 @@ export function configure(aurelia: Aurelia) {
|
||||
aurelia.use.developmentLogging();
|
||||
}
|
||||
|
||||
new HttpClient().configure(config => {
|
||||
const baseUrl = document.getElementsByTagName('base')[0].href;
|
||||
config.withBaseUrl(baseUrl);
|
||||
});
|
||||
|
||||
aurelia.start().then(() => aurelia.setRoot(PLATFORM.moduleName('app/components/app/app')));
|
||||
}
|
||||
@@ -5,7 +5,7 @@ using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace WebApplicationBasic.Controllers
|
||||
namespace AureliaSpa.Controllers
|
||||
{
|
||||
public class HomeController : Controller
|
||||
{
|
||||
@@ -4,7 +4,7 @@ using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace WebApplicationBasic.Controllers
|
||||
namespace AureliaSpa.Controllers
|
||||
{
|
||||
[Route("api/[controller]")]
|
||||
public class SampleDataController : Controller
|
||||
@@ -8,7 +8,7 @@ using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace WebApplicationBasic
|
||||
namespace AureliaSpa
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
2
templates/KnockoutSpa/Startup.cs → templates/Microsoft.AspNetCore.SpaTemplates/aurelia/Startup.cs
Executable file → Normal file
2
templates/KnockoutSpa/Startup.cs → templates/Microsoft.AspNetCore.SpaTemplates/aurelia/Startup.cs
Executable file → Normal file
@@ -8,7 +8,7 @@ using Microsoft.AspNetCore.SpaServices.Webpack;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace WebApplicationBasic
|
||||
namespace AureliaSpa
|
||||
{
|
||||
public class Startup
|
||||
{
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>@ViewData["Title"] - WebApplicationBasic</title>
|
||||
<title>@ViewData["Title"] - AureliaSpa</title>
|
||||
<base href="~/" />
|
||||
|
||||
<link rel="stylesheet" href="~/dist/vendor.css" asp-append-version="true" />
|
||||
@@ -1,3 +1,3 @@
|
||||
@using WebApplicationBasic
|
||||
@using AureliaSpa
|
||||
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
||||
@addTagHelper *, Microsoft.AspNetCore.SpaServices
|
||||
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"name": "WebApplicationBasic",
|
||||
"name": "AureliaSpa",
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"@types/node": {
|
||||
"version": "7.0.32",
|
||||
"from": "@types/node@>=7.0.12 <8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-7.0.32.tgz",
|
||||
"@types/webpack-env": {
|
||||
"version": "1.13.0",
|
||||
"from": "@types/webpack-env@>=1.13.0 <2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/webpack-env/-/webpack-env-1.13.0.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"acorn": {
|
||||
@@ -113,9 +113,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"aspnet-webpack": {
|
||||
"version": "1.0.29",
|
||||
"from": "aspnet-webpack@>=1.0.28 <2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/aspnet-webpack/-/aspnet-webpack-1.0.29.tgz",
|
||||
"version": "2.0.1",
|
||||
"from": "aspnet-webpack@2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/aspnet-webpack/-/aspnet-webpack-2.0.1.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"assert": {
|
||||
@@ -1,26 +1,24 @@
|
||||
{
|
||||
"name": "WebApplicationBasic",
|
||||
"name": "AureliaSpa",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"devDependencies": {
|
||||
"@types/webpack-env": "^1.13.0",
|
||||
"aspnet-webpack": "^2.0.1",
|
||||
"aurelia-bootstrapper": "^2.0.1",
|
||||
"aurelia-fetch-client": "^1.0.1",
|
||||
"aurelia-framework": "^1.1.0",
|
||||
"aurelia-loader-webpack": "^2.0.0",
|
||||
"aurelia-pal": "^1.3.0",
|
||||
"aurelia-router": "^1.2.1",
|
||||
"bootstrap": "^3.3.7",
|
||||
"isomorphic-fetch": "^2.2.1",
|
||||
"jquery": "^3.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^7.0.12",
|
||||
"aspnet-webpack": "^1.0.28",
|
||||
"aurelia-webpack-plugin": "^2.0.0-rc.2",
|
||||
"bootstrap": "^3.3.7",
|
||||
"css-loader": "^0.28.0",
|
||||
"extract-text-webpack-plugin": "^2.1.0",
|
||||
"file-loader": "^0.11.1",
|
||||
"html-loader": "^0.4.5",
|
||||
"isomorphic-fetch": "^2.2.1",
|
||||
"jquery": "^3.2.1",
|
||||
"json-loader": "^0.5.4",
|
||||
"style-loader": "^0.16.1",
|
||||
"ts-loader": "^2.0.3",
|
||||
@@ -1,13 +1,15 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "es2015",
|
||||
"moduleResolution": "node",
|
||||
"target": "es5",
|
||||
"sourceMap": true,
|
||||
"experimentalDecorators": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"skipDefaultLibCheck": true,
|
||||
"strict": true,
|
||||
"lib": [ "es2015", "dom" ],
|
||||
"types": [ "node" ]
|
||||
"types": [ "webpack-env" ]
|
||||
},
|
||||
"exclude": [ "bin", "node_modules" ],
|
||||
"atom": { "rewriteTsconfig": false }
|
||||
@@ -14,7 +14,7 @@ module.exports = (env) => {
|
||||
},
|
||||
output: {
|
||||
path: path.resolve(bundleOutputDir),
|
||||
publicPath: '/dist/',
|
||||
publicPath: 'dist/',
|
||||
filename: '[name].js'
|
||||
},
|
||||
module: {
|
||||
@@ -38,7 +38,7 @@ module.exports = ({ prod } = {}) => {
|
||||
},
|
||||
output: {
|
||||
path: path.join(__dirname, 'wwwroot', 'dist'),
|
||||
publicPath: '/dist/',
|
||||
publicPath: 'dist/',
|
||||
filename: '[name].js',
|
||||
library: '[name]_[hash]',
|
||||
},
|
||||
|
Before Width: | Height: | Size: 83 KiB After Width: | Height: | Size: 83 KiB |
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"$schema": "http://json.schemastore.org/dotnetcli.host",
|
||||
"symbolInfo": {
|
||||
"TargetFrameworkOverride": {
|
||||
"isHidden": "true",
|
||||
"longName": "target-framework-override",
|
||||
"shortName": ""
|
||||
},
|
||||
"Framework": {
|
||||
"longName": "framework"
|
||||
},
|
||||
"skipRestore": {
|
||||
"longName": "no-restore",
|
||||
"shortName": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 6.0 KiB After Width: | Height: | Size: 6.0 KiB |
@@ -0,0 +1,87 @@
|
||||
{
|
||||
"author": "Microsoft",
|
||||
"classifications": [
|
||||
"Web",
|
||||
"MVC",
|
||||
"SPA"
|
||||
],
|
||||
"groupIdentity": "Microsoft.AspNetCore.SpaTemplates.Knockout",
|
||||
"identity": "Microsoft.AspNetCore.SpaTemplates.Knockout.CSharp",
|
||||
"name": "ASP.NET Core with Knockout.js",
|
||||
"preferNameDirectory": true,
|
||||
"primaryOutputs": [
|
||||
{
|
||||
"path": "KnockoutSpa.csproj"
|
||||
}
|
||||
],
|
||||
"shortName": "knockout",
|
||||
"sourceName": "KnockoutSpa",
|
||||
"sources": [
|
||||
{
|
||||
"source": "./",
|
||||
"target": "./",
|
||||
"exclude": [
|
||||
".template.config/**"
|
||||
]
|
||||
}
|
||||
],
|
||||
"symbols": {
|
||||
"TargetFrameworkOverride": {
|
||||
"type": "parameter",
|
||||
"description": "Overrides the target framework",
|
||||
"replaces": "TargetFrameworkOverride",
|
||||
"datatype": "string",
|
||||
"defaultValue": ""
|
||||
},
|
||||
"Framework": {
|
||||
"type": "parameter",
|
||||
"description": "The target framework for the project.",
|
||||
"datatype": "choice",
|
||||
"choices": [
|
||||
{
|
||||
"choice": "netcoreapp2.0",
|
||||
"description": "Target netcoreapp2.0"
|
||||
}
|
||||
],
|
||||
"replaces": "netcoreapp2.0",
|
||||
"defaultValue": "netcoreapp2.0"
|
||||
},
|
||||
"HostIdentifier": {
|
||||
"type": "bind",
|
||||
"binding": "HostIdentifier"
|
||||
},
|
||||
"skipRestore": {
|
||||
"type": "parameter",
|
||||
"datatype": "bool",
|
||||
"description": "If specified, skips the automatic restore of the project on create.",
|
||||
"defaultValue": "false"
|
||||
}
|
||||
},
|
||||
"tags": {
|
||||
"language": "C#",
|
||||
"type": "project"
|
||||
},
|
||||
"postActions": [
|
||||
{
|
||||
"condition": "(!skipRestore)",
|
||||
"description": "Restore NuGet packages required by this project.",
|
||||
"manualInstructions": [
|
||||
{
|
||||
"text": "Run 'dotnet restore'"
|
||||
}
|
||||
],
|
||||
"actionId": "210D431B-A78B-4D2F-B762-4ED3E3EA9025",
|
||||
"continueOnError": true
|
||||
},
|
||||
{
|
||||
"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\""
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"$schema": "http://json.schemastore.org/vs-2017.3.host",
|
||||
"name": {
|
||||
"text": "Knockout.js",
|
||||
"package": "{0CD94836-1526-4E85-87D3-FB5274C5AFC9}",
|
||||
"id": "1300"
|
||||
},
|
||||
"description": {
|
||||
"text": "A project template for creating an ASP.NET Core application with Knockout.js",
|
||||
"package": "{0CD94836-1526-4E85-87D3-FB5274C5AFC9}",
|
||||
"id": "1301"
|
||||
},
|
||||
"order": 301,
|
||||
"icon": "icon.png",
|
||||
"learnMoreLink": "https://github.com/aspnet/JavaScriptServices",
|
||||
"uiFilters": [
|
||||
"oneaspnet"
|
||||
],
|
||||
"minFullFrameworkVersion": "4.6.1"
|
||||
}
|
||||
@@ -1,19 +1,20 @@
|
||||
import './css/site.css';
|
||||
import 'bootstrap';
|
||||
import * as ko from 'knockout';
|
||||
import { createBrowserHistory } from 'history';
|
||||
import './webpack-component-loader';
|
||||
import AppRootComponent from './components/app-root/app-root';
|
||||
const createHistory = require('history').createBrowserHistory;
|
||||
const baseUrl = document.getElementsByTagName('base')[0].getAttribute('href')!;
|
||||
const basename = baseUrl.substring(0, baseUrl.length - 1); // History component needs no trailing slash
|
||||
|
||||
// Load and register the <app-root> component
|
||||
ko.components.register('app-root', AppRootComponent);
|
||||
|
||||
// Tell Knockout to start up an instance of your application
|
||||
ko.applyBindings({ history: createHistory() });
|
||||
ko.applyBindings({ history: createBrowserHistory({ basename }), basename });
|
||||
|
||||
// Basic hot reloading support. Automatically reloads and restarts the Knockout app each time
|
||||
// you modify source files. This will not preserve any application state other than the URL.
|
||||
declare var module: any;
|
||||
if (module.hot) {
|
||||
module.hot.accept();
|
||||
module.hot.dispose(() => ko.cleanNode(document.body));
|
||||
@@ -1,7 +1,7 @@
|
||||
<div class='container-fluid'>
|
||||
<div class='row'>
|
||||
<div class='col-sm-3'>
|
||||
<nav-menu params='route: route'></nav-menu>
|
||||
<nav-menu params='router: router'></nav-menu>
|
||||
</div>
|
||||
<div class='col-sm-9' data-bind='component: { name: route().page, params: route }'></div>
|
||||
</div>
|
||||
@@ -12,12 +12,12 @@ const routes: Route[] = [
|
||||
|
||||
class AppRootViewModel {
|
||||
public route: KnockoutObservable<Route>;
|
||||
private _router: Router;
|
||||
public router: Router;
|
||||
|
||||
constructor(params: { history: History.History }) {
|
||||
constructor(params: { history: History.History, basename: string }) {
|
||||
// Activate the client-side router
|
||||
this._router = new Router(params.history, routes)
|
||||
this.route = this._router.currentRoute;
|
||||
this.router = new Router(params.history, routes, params.basename);
|
||||
this.route = this.router.currentRoute;
|
||||
|
||||
// Load and register all the KO components needed to handle the routes
|
||||
// The optional 'bundle-loader?lazy!' prefix is a Webpack feature that causes the referenced modules
|
||||
@@ -32,7 +32,7 @@ class AppRootViewModel {
|
||||
// To support hot module replacement, this method unregisters the router and KO components.
|
||||
// In production scenarios where hot module replacement is disabled, this would not be invoked.
|
||||
public dispose() {
|
||||
this._router.dispose();
|
||||
this.router.dispose();
|
||||
|
||||
// TODO: Need a better API for this
|
||||
Object.getOwnPropertyNames((<any>ko).components._allRegisteredComponents).forEach(componentName => {
|
||||
@@ -12,7 +12,7 @@ class FetchDataViewModel {
|
||||
public forecasts = ko.observableArray<WeatherForecast>();
|
||||
|
||||
constructor() {
|
||||
fetch('/api/SampleData/WeatherForecasts')
|
||||
fetch('api/SampleData/WeatherForecasts')
|
||||
.then(response => response.json() as Promise<WeatherForecast[]>)
|
||||
.then(data => {
|
||||
this.forecasts(data);
|
||||
@@ -7,23 +7,23 @@
|
||||
<span class='icon-bar'></span>
|
||||
<span class='icon-bar'></span>
|
||||
</button>
|
||||
<a class='navbar-brand' href='/'>WebApplicationBasic</a>
|
||||
<a class='navbar-brand' href='/'>KnockoutSpa</a>
|
||||
</div>
|
||||
<div class='clearfix'></div>
|
||||
<div class='navbar-collapse collapse'>
|
||||
<ul class='nav navbar-nav'>
|
||||
<li>
|
||||
<a href='/' data-bind='css: { active: route().page === "home-page" }'>
|
||||
<a data-bind='attr: { href: router.link("/") }, css: { active: route().page === "home-page" }'>
|
||||
<span class='glyphicon glyphicon-home'></span> Home
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href='/counter' data-bind='css: { active: route().page === "counter-example" }'>
|
||||
<a data-bind='attr: { href: router.link("/counter") }, css: { active: route().page === "counter-example" }'>
|
||||
<span class='glyphicon glyphicon-education'></span> Counter
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href='/fetch-data' data-bind='css: { active: route().page === "fetch-data" }'>
|
||||
<a data-bind='attr: { href: router.link("/fetch-data") }, css: { active: route().page === "fetch-data" }'>
|
||||
<span class='glyphicon glyphicon-th-list'></span> Fetch data
|
||||
</a>
|
||||
</li>
|
||||
@@ -1,18 +1,20 @@
|
||||
import * as ko from 'knockout';
|
||||
import { Route } from '../../router';
|
||||
import { Route, Router } from '../../router';
|
||||
|
||||
interface NavMenuParams {
|
||||
route: KnockoutObservable<Route>;
|
||||
router: Router;
|
||||
}
|
||||
|
||||
class NavMenuViewModel {
|
||||
public router: Router;
|
||||
public route: KnockoutObservable<Route>;
|
||||
|
||||
constructor(params: NavMenuParams) {
|
||||
// This viewmodel doesn't do anything except pass through the 'route' parameter to the view.
|
||||
// You could remove this viewmodel entirely, and define 'nav-menu' as a template-only component.
|
||||
// But in most apps, you'll want some viewmodel logic to determine what navigation options appear.
|
||||
this.route = params.route;
|
||||
this.router = params.router;
|
||||
this.route = this.router.currentRoute;
|
||||
}
|
||||
}
|
||||
|
||||
0
templates/KnockoutSpa/ClientApp/css/site.css → templates/Microsoft.AspNetCore.SpaTemplates/knockout/ClientApp/css/site.css
Executable file → Normal file
0
templates/KnockoutSpa/ClientApp/css/site.css → templates/Microsoft.AspNetCore.SpaTemplates/knockout/ClientApp/css/site.css
Executable file → Normal file
@@ -1,7 +1,7 @@
|
||||
import * as ko from 'knockout';
|
||||
import * as $ from 'jquery';
|
||||
import * as History from 'history';
|
||||
import crossroads = require('crossroads');
|
||||
import * as crossroads from 'crossroads';
|
||||
|
||||
// This module configures crossroads.js, a routing library. If you prefer, you
|
||||
// can use any other routing library (or none at all) as Knockout is designed to
|
||||
@@ -16,13 +16,13 @@ export class Router {
|
||||
private disposeHistory: () => void;
|
||||
private clickEventListener: EventListener;
|
||||
|
||||
constructor(history: History.History, routes: Route[]) {
|
||||
constructor(private history: History.History, routes: Route[], basename: string) {
|
||||
// Reset and configure Crossroads so it matches routes and updates this.currentRoute
|
||||
crossroads.removeAllRoutes();
|
||||
crossroads.resetState();
|
||||
crossroads.normalizeFn = crossroads.NORM_AS_OBJECT;
|
||||
(crossroads as any).normalizeFn = crossroads.NORM_AS_OBJECT;
|
||||
routes.forEach(route => {
|
||||
crossroads.addRoute(route.url, (requestParams) => {
|
||||
crossroads.addRoute(route.url, (requestParams: any) => {
|
||||
this.currentRoute(ko.utils.extend(requestParams, route.params));
|
||||
});
|
||||
});
|
||||
@@ -33,8 +33,9 @@ export class Router {
|
||||
let target: any = evt.currentTarget;
|
||||
if (target && target.tagName === 'A') {
|
||||
let href = target.getAttribute('href');
|
||||
if (href && href.charAt(0) == '/') {
|
||||
history.push(href);
|
||||
if (href && href.indexOf(basename + '/') === 0) {
|
||||
const hrefAfterBasename = href.substring(basename.length);
|
||||
history.push(hrefAfterBasename);
|
||||
evt.preventDefault();
|
||||
}
|
||||
}
|
||||
@@ -42,8 +43,11 @@ export class Router {
|
||||
$(document).on('click', 'a', this.clickEventListener);
|
||||
|
||||
// Initialize Crossroads with starting location
|
||||
// Need to cast history to 'any' because @types/history is out-of-date
|
||||
crossroads.parse((history as any).location.pathname);
|
||||
crossroads.parse(history.location.pathname);
|
||||
}
|
||||
|
||||
public link(url: string): string {
|
||||
return this.history.createHref({ pathname: url });
|
||||
}
|
||||
|
||||
public dispose() {
|
||||
@@ -8,18 +8,18 @@ ko.components.loaders.unshift({
|
||||
loadComponent: (name, componentConfig, callback) => {
|
||||
if (typeof componentConfig === 'function') {
|
||||
// It's a lazy-loaded Webpack bundle
|
||||
(componentConfig as any)(loadedModule => {
|
||||
(componentConfig as any)((loadedModule: any) => {
|
||||
// Handle TypeScript-style default exports
|
||||
if (loadedModule.__esModule && loadedModule.default) {
|
||||
loadedModule = loadedModule.default;
|
||||
}
|
||||
|
||||
// Pass the loaded module to KO's default loader
|
||||
ko.components.defaultLoader.loadComponent(name, loadedModule, callback);
|
||||
ko.components.defaultLoader.loadComponent!(name, loadedModule as KnockoutComponentTypes.ComponentConfig, callback);
|
||||
});
|
||||
} else {
|
||||
// It's something else - let another component loader handle it
|
||||
callback(null);
|
||||
callback((null as any) as KnockoutComponentTypes.Definition); // workaround until https://github.com/DefinitelyTyped/DefinitelyTyped/pull/17999
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -5,7 +5,7 @@ using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace WebApplicationBasic.Controllers
|
||||
namespace KnockoutSpa.Controllers
|
||||
{
|
||||
public class HomeController : Controller
|
||||
{
|
||||
@@ -4,7 +4,7 @@ using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace WebApplicationBasic.Controllers
|
||||
namespace KnockoutSpa.Controllers
|
||||
{
|
||||
[Route("api/[controller]")]
|
||||
public class SampleDataController : Controller
|
||||
@@ -9,17 +9,17 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFrameworkOverride)' == ''">
|
||||
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0-rtm-26190" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0" />
|
||||
</ItemGroup>
|
||||
<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" />
|
||||
<PackageReference Include="Microsoft.AspNetCore" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.SpaServices" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="2.0.0" />
|
||||
</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>
|
||||
|
||||
<!--/-:cnd:noEmit -->
|
||||
@@ -8,7 +8,7 @@ using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace WebApplicationBasic
|
||||
namespace KnockoutSpa
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
@@ -8,7 +8,7 @@ using Microsoft.AspNetCore.SpaServices.Webpack;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace WebApplicationBasic
|
||||
namespace KnockoutSpa
|
||||
{
|
||||
public class Startup
|
||||
{
|
||||
@@ -2,7 +2,7 @@
|
||||
ViewData["Title"] = "Home Page";
|
||||
}
|
||||
|
||||
<app-root params="history: history"></app-root>
|
||||
<app-root params="history: history, basename: basename"></app-root>
|
||||
|
||||
@section scripts {
|
||||
<script src="~/dist/main.js" asp-append-version="true"></script>
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>@ViewData["Title"] - WebApplicationBasic</title>
|
||||
<title>@ViewData["Title"] - KnockoutSpa</title>
|
||||
<base href="~/" />
|
||||
|
||||
<link rel="stylesheet" href="~/dist/vendor.css" asp-append-version="true" />
|
||||
@@ -1,3 +1,3 @@
|
||||
@using WebApplicationBasic
|
||||
@using KnockoutSpa
|
||||
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
||||
@addTagHelper *, Microsoft.AspNetCore.SpaServices
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "WebApplicationBasic",
|
||||
"name": "KnockoutSpa",
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"@types/core-js": {
|
||||
@@ -21,9 +21,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"@types/history": {
|
||||
"version": "2.0.48",
|
||||
"from": "@types/history@>=2.0.38 <3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/history/-/history-2.0.48.tgz",
|
||||
"version": "4.6.0",
|
||||
"from": "@types/history@4.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/history/-/history-4.6.0.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"@types/jquery": {
|
||||
@@ -38,30 +38,18 @@
|
||||
"resolved": "https://registry.npmjs.org/@types/knockout/-/knockout-3.4.41.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"@types/react": {
|
||||
"version": "15.0.31",
|
||||
"from": "@types/react@*",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-15.0.31.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"@types/react-router": {
|
||||
"version": "2.0.50",
|
||||
"from": "@types/react-router@>=2.0.37 <3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-2.0.50.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"@types/requirejs": {
|
||||
"version": "2.1.29",
|
||||
"from": "@types/requirejs@>=2.1.26 <3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/requirejs/-/requirejs-2.1.29.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"@types/signals": {
|
||||
"version": "0.0.16",
|
||||
"from": "@types/signals@0.0.16",
|
||||
"resolved": "https://registry.npmjs.org/@types/signals/-/signals-0.0.16.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"@types/webpack-env": {
|
||||
"version": "1.13.0",
|
||||
"from": "@types/webpack-env@>=1.13.0 <2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/webpack-env/-/webpack-env-1.13.0.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"acorn": {
|
||||
"version": "5.0.3",
|
||||
"from": "acorn@>=5.0.0 <6.0.0",
|
||||
@@ -167,9 +155,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"aspnet-webpack": {
|
||||
"version": "1.0.29",
|
||||
"from": "aspnet-webpack@>=1.0.27 <2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/aspnet-webpack/-/aspnet-webpack-1.0.29.tgz",
|
||||
"version": "2.0.1",
|
||||
"from": "aspnet-webpack@2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/aspnet-webpack/-/aspnet-webpack-2.0.1.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"assert": {
|
||||
@@ -1,18 +1,17 @@
|
||||
{
|
||||
"name": "WebApplicationBasic",
|
||||
"name": "KnockoutSpa",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"devDependencies": {
|
||||
"@types/core-js": "^0.9.34",
|
||||
"@types/crossroads": "0.0.29",
|
||||
"@types/es6-promise": "0.0.32",
|
||||
"@types/history": "^2.0.38",
|
||||
"@types/history": "^4.6.0",
|
||||
"@types/jquery": "^2.0.32",
|
||||
"@types/knockout": "^3.4.35",
|
||||
"@types/react-router": "^2.0.37",
|
||||
"@types/requirejs": "^2.1.26",
|
||||
"@types/knockout": "^3.4.41",
|
||||
"@types/signals": "0.0.16",
|
||||
"aspnet-webpack": "^1.0.27",
|
||||
"@types/webpack-env": "^1.13.0",
|
||||
"aspnet-webpack": "^2.0.1",
|
||||
"awesome-typescript-loader": "^3.0.0",
|
||||
"bootstrap": "^3.3.6",
|
||||
"bundle-loader": "^0.5.4",
|
||||
@@ -21,7 +20,7 @@
|
||||
"event-source-polyfill": "^0.0.7",
|
||||
"extract-text-webpack-plugin": "^2.0.0-rc",
|
||||
"file-loader": "^0.9.0",
|
||||
"history": "^4.3.0",
|
||||
"history": "^4.6.3",
|
||||
"isomorphic-fetch": "^2.2.1",
|
||||
"jquery": "^2.2.1",
|
||||
"json-loader": "^0.5.4",
|
||||
@@ -1,10 +1,12 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "es2015",
|
||||
"moduleResolution": "node",
|
||||
"target": "es5",
|
||||
"sourceMap": true,
|
||||
"skipDefaultLibCheck": true,
|
||||
"types": ["es6-promise", "history", "requirejs"]
|
||||
"strict": true,
|
||||
"types": ["es6-promise", "webpack-env"]
|
||||
},
|
||||
"exclude": [
|
||||
"bin",
|
||||
@@ -13,7 +13,7 @@ module.exports = (env) => {
|
||||
output: {
|
||||
path: path.join(__dirname, bundleOutputDir),
|
||||
filename: '[name].js',
|
||||
publicPath: '/dist/'
|
||||
publicPath: 'dist/'
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
@@ -21,7 +21,7 @@ module.exports = (env) => {
|
||||
},
|
||||
output: {
|
||||
path: path.join(__dirname, 'wwwroot', 'dist'),
|
||||
publicPath: '/dist/',
|
||||
publicPath: 'dist/',
|
||||
filename: '[name].js',
|
||||
library: '[name]_[hash]',
|
||||
},
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user