Skip to content
This repository was archived by the owner on Nov 6, 2023. It is now read-only.

Commit 159fcab

Browse files
committed
initialize repo with code from JavaScriptServices PR
0 parents  commit 159fcab

9 files changed

+269
-0
lines changed

.gitignore

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
2+
#Ignore thumbnails created by Windows
3+
Thumbs.db
4+
#Ignore files built by Visual Studio
5+
*.obj
6+
*.exe
7+
*.pdb
8+
*.user
9+
*.aps
10+
*.pch
11+
*.vspscc
12+
*_i.c
13+
*_p.c
14+
*.ncb
15+
*.suo
16+
*.tlb
17+
*.tlh
18+
*.bak
19+
*.cache
20+
*.ilk
21+
*.log
22+
[Bb]in
23+
[Dd]ebug*/
24+
*.lib
25+
*.sbr
26+
obj/
27+
[Rr]elease*/
28+
_ReSharper*/
29+
[Tt]est[Rr]esult*
30+
.vs/
31+
#Nuget packages folder
32+
packages/

.gitmodules

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
[submodule "JavaScriptServices"]
2+
path = JavaScriptServices
3+
url = https://github.com/aspnet/JavaScriptServices
4+
branch = release/2.1
5+
[submodule "submodules/JavaScriptServices"]
6+
path = submodules/JavaScriptServices
7+
url = https://github.com/aspnet/JavaScriptServices
8+
branch = release/2.1

LICENSE.txt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
Copyright (c) .NET Foundation Contributors
2+
3+
All rights reserved.
4+
5+
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
6+
this file except in compliance with the License. You may obtain a copy of the
7+
License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software distributed
12+
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
13+
CONDITIONS OF ANY KIND, either express or implied. See the License for the
14+
specific language governing permissions and limitations under the License.

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
This is a stand-alone module to add VueCli support to Aspnet Core 2.1.
2+
3+
Due to the discussion here, it was decided to not be included in the Microsoft owned package.
4+
https://github.com/aspnet/JavaScriptServices/pull/1726

src/VueCliMiddleware.csproj

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<Description>Helpers for building single-page applications on ASP.NET MVC Core using Vue Cli.</Description>
5+
<TargetFramework>netstandard2.0</TargetFramework>
6+
<VersionPrefix>2.1.1</VersionPrefix>
7+
<LangVersion>Latest</LangVersion>
8+
<Authors>EEParker</Authors>
9+
<RepositoryUrl>https://github.com/EEParker/aspnetcore-vueclimiddleware.git</RepositoryUrl>
10+
<PackageProjectUrl>https://github.com/EEParker/aspnetcore-vueclimiddleware.git</PackageProjectUrl>
11+
</PropertyGroup>
12+
13+
<ItemGroup>
14+
<PackageReference Include="Microsoft.AspNetCore.SpaServices.Extensions" Version="2.1.1" />
15+
</ItemGroup>
16+
17+
<ItemGroup>
18+
<Compile Include="..\submodules\JavaScriptServices\src\Microsoft.AspNetCore.SpaServices.Extensions\Npm\NpmScriptRunner.cs" Link="Npm\NpmScriptRunner.cs" />
19+
<Compile Include="..\submodules\JavaScriptServices\src\Microsoft.AspNetCore.SpaServices.Extensions\Util\EventedStreamReader.cs" Link="Util\EventedStreamReader.cs" />
20+
<Compile Include="..\submodules\JavaScriptServices\src\Microsoft.AspNetCore.SpaServices.Extensions\Util\EventedStreamStringReader.cs" Link="Util\EventedStreamStringReader.cs" />
21+
<Compile Include="..\submodules\JavaScriptServices\src\Microsoft.AspNetCore.SpaServices.Extensions\Util\LoggerFinder.cs" Link="Util\LoggerFinder.cs" />
22+
<Compile Include="..\submodules\JavaScriptServices\src\Microsoft.AspNetCore.SpaServices.Extensions\Util\TaskTimeoutExtensions.cs" Link="Util\TaskTimeoutExtensions.cs" />
23+
<Compile Include="..\submodules\JavaScriptServices\src\Microsoft.AspNetCore.SpaServices.Extensions\Util\TcpPortFinder.cs" Link="Util\TcpPortFinder.cs" />
24+
</ItemGroup>
25+
26+
27+
28+
<ItemGroup>
29+
30+
</ItemGroup>
31+
<ItemGroup>
32+
<Folder Include="Npm\" />
33+
<Folder Include="Util\" />
34+
</ItemGroup>
35+
36+
</Project>

