Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add basic static rendering #3411

Merged
merged 13 commits into from
Sep 12, 2018
21 changes: 21 additions & 0 deletions docs/specs/rendering.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
# Apply template and produce html page when output is not json
inputs:
docfx.yml: |
output:
json: false
docs/a.md:
outputs:
docs/a/index.html:
build.manifest:
---
# Use .html extension on uglify url
inputs:
docfx.yml: |
output:
json: false
uglifyUrl: true
docs/a.md:
outputs:
docs/a.html:
build.manifest:
18 changes: 18 additions & 0 deletions schemas/Conceptual.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"type": "object",
"additionalProperties": false,
"properties": {
"html": {
"type": "string"
},
"title": {
"type": "string"
},
"htmlTitle": {
"type": "string"
},
"wordCount": {
"type": "integer"
}
}
}
6 changes: 6 additions & 0 deletions schemas/docfx.json
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,12 @@
"path": {
"type": "string"
},
"json": {
"type": "boolean"
},
"uglifyUrl": {
"type": "boolean"
},
"copyResources": {
"type": "boolean"
}
Expand Down
3 changes: 2 additions & 1 deletion src/Microsoft.Docs.Template/Models/Conceptual.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ namespace Microsoft.Docs.Build
// Conceptual model is more than just an html string, it also contain other properties.
// We currently bake these other properties into PageModel to make the output flat,
// but it makes conceptual kinda special. We may consider lift them outside PageModel.
public class Conceptual
[PageSchema]
public sealed class Conceptual
{
public string Html { get; set; }

Expand Down
Empty file.
10 changes: 8 additions & 2 deletions src/docfx/build/page/BuildPage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ namespace Microsoft.Docs.Build
{
internal static class BuildPage
{
public static async Task<(IEnumerable<Error> errors, PageModel result, DependencyMap dependencies)> Build(
public static async Task<(IEnumerable<Error> errors, object result, DependencyMap dependencies)> Build(
Document file,
TableOfContentsMap tocMap,
ContributionInfo contribution,
Expand Down Expand Up @@ -43,7 +43,13 @@ internal static class BuildPage
if (error != null)
errors.Add(error);

return (errors, model, dependencies.Build());
var output = (object)model;
if (!file.Docset.Config.Output.Json && schema.Attribute is PageSchemaAttribute)
{
output = await Template.Render(model);
}

return (errors, output, dependencies.Build());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

output [](start = 28, length = 6)

this can be PageModel or string, but Build.cs always uses context.WriteJson to write it. Add a context.WriteString when it's string?

}

private static async Task<(List<Error> errors, Schema schema, PageModel model)>
Expand Down
1 change: 1 addition & 0 deletions src/docfx/cli/CommandLineOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public JObject ToJObject()
["output"] = new JObject
{
["path"] = Output != null ? (JValue)Output : JValue.CreateNull(),
["json"] = Legacy ? (JValue)true : JValue.CreateNull(),
},
};
}
Expand Down
7 changes: 4 additions & 3 deletions src/docfx/config/Config.cs
Original file line number Diff line number Diff line change
Expand Up @@ -172,14 +172,15 @@ private static (List<Error>, Config) LoadCore(string docsetPath, string configPa

if (extend)
{
var extendErros = new List<Error>();
(extendErros, finalConfigObject) = ExtendConfigs(finalConfigObject, docsetPath, restoreMap ?? new RestoreMap(docsetPath));
errors.AddRange(extendErros);
var extendErrors = new List<Error>();
(extendErrors, finalConfigObject) = ExtendConfigs(finalConfigObject, docsetPath, restoreMap ?? new RestoreMap(docsetPath));
errors.AddRange(extendErrors);
}

var deserializeErrors = new List<Error>();
(deserializeErrors, config) = JsonUtility.ToObject<Config>(finalConfigObject);
errors.AddRange(deserializeErrors);

return (errors, config);
}

Expand Down
11 changes: 11 additions & 0 deletions src/docfx/config/OutputConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,17 @@ internal sealed class OutputConfig
/// </summary>
public readonly string Path = "_site";

/// <summary>
/// Gets whether to output JSON model.
/// </summary>
public readonly bool Json = false;

/// <summary>
/// Gets whether to include `.html` in urls.
/// The default value is to generate an `index.html` for each article.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default value is to generate an index.html for each article. [](start = 12, length = 66)

Is this comment for Json?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clarified in comments

/// </summary>
public readonly bool UglifyUrl = false;

/// <summary>
/// Gets whether resources are copied to output.
/// </summary>
Expand Down
16 changes: 13 additions & 3 deletions src/docfx/docset/Document.cs
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ public static (Error error, Document doc) TryCreate(Docset docset, string path,
var isExperimental = Path.GetFileNameWithoutExtension(filePath).EndsWith(".experimental", PathUtility.PathComparison);
var routedFilePath = ApplyRoutes(filePath, docset.Config.Routes);

var sitePath = FilePathToSitePath(routedFilePath, type, schema);
var sitePath = FilePathToSitePath(routedFilePath, type, schema, docset.Config.Output.Json, docset.Config.Output.UglifyUrl);
var siteUrl = PathToAbsoluteUrl(sitePath, type, schema);
var outputPath = sitePath;
var contentType = redirectionUrl != null ? ContentType.Redirection : type;
Expand Down Expand Up @@ -293,7 +293,7 @@ internal static ContentType GetContentType(string path)
return ContentType.Page;
}

internal static string FilePathToSitePath(string path, ContentType contentType, Schema schema)
internal static string FilePathToSitePath(string path, ContentType contentType, Schema schema, bool json, bool uglifyUrl)
{
switch (contentType)
{
Expand All @@ -302,8 +302,18 @@ internal static string FilePathToSitePath(string path, ContentType contentType,
{
if (Path.GetFileNameWithoutExtension(path).Equals("index", PathUtility.PathComparison))
{
return Path.Combine(Path.GetDirectoryName(path), "index.json").Replace('\\', '/');
var extension = json ? ".json" : ".html";
return Path.Combine(Path.GetDirectoryName(path), "index" + extension).Replace('\\', '/');
}
if (json)
{
return Path.ChangeExtension(path, ".json");
}
if (uglifyUrl)
{
return Path.ChangeExtension(path, ".html");
}
return Path.Combine(Path.GetDirectoryName(path), Path.GetFileNameWithoutExtension(path), "index.html").Replace('\\', '/');
}
return Path.ChangeExtension(path, ".json");
case ContentType.TableOfContents:
Expand Down
10 changes: 5 additions & 5 deletions src/docfx/lib/JsonUtility.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ namespace Microsoft.Docs.Build
/// </summary>
internal static class JsonUtility
{
public static readonly JsonSerializer DefaultDeserializer = new JsonSerializer
public static readonly JsonSerializer DefaultSerializer = new JsonSerializer
{
NullValueHandling = NullValueHandling.Ignore,
ContractResolver = new JsonContractResolver(),
Expand Down Expand Up @@ -182,7 +182,7 @@ public static (List<Error>, object) ToObject(
var serializer = new JsonSerializer
{
NullValueHandling = NullValueHandling.Ignore,
ContractResolver = DefaultDeserializer.ContractResolver,
ContractResolver = DefaultSerializer.ContractResolver,
};
serializer.Error += HandleError;
var value = token.ToObject(type, serializer);
Expand Down Expand Up @@ -349,7 +349,7 @@ private static void ReportUnknownFields(this JToken token, List<Error> errors, T

private static Type GetCollectionItemTypeIfArrayType(Type type)
{
var contract = DefaultDeserializer.ContractResolver.ResolveContract(type);
var contract = DefaultSerializer.ContractResolver.ResolveContract(type);
if (contract is JsonObjectContract)
{
return type;
Expand All @@ -371,7 +371,7 @@ private static Type GetCollectionItemTypeIfArrayType(Type type)

private static Type GetNestedTypeAndCheckForUnknownField(Type type, JProperty prop, List<Error> errors)
{
var contract = DefaultDeserializer.ContractResolver.ResolveContract(type);
var contract = DefaultSerializer.ContractResolver.ResolveContract(type);

if (contract is JsonObjectContract objectContract)
{
Expand All @@ -396,7 +396,7 @@ private static Type GetNestedTypeAndCheckForUnknownField(Type type, JProperty pr

private static JsonPropertyCollection GetPropertiesFromJsonArrayContract(JsonArrayContract arrayContract)
{
var itemContract = DefaultDeserializer.ContractResolver.ResolveContract(arrayContract.CollectionItemType);
var itemContract = DefaultSerializer.ContractResolver.ResolveContract(arrayContract.CollectionItemType);
if (itemContract is JsonObjectContract objectContract)
return objectContract.Properties;
else if (itemContract is JsonArrayContract contract)
Expand Down
34 changes: 20 additions & 14 deletions test/docfx.Test/build/DocumentTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,28 +8,34 @@ namespace Microsoft.Docs.Build
public static class DocumentTest
{
[Theory]
[InlineData("docfx.yml", ContentType.Unknown, "docfx.yml", "/docfx.yml", "docfx.yml")]
[InlineData("docfx.json", ContentType.Unknown, "docfx.json", "/docfx.json", "docfx.json")]
[InlineData("a.md", ContentType.Page, "a.json", "/a", "a")]
[InlineData("a/b.md", ContentType.Page, "a/b.json", "/a/b", "a/b")]
[InlineData("index.md", ContentType.Page, "index.json", "/", ".")]
[InlineData("a/index.md", ContentType.Page, "a/index.json", "/a/", "a/")]
[InlineData("a.yml", ContentType.Page, "a.json", "/a", "a")]
[InlineData("a/index.yml", ContentType.Page, "a/index.json", "/a/", "a/")]
[InlineData("TOC.md", ContentType.TableOfContents, "TOC.json", "/TOC.json", "TOC.json")]
[InlineData("TOC.yml", ContentType.TableOfContents, "TOC.json", "/TOC.json", "TOC.json")]
[InlineData("TOC.json", ContentType.TableOfContents, "TOC.json", "/TOC.json", "TOC.json")]
[InlineData("image.png", ContentType.Resource, "image.png", "/image.png", "image.png")]
[InlineData("a&#/b\\.* d.png", ContentType.Resource, "a&#/b\\.* d.png", "/a&#/b/.* d.png", "a&#/b/.* d.png")]
[InlineData("docfx.yml", true, false, ContentType.Unknown, "docfx.yml", "/docfx.yml", "docfx.yml")]
[InlineData("docfx.json", true, false, ContentType.Unknown, "docfx.json", "/docfx.json", "docfx.json")]
[InlineData("a.md", true, false, ContentType.Page, "a.json", "/a", "a")]
[InlineData("a/b.md", true, false, ContentType.Page, "a/b.json", "/a/b", "a/b")]
[InlineData("index.md", true, false, ContentType.Page, "index.json", "/", ".")]
[InlineData("a/index.md", true, false, ContentType.Page, "a/index.json", "/a/", "a/")]
[InlineData("a.yml", true, false, ContentType.Page, "a.json", "/a", "a")]
[InlineData("a/index.yml", true, false, ContentType.Page, "a/index.json", "/a/", "a/")]
[InlineData("TOC.md", true, false, ContentType.TableOfContents, "TOC.json", "/TOC.json", "TOC.json")]
[InlineData("TOC.yml", true, false, ContentType.TableOfContents, "TOC.json", "/TOC.json", "TOC.json")]
[InlineData("TOC.json", true, false, ContentType.TableOfContents, "TOC.json", "/TOC.json", "TOC.json")]
[InlineData("image.png", true, false, ContentType.Resource, "image.png", "/image.png", "image.png")]
[InlineData("a&#/b\\.* d.png", true, false, ContentType.Resource, "a&#/b\\.* d.png", "/a&#/b/.* d.png", "a&#/b/.* d.png")]
[InlineData("a.md", false, false, ContentType.Page, "a/index.html", "/a/", "a/")]
[InlineData("a.md", false, true, ContentType.Page, "a.html", "/a", "a")]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"a.html", "/a", "a" [](start = 59, length = 19)

should this be "a.html", "/a.html", "a.html"?

[InlineData("a/index.md", false, false, ContentType.Page, "a/index.html", "/a/", "a/")]
[InlineData("a/index.md", false, true, ContentType.Page, "a/index.html", "/a/", "a/")]
internal static void FilePathToUrl(
string path,
bool json,
bool uglifyUrl,
ContentType expectedContentType,
string expectedSitePath,
string expectedSiteUrl,
string expectedRelativeSiteUrl)
{
Assert.Equal(expectedContentType, Document.GetContentType(path));
Assert.Equal(expectedSitePath, Document.FilePathToSitePath(path, expectedContentType, null));
Assert.Equal(expectedSitePath, Document.FilePathToSitePath(path, expectedContentType, null, json, uglifyUrl));
Assert.Equal(expectedSiteUrl, Document.PathToAbsoluteUrl(expectedSitePath, expectedContentType, null));
Assert.Equal(expectedRelativeSiteUrl, Document.PathToRelativeUrl(expectedSitePath, expectedContentType, null));
}
Expand Down
12 changes: 8 additions & 4 deletions test/docfx.Test/build/TocTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,18 @@

using System;
using System.IO;
using Newtonsoft.Json.Linq;
using Xunit;

namespace Microsoft.Docs.Build
{
public static class TocTest
{
private static readonly Docset s_docset = new Docset(
new Context(new Report(), "."),
Directory.GetCurrentDirectory(),
JsonUtility.Deserialize<Config>("{'output': { 'json': true } }".Replace('\'', '\"')).Item2,
new CommandLineOptions());

[Theory]
// same level
[InlineData(new[] { "TOC.md" }, "b.md", "TOC.json")]
Expand Down Expand Up @@ -39,12 +44,11 @@ public static class TocTest
public static void FindTocRelativePath(string[] tocFiles, string file, string expectedTocPath)
{
var builder = new TableOfContentsMapBuilder();
var docset = new Docset(new Context(new Report(), "."), Directory.GetCurrentDirectory(), new Config(), new CommandLineOptions());
var (_, document) = Document.TryCreate(docset, file);
var (_, document) = Document.TryCreate(s_docset, file);

foreach (var tocFile in tocFiles)
{
var (_, toc) = Document.TryCreate(docset, tocFile);
var (_, toc) = Document.TryCreate(s_docset, tocFile);
builder.Add(toc, new[] { document }, Array.Empty<Document>());
}

Expand Down
2 changes: 2 additions & 0 deletions test/docfx.Test/docfx.test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@ rules:
heading-not-found: off
resolve-author-failed: off
resolve-commit-failed: off
output:
json: true
10 changes: 9 additions & 1 deletion test/docfx.Test/e2e/E2ETest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ private static IEnumerable<string> FindTestSpecHeadersInFile(string path)

private static void VerifyFile(string file, string content)
{
switch (Path.GetExtension(file.ToLower()))
switch (Path.GetExtension(file.ToLowerInvariant()))
{
case ".json":
case ".manifest":
Expand All @@ -196,6 +196,14 @@ private static void VerifyFile(string file, string content)
Assert.Equal(string.Join("\n", expected), string.Join("\n", actual));
}
break;
case ".html":
if (!string.IsNullOrEmpty(content))
{
Assert.Equal(
TestUtility.NormalizeHtml(content),
TestUtility.NormalizeHtml(File.ReadAllText(file)));
}
break;
default:
Assert.Equal(
content?.Trim() ?? "",
Expand Down