Skip to content

Commit eada852

Browse files
committed
fix(docs): reference resolution and missing site info
1 parent b6497fa commit eada852

File tree

3 files changed

+153
-114
lines changed

3 files changed

+153
-114
lines changed

docs/generators/lunr.fsx

Lines changed: 46 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33

44
#if !FORNAX
55
#load "../loaders/contentloader.fsx"
6-
76
#load "../loaders/apirefloader.fsx"
87
#load "../loaders/globalloader.fsx"
98
#endif
@@ -19,61 +18,63 @@ type Entry = {
1918
content: string
2019
}
2120
let generate (ctx : SiteContents) (projectRoot: string) (page: string) =
22-
let siteInfo = ctx.TryGetValue<Globalloader.SiteInfo>().Value
23-
let rootUrl = siteInfo.root_url
21+
let siteInfo = ctx.TryGetValue<Globalloader.SiteInfo>()
22+
let rootUrl =
23+
match siteInfo with
24+
| Some info -> info.root_url
25+
| None -> ""
2426

2527
let pages = ctx.TryGetValues<Contentloader.Post> () |> Option.defaultValue Seq.empty
2628
let entries =
27-
pages
28-
|> Seq.map (fun n ->
29-
{uri = rootUrl + "/" + n.link.Replace("content/", ""); title = n.title; content = n.text}
30-
)
29+
pages
30+
|> Seq.map (fun n ->
31+
{uri = rootUrl + "/" + n.link.Replace("content/", ""); title = n.title; content = n.text}
32+
)
3133

3234
let all = ctx.TryGetValues<AssemblyEntities>()
3335
let refs =
34-
match all with
35-
| None -> []
36-
| Some all ->
37-
all
38-
|> Seq.toList
39-
|> List.collect (fun n ->
40-
let generatorOutput = n.GeneratorOutput
41-
let allModules = n.Modules
42-
let allTypes = n.Types
36+
match all with
37+
| None -> []
38+
| Some all ->
39+
all
40+
|> Seq.toList
41+
|> List.collect (fun n ->
42+
let generatorOutput = n.GeneratorOutput
43+
let allModules = n.Modules
44+
let allTypes = n.Types
4345

44-
let gen =
45-
let ctn =
46-
let namespaces = generatorOutput.Collection.Namespaces |> Seq.map (fun n -> n.Name) |> String.concat " "
47-
$"%s{generatorOutput.Collection.CollectionName} \n %s{namespaces}"
48-
{uri = $"{rootUrl}/reference/%s{n.Label}/index.html"; title = $"%s{n.Label} - API Reference"; content = ctn }
46+
let gen =
47+
let ctn =
48+
let namespaces = generatorOutput.Collection.Namespaces |> Seq.map (fun n -> n.Name) |> String.concat " "
49+
$"%s{generatorOutput.Collection.CollectionName} \n %s{namespaces}"
50+
{uri = $"{rootUrl}/reference/%s{n.Label}/index.html"; title = $"%s{n.Label} - API Reference"; content = ctn }
4951

50-
let mdlsGen =
51-
allModules
52-
|> Seq.map (fun m ->
53-
let m = m.Info
54-
let cnt =
55-
sprintf "%s \n %s \n %s"
56-
m.Name
57-
(m.Comment.Xml |> Option.map string |> Option.defaultValue "")
58-
(m.NestedEntities |> List.map (fun m -> $"{m.Name} {m.Comment.Xml}") |> String.concat " ")
52+
let mdlsGen =
53+
allModules
54+
|> Seq.map (fun m ->
55+
let m = m.Info
56+
let cnt =
57+
sprintf "%s \n %s \n %s"
58+
m.Name
59+
(m.Comment.Xml |> Option.map string |> Option.defaultValue "")
60+
(m.NestedEntities |> List.map (fun m -> $"{m.Name} {m.Comment.Xml}") |> String.concat " ")
5961

60-
{uri = $"{rootUrl}/reference/%s{n.Label}/%s{m.UrlBaseName}.html"; title = m.Name; content = cnt }
61-
)
62+
{uri = $"{rootUrl}/reference/%s{n.Label}/%s{m.UrlBaseName}.html"; title = m.Name; content = cnt }
63+
)
6264

63-
let tsGen =
64-
allTypes
65-
|> Seq.map (fun m ->
66-
let m = m.Info
67-
let cnt =
68-
let getComment xml = xml |> Option.map string |> Option.defaultValue ""
69-
let allMembers = m.AllMembers |> List.map (fun m -> $"{m.Name} {m.Comment.Xml |> getComment}" ) |> String.concat " "
70-
$"%s{m.Name} \n %s{m.Comment.Xml |> getComment} \n %s{allMembers}"
65+
let tsGen =
66+
allTypes
67+
|> Seq.map (fun m ->
68+
let m = m.Info
69+
let cnt =
70+
let getComment xml = xml |> Option.map string |> Option.defaultValue ""
71+
let allMembers = m.AllMembers |> List.map (fun m -> $"{m.Name} {m.Comment.Xml |> getComment}" ) |> String.concat " "
72+
$"%s{m.Name} \n %s{m.Comment.Xml |> getComment} \n %s{allMembers}"
7173

72-
73-
{uri = $"{rootUrl}/reference/%s{n.Label}/%s{m.UrlBaseName}.html"; title = m.Name; content = cnt }
74-
)
75-
[yield! entries; gen; yield! mdlsGen; yield! tsGen]
76-
)
74+
{uri = $"{rootUrl}/reference/%s{n.Label}/%s{m.UrlBaseName}.html"; title = m.Name; content = cnt }
75+
)
76+
[yield! entries; gen; yield! mdlsGen; yield! tsGen]
77+
)
7778