src/VueCliMiddleware.sln

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio 15
4+
VisualStudioVersion = 15.0.28010.2026
5+
MinimumVisualStudioVersion = 10.0.40219.1
6+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VueCliMiddleware", "VueCliMiddleware.csproj", "{6EF79200-935A-4EBB-BB10-EB233A360210}"
7+
EndProject
8+
Global
9+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
10+
Debug|Any CPU = Debug|Any CPU
11+
Release|Any CPU = Release|Any CPU
12+
EndGlobalSection
13+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
14+
{6EF79200-935A-4EBB-BB10-EB233A360210}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15+
{6EF79200-935A-4EBB-BB10-EB233A360210}.Debug|Any CPU.Build.0 = Debug|Any CPU
16+
{6EF79200-935A-4EBB-BB10-EB233A360210}.Release|Any CPU.ActiveCfg = Release|Any CPU
17+
{6EF79200-935A-4EBB-BB10-EB233A360210}.Release|Any CPU.Build.0 = Release|Any CPU
18+
EndGlobalSection
19+
GlobalSection(SolutionProperties) = preSolution
20+
HideSolutionNode = FALSE
21+
EndGlobalSection
22+
GlobalSection(ExtensibilityGlobals) = postSolution
23+
SolutionGuid = {A9280CDC-FB96-4CF4-B48A-048A3355168A}
24+
EndGlobalSection
25+
EndGlobal

src/VueDevelopmentServerMiddleware.cs

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
2+
3+
using Microsoft.AspNetCore.Builder;
4+
using Microsoft.Extensions.Logging;
5+
using Microsoft.AspNetCore.NodeServices.Npm;
6+
using Microsoft.AspNetCore.SpaServices.Util;
7+
using System;
8+
using System.IO;
9+
using System.Collections.Generic;
10+
using System.Text.RegularExpressions;
11+
using System.Threading.Tasks;
12+
using Microsoft.AspNetCore.SpaServices.Extensions.Util;
13+
using Microsoft.AspNetCore.SpaServices;
14+
using Microsoft.AspNetCore.NodeServices.Util;
15+
16+
namespace VueCliMiddleware
17+
{
18+
internal static class VueCliMiddleware
19+
{
20+
private const string LogCategoryName = "Microsoft.AspNetCore.SpaServices";
21+
private static TimeSpan RegexMatchTimeout = TimeSpan.FromSeconds(5); // This is a development-time only feature, so a very long timeout is fine
22+
23+
public static void Attach(
24+
ISpaBuilder spaBuilder,
25+
string npmScriptName, int port = 0)
26+
{
27+
var sourcePath = spaBuilder.Options.SourcePath;
28+
if (string.IsNullOrEmpty(sourcePath))
29+
{
30+
throw new ArgumentException("Cannot be null or empty", nameof(sourcePath));
31+
}
32+
33+
if (string.IsNullOrEmpty(npmScriptName))
34+
{
35+
throw new ArgumentException("Cannot be null or empty", nameof(npmScriptName));
36+
}
37+
38+
// Start vue-cli and attach to middleware pipeline
39+
var appBuilder = spaBuilder.ApplicationBuilder;
40+
var logger = LoggerFinder.GetOrCreateLogger(appBuilder, LogCategoryName);
41+
var portTask = StartVueCliServerAsync(sourcePath, npmScriptName, logger, port);
42+
43+
// Everything we proxy is hardcoded to target http://localhost because:
44+
// - the requests are always from the local machine (we're not accepting remote
45+
// requests that go directly to the vue-cli server)
46+
// - given that, there's no reason to use https, and we couldn't even if we
47+
// wanted to, because in general the vue-cli server has no certificate
48+
var targetUriTask = portTask.ContinueWith(
49+
task =>
50+
new UriBuilder("http", "localhost", task.Result).Uri);
51+
52+
SpaProxyingExtensions.UseProxyToSpaDevelopmentServer(spaBuilder, () =>
53+
{
54+
// On each request, we create a separate startup task with its own timeout. That way, even if
55+
// the first request times out, subsequent requests could still work.
56+
var timeout = spaBuilder.Options.StartupTimeout;
57+
return targetUriTask.WithTimeout(timeout,
58+
$"The vue-cli server did not start listening for requests " +
59+
$"within the timeout period of {timeout.Seconds} seconds. " +
60+
$"Check the log output for error information.");
61+
});
62+
}
63+
64+
private static async Task<int> StartVueCliServerAsync(
65+
string sourcePath, string npmScriptName, ILogger logger, int portNumber)
66+
{
67+
if (portNumber < 80)
68+
portNumber = TcpPortFinder.FindAvailablePort();
69+
logger.LogInformation($"Starting server on port {portNumber}...");
70+
71+
var envVars = new Dictionary<string, string>
72+
{
73+
{ "PORT", portNumber.ToString() },
74+
{ "DEV_SERVER_PORT", portNumber.ToString() }, // vue cli 3 uses --port {number}, included below
75+
{ "BROWSER", "none" }, // We don't want vue-cli to open its own extra browser window pointing to the internal dev server port
76+
};
77+
var npmScriptRunner = new NpmScriptRunner(sourcePath, npmScriptName, $"--port {portNumber}", envVars);
78+
npmScriptRunner.AttachToLogger(logger);
79+
80+
using (var stdErrReader = new EventedStreamStringReader(npmScriptRunner.StdErr))
81+
{
82+
try
83+
{
84+
// Although the Vue dev server may eventually tell us the URL it's listening on,
85+
// it doesn't do so until it's finished compiling, and even then only if there were
86+
// no compiler warnings. So instead of waiting for that, consider it ready as soon
87+
// as it starts listening for requests.
88+
await npmScriptRunner.StdOut.WaitForMatch(
89+
new Regex("running at", RegexOptions.None, RegexMatchTimeout));
90+
}
91+
catch (EndOfStreamException ex)
92+
{
93+
throw new InvalidOperationException(
94+
$"The NPM script '{npmScriptName}' exited without indicating that the " +
95+
$"server was listening for requests. The error output was: " +
96+
$"{stdErrReader.ReadAsString()}", ex);
97+
}
98+
}
99+
100+
return portNumber;
101+
}
102+
}
103+
104+
105+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
2+
3+
using Microsoft.AspNetCore.Builder;
4+
using Microsoft.AspNetCore.SpaServices;
5+
using System;
6+
7+
namespace VueCliMiddleware
8+
{
9+
/// <summary>
10+
/// Extension methods for enabling Vue development server middleware support.
11+
/// </summary>
12+
public static class VueCliMiddlewareExtensions
13+
{
14+
/// <summary>
15+
/// Handles requests by passing them through to an instance of the vue-cli server.
16+
/// This means you can always serve up-to-date CLI-built resources without having
17+
/// to run the vue-cli server manually.
18+
///
19+
/// This feature should only be used in development. For production deployments, be
20+
/// sure not to enable the vue-cli server.
21+
/// </summary>
22+
/// <param name="spaBuilder">The <see cref="ISpaBuilder"/>.</param>
23+
/// <param name="npmScript">The name of the script in your package.json file that launches the vue-cli server.</param>
24+
/// <param name="port">Specify vue cli server port number. If &lt; 80, uses random port. </param>
25+
public static void UseVueCli(
26+
this ISpaBuilder spaBuilder,
27+
string npmScript, int port = 0)
28+
{
29+
if (spaBuilder == null)
30+
{
31+
throw new ArgumentNullException(nameof(spaBuilder));
32+
}
33+
34+
var spaOptions = spaBuilder.Options;
35+
36+
if (string.IsNullOrEmpty(spaOptions.SourcePath))
37+
{
38+
throw new InvalidOperationException($"To use {nameof(UseVueCli)}, you must supply a non-empty value for the {nameof(SpaOptions.SourcePath)} property of {nameof(SpaOptions)} when calling {nameof(SpaApplicationBuilderExtensions.UseSpa)}.");
39+
}
40+
41+
VueCliMiddleware.Attach(spaBuilder, npmScript, port);
42+
}
43+
}
44+
}

submodules/JavaScriptServices

Submodule JavaScriptServices added at ea3a7bc

0 commit comments

Comments
 (0)