Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 99 additions & 2 deletions src/Microsoft.DocAsCode.App/RunServe.cs
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics;
using System.Runtime.InteropServices;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.DocAsCode.Common;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.DocAsCode.Plugins;

namespace Microsoft.DocAsCode;

internal static class RunServe
{
public static void Exec(string folder, string host, int? port)
public static void Exec(string folder, string host, int? port, bool openBrowser, string openBrowserRelativePath)
{
if (string.IsNullOrEmpty(folder))
folder = Directory.GetCurrentDirectory();
Expand Down Expand Up @@ -43,11 +47,104 @@ public static void Exec(string folder, string host, int? port)
Console.WriteLine($"Serving \"{folder}\" on {url}. Press Ctrl+C to shut down.");
using var app = builder.Build();
app.UseFileServer(fileServerOptions);
app.Run();

if (openBrowser)
{
string relativePath = openBrowserRelativePath;
var launchUrl = string.IsNullOrEmpty(relativePath)
? url
: Path.Combine(url, ResolveOutputHtmlRelativePath(folder, relativePath));

// Start web server.
app.Start();

// Launch browser process.

Console.WriteLine($"Launching browser with url: {url}.");
Comment thread
filzrev marked this conversation as resolved.
Outdated
using var process = LaunchBrowser(launchUrl);
Comment thread
filzrev marked this conversation as resolved.
Outdated

// Wait until server exited.
app.WaitForShutdown();

// process object is disposed. (Note:Launched process remain running.after dispose)
Comment thread
filzrev marked this conversation as resolved.
Outdated
}
else
{
app.Run();
}
}
catch (System.Reflection.TargetInvocationException)
{
Logger.LogError($"Error serving \"{folder}\" on {url}, check if the port is already being in use.");
}
}

/// <summary>
/// Resolve output HTML file path by `manifest.json` file.
/// </summary>
private static string ResolveOutputHtmlRelativePath(string folder, string relativePath)
{
var manifestPath = Path.GetFullPath(Path.Combine(folder, "manifest.json"));
if (!File.Exists(manifestPath))
return string.Empty;

try
{
Manifest manifest = JsonUtility.Deserialize<Manifest>(manifestPath);

// Try to find output html file (html->html)
OutputFileInfo outputFileInfo = manifest.FindOutputFileInfo(relativePath);
if (outputFileInfo != null)
return outputFileInfo.RelativePath;

// Try to resolve output HTML file. (md->html)
relativePath = relativePath.Replace('\\', '/'); // Normalize path.
var manifestFile = manifest.Files
.Where(x => FilePathComparer.OSPlatformSensitiveRelativePathComparer.Equals(x.SourceRelativePath, relativePath))
.FirstOrDefault(x => x.OutputFiles.TryGetValue(".html", out outputFileInfo));

if (outputFileInfo != null)
return outputFileInfo.RelativePath;
}
catch (Exception)
Comment thread
yufeih marked this conversation as resolved.
Outdated
{
throw; // Unexpected exception occurred.(e.g. Failed to deserialize Manifest)
}

// Failed to resolve output HTML file.
Logger.LogError($"Failed to resolve output HTML file path. sourceRelativePath: {relativePath}");
return string.Empty;
}

private static Process LaunchBrowser(string url)
{
try
{
// Windows
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
return Process.Start("cmd", new[] { "/C", "start", url });
}

// Linux
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
return Process.Start("xdg-open", url);
}

// OSX
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
return Process.Start("open", url);
}

Logger.LogError($"Could not launch the browser process. Unknown OS platform: {RuntimeInformation.OSDescription}");
}
catch (Exception ex)
Comment thread
filzrev marked this conversation as resolved.
{
Logger.LogError($"Could not launch the browser process, with error - {ex.Message}");
}

return null;
}
}
2 changes: 1 addition & 1 deletion src/docfx/Models/BuildCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public override int Execute(CommandContext context, BuildCommandOptions settings
var serveDirectory = RunBuild.Exec(config.Item, new(), baseDirectory, settings.OutputFolder);

if (settings.Serve)
RunServe.Exec(serveDirectory, settings.Host, settings.Port);
RunServe.Exec(serveDirectory, settings.Host, settings.Port, settings.OpenBrowser, settings.OpenBrowserRelativePath);
});
}

Expand Down
8 changes: 8 additions & 0 deletions src/docfx/Models/BuildCommandOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,14 @@ internal class BuildCommandOptions : LogOptions
[CommandOption("-p|--port")]
public int? Port { get; set; }

[Description("Open a web browser when the hosted website starts.")]
[CommandOption("--open-browser")]
public bool OpenBrowser { get; set; }

[Description("Specify the relative path to open.")]
[CommandOption("--open-browser-relative-path")]
public string OpenBrowserRelativePath { get; set; }

[Description("Run in debug mode. With debug mode, raw model and view model will be exported automatically when it encounters error when applying templates. If not specified, it is false.")]
[CommandOption("--debug")]
public bool EnableDebugMode { get; set; }
Expand Down
2 changes: 1 addition & 1 deletion src/docfx/Models/DefaultCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public override int Execute(CommandContext context, Options options)

if (options.Serve && serveDirectory is not null)
{
RunServe.Exec(serveDirectory, options.Host, options.Port);
RunServe.Exec(serveDirectory, options.Host, options.Port, options.OpenBrowser, options.OpenBrowserRelativePath);
}
});
}
Expand Down
10 changes: 9 additions & 1 deletion src/docfx/Models/ServeCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,18 @@ internal class Settings : CommandSettings
[Description("Specify the port of the hosted website")]
[CommandOption("-p|--port")]
public int? Port { get; set; }

[Description("Open a web browser when the hosted website starts.")]
[CommandOption("--open-browser")]
public bool OpenBrowser { get; set; }

[Description("Specify the relative path to open.")]
[CommandOption("--open-browser-relative-path")]
public string OpenBrowserRelativePath { get; set; }
}

public override int Execute([NotNull] CommandContext context, [NotNull] Settings options)
{
return CommandHelper.Run(() => RunServe.Exec(options.Folder, options.Host, options.Port));
return CommandHelper.Run(() => RunServe.Exec(options.Folder, options.Host, options.Port, options.OpenBrowser, options.OpenBrowserRelativePath));
}
}