7879
[|yield! entries; yield! refs|]
7980
|> JsonSerializer.Serialize

docs/generators/partials/menu.fsx

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,14 @@ open Html
1010

1111
let menu (ctx : SiteContents) (page: string) =
1212
let shortcuts = ctx.GetValues<Pageloader.Shortcut> ()
13-
let all = ctx.GetValues<Apirefloader.AssemblyEntities>()
13+
let all = ctx.TryGetValues<Apirefloader.AssemblyEntities>() |> Option.defaultValue Seq.empty
1414

1515
let content = ctx.GetValues<Contentloader.Post> ()
16-
let siteInfo = ctx.TryGetValue<Globalloader.SiteInfo>().Value
17-
let rootUrl = siteInfo.root_url
16+
let siteInfo = ctx.TryGetValue<Globalloader.SiteInfo>()
17+
let rootUrl =
18+
match siteInfo with
19+
| Some info -> info.root_url
20+
| None -> ""
1821

1922
let group = content |> Seq.tryFind (fun n -> n.title = page) |> Option.map (fun n -> n.category)
2023

@@ -28,6 +31,25 @@ let menu (ctx : SiteContents) (page: string) =
2831
|> Seq.filter (fun n -> n.category = Contentloader.HowTo && not n.hide_menu )
2932
|> Seq.sortBy (fun n -> n.menu_order)
3033

34+
let apiReferencesSection =
35+
if Seq.isEmpty all then
36+
// If no API references are available, don't show the section
37+
[]
38+
else
39+
[
40+
li [Class "dd-item parent"] [
41+
a [if group = None then Class "active" else Class ""] [!! "API References"]
42+
ul [Class "child"] [
43+
for r in all ->
44+
li [Class "dd-item"] [
45+
a [Href (rootUrl + "/reference/" + r.Label + "/index.html"); if r.Label = page then Class "active" else Class "" ] [
46+
!! r.Label
47+
]
48+
]
49+
]
50+
]
51+
]
52+
3153
let menuHeader =
3254
[
3355
li [Class "dd-item"] [
@@ -44,17 +66,7 @@ let menu (ctx : SiteContents) (page: string) =
4466
]
4567
]
4668
]
47-
li [Class "dd-item parent"] [
48-
a [if group = None then Class "active" else Class ""] [!! "API References"]
49-
ul [Class "child"] [
50-
for r in all ->
51-
li [Class "dd-item"] [
52-
a [Href (rootUrl + "/reference/" + r.Label + "/index.html"); if r.Label = page then Class "active" else Class "" ] [
53-
!! r.Label
54-
]
55-
]
56-
]
57-
]
69+
yield! apiReferencesSection
5870
]
5971

6072
let renderShortcuts =
@@ -77,11 +89,15 @@ let menu (ctx : SiteContents) (page: string) =
7789
!! """<p>Built with <a href="https://github.com/ionide/Fornax">Fornax</a>"""
7890
]
7991

92+
let title =
93+
match siteInfo with
94+
| Some info -> info.title
95+
| None -> "FSharpLint"
8096

8197
nav [Id "sidebar"] [
8298
div [Id "header-wrapper"] [
8399
div [Id "header"] [
84-
h2 [Id "logo"] [!! siteInfo.title]
100+
h2 [Id "logo"] [!! title]
85101
]
86102
div [Class "searchbox"] [
87103
label [HtmlProperties.Custom ("for", "search-by")] [i [Class "fas fa-search"] []]

docs/loaders/apirefloader.fsx

Lines changed: 76 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -30,64 +30,86 @@ let rec collectModules pn pu nn nu (m: ApiDocEntity) =
3030

3131
let loader (projectRoot: string) (siteContet: SiteContents) =
3232
try
33-
// Try to find FSharpLint.Core.dll in the project build output
34-
let projectDir = Path.Combine(projectRoot, "..", "src", "FSharpLint.Core")
35-
let binDir = Path.Combine(projectDir, "bin", "Release", "net9.0")
36-
let dllPath = Path.Combine(binDir, "FSharpLint.Core.dll")
37-
38-
let dlls =
39-
[
40-
"FSharpLint.Core", dllPath
33+
// We need the console location as it contains all the dependencies
34+
let projectDir = Path.Combine(projectRoot, "..", "src", "FSharpLint.Console")
35+
let dotNetMoniker = "net9.0"
36+
let projectName = "FSharpLint.Console"
37+
let projectArtifactName = "FSharpLint.Core.dll"
38+
// Try multiple possible locations for the assembly
39+
let possiblePaths = [
40+
// CI build output
41+
Path.Combine("..", "build", projectArtifactName)
42+
// Release build
43+
Path.Combine(projectDir, "bin", "Release", dotNetMoniker, projectArtifactName)
44+
// Debug build
45+
Path.Combine(projectDir, "bin", "Debug", dotNetMoniker, projectArtifactName)
46+
// Default build output (no custom output path)
47+
Path.Combine(projectDir, "bin", "Release", projectArtifactName)
48+
Path.Combine(projectDir, "bin", "Debug", projectArtifactName)
4149
]
42-
let libs =
43-
[
44-
binDir
45-
]
46-
for (label, dll) in dlls do
47-
if File.Exists dll then
48-
let inputs = [ApiDocInput.FromFile(dll)]
49-
let output = ApiDocs.GenerateModel(inputs, label, [], libDirs = libs)
5050

51-
let allModules =
52-
output.Collection.Namespaces
53-
|> List.collect (fun n ->
54-
List.collect (collectModules n.Name n.Name n.Name n.Name) n.Entities
55-
)
51+
let foundDll = possiblePaths |> List.tryFind File.Exists
52+
53+
match foundDll with
54+
| Some dllPath ->
55+
let binDir = Path.GetDirectoryName(dllPath)
56+
printfn $"Found assembly at: %s{dllPath}"
57+
printfn $"Using lib directory: %s{binDir}"
58+
59+
let libs = [binDir]
60+
61+
// Try to load with minimal dependencies first
62+
let inputs = [ApiDocInput.FromFile(dllPath, mdcomments = true)]
63+
try
64+
let output = ApiDocs.GenerateModel(inputs, projectName, [], libDirs = libs)
65+
66+
let allModules =
67+
output.Collection.Namespaces
68+
|> List.collect (fun n ->
69+
List.collect (collectModules n.Name n.Name n.Name n.Name) n.Entities
70+
)
5671

57-
let allTypes =
58-
[
59-
yield!
60-
output.Collection.Namespaces
61-
|> List.collect (fun n ->
62-
n.Entities |> List.choose (fun t ->
63-
if t.IsTypeDefinition then
64-
Some {ParentName = n.Name; ParentUrlName = n.Name; NamespaceName = n.Name; NamespaceUrlName = n.Name; Info = t}
65-
else
66-
None)
67-
)
68-
yield!
69-
allModules
70-
|> List.collect (fun n ->
71-
// Get nested types from nested entities
72-
n.Info.NestedEntities
73-
|> List.choose (fun e ->
74-
if e.IsTypeDefinition then
75-
Some {ParentName = n.Info.Name; ParentUrlName = n.Info.UrlBaseName; NamespaceName = n.NamespaceName; NamespaceUrlName = n.NamespaceUrlName; Info = e}
76-
else
77-
None)
78-
)
79-
]
80-
let entities = {
81-
Label = label
82-
Modules = allModules
83-
Types = allTypes
84-
GeneratorOutput = output
85-
}
86-
siteContet.Add entities
87-
else
88-
printfn "Warning: Could not find assembly at %s" dll
72+
let allTypes =
73+
[
74+
yield!
75+
output.Collection.Namespaces
76+
|> List.collect (fun n ->
77+
n.Entities |> List.choose (fun t ->
78+
if t.IsTypeDefinition then
79+
Some {ParentName = n.Name; ParentUrlName = n.Name; NamespaceName = n.Name; NamespaceUrlName = n.Name; Info = t}
80+
else
81+
None)
82+
)
83+
yield!
84+
allModules
85+
|> List.collect (fun n ->
86+
// Get nested types from nested entities
87+
n.Info.NestedEntities
88+
|> List.choose (fun e ->
89+
if e.IsTypeDefinition then
90+
Some {ParentName = n.Info.Name; ParentUrlName = n.Info.UrlBaseName; NamespaceName = n.NamespaceName; NamespaceUrlName = n.NamespaceUrlName; Info = e}
91+
else
92+
None)
93+
)
94+
]
95+
let entities = {
96+
Label = "FSharpLint.Core"
97+
Modules = allModules
98+
Types = allTypes
99+
GeneratorOutput = output
100+
}
101+
siteContet.Add entities
102+
printfn $"Successfully loaded API documentation for {projectName}"
103+
with
104+
| ex ->
105+
printfn $"Failed to generate API docs from %s{dllPath}: %A{ex}"
106+
printfn "Continuing without API documentation..."
107+
| None ->
108+
printfn $"Warning: Could not find {projectArtifactName} in any of the expected locations:"
109+
possiblePaths |> List.iter (printfn " - %s")
110+
printfn "API documentation will not be generated."
89111
with
90112
| ex ->
91-
printfn "%A" ex
113+
printfn "Error in API reference loader: %A" ex
92114

93115
siteContet

0 commit comments

Comments
 (0)