1
+ package main
2
+
3
+ import (
4
+ "bufio"
5
+ "fmt"
6
+ "io/fs"
7
+ "log"
8
+ "os"
9
+ "path/filepath"
10
+ "sort"
11
+ "strings"
12
+ )
13
+
14
+ func main () {
15
+ // --- Instructions on how to run this script ---
16
+ // Save this code to a file named generate_index.go in the root directory
17
+ // Ensure Go is installed on your system.
18
+ // Open your terminal and navigate to the directory containing generate_index.go.
19
+ // Either - Compile the code (optional): `go build generate_index.go`
20
+ // and then run the code: `./generate_index`
21
+ // Or `go run generate_index.go` to run the Go script without compile.
22
+ // The script generates index-contents.md inside the "docs" directory.
23
+ // --- End of Instructions ---
24
+ docsDir := "docs"
25
+ indexFileName := "index-contents.md"
26
+ indexFilePath := filepath .Join (docsDir , indexFileName )
27
+ maxDepth := 5
28
+ excludeDirs := map [string ]struct {}{
29
+ "_static" : {}, "assets" : {}, "css" : {}, "fonts" : {}, "js" : {},
30
+ "release-notes" : {}, filepath .Join ("release-notes" , "8.0" ): {},
31
+ }
32
+ excludeFiles := map [string ]struct {}{
33
+ "404.md" : {}, indexFileName : {},
34
+ }
35
+ prefixesToStrip := []string {"The " , "Work with " }
36
+
37
+ if err := os .Remove (indexFilePath ); err == nil {
38
+ fmt .Printf ("Deleted existing index file: %s\n " , indexFilePath )
39
+ } else if ! os .IsNotExist (err ) {
40
+ log .Fatalf ("Failed to delete existing index file: %v" , err )
41
+ }
42
+
43
+ var fileEntries []struct {
44
+ sortKey , displayName , relPath string
45
+ indentLevel int
46
+ }
47
+
48
+ err := filepath .Walk (docsDir , func (path string , info fs.FileInfo , err error ) error {
49
+ if err != nil {
50
+ return err
51
+ }
52
+
53
+ relPath , err := filepath .Rel (docsDir , path )
54
+ if err != nil {
55
+ return err
56
+ }
57
+ relPath = filepath .ToSlash (relPath )
58
+
59
+ depth := strings .Count (relPath , "/" )
60
+ if depth > maxDepth {
61
+ if info .IsDir () {
62
+ return filepath .SkipDir
63
+ }
64
+ return nil
65
+ }
66
+
67
+ if info .IsDir () {
68
+ if _ , ok := excludeDirs [info .Name ()]; ok {
69
+ return nil
70
+ }
71
+ if _ , ok := excludeDirs [relPath ]; ok {
72
+ return nil
73
+ }
74
+ return nil
75
+ }
76
+
77
+ if ! strings .HasSuffix (info .Name (), ".md" ) {
78
+ return nil
79
+ }
80
+
81
+ if _ , ok := excludeFiles [info .Name ()]; ok {
82
+ return nil
83
+ }
84
+
85
+ dirsInPath := strings .Split (filepath .Dir (relPath ), "/" )
86
+ for i := range dirsInPath {
87
+ if _ , ok := excludeDirs [strings .Join (dirsInPath [:i + 1 ], "/" )]; ok {
88
+ return nil
89
+ }
90
+ }
91
+
92
+ if contains (dirsInPath , "release-notes" ) && info .Name () != "release-notes.md" {
93
+ return nil
94
+ }
95
+
96
+ displayName := extractDisplayName (path )
97
+ if displayName == "" {
98
+ displayName = strings .TrimSuffix (info .Name (), ".md" )
99
+ }
100
+
101
+ sortKey := stripPrefixes (displayName , prefixesToStrip )
102
+ sortKey = strings .ToLower (sortKey )
103
+
104
+ indentLevel := strings .Count (relPath , "/" ) - 1
105
+ if indentLevel < 0 {
106
+ indentLevel = 0
107
+ }
108
+
109
+ fileEntries = append (fileEntries , struct {
110
+ sortKey , displayName , relPath string
111
+ indentLevel int
112
+ }{sortKey , displayName , relPath , indentLevel })
113
+
114
+ return nil
115
+ })
116
+
117
+ if err != nil {
118
+ log .Fatalf ("Error walking the docs directory: %v" , err )
119
+ }
120
+
121
+ sort .Slice (fileEntries , func (i , j int ) bool {
122
+ return fileEntries [i ].sortKey < fileEntries [j ].sortKey
123
+ })
124
+
125
+ var content strings.Builder
126
+ content .WriteString ("# Index\n \n " )
127
+
128
+ prevDir := ""
129
+ for _ , entry := range fileEntries {
130
+ dir := filepath .Dir (entry .relPath )
131
+ dir = strings .ReplaceAll (dir , "\\ " , "/" )
132
+ if dir == "." {
133
+ dir = ""
134
+ }
135
+
136
+ if dir != prevDir && dir != "" {
137
+ dirDisplayName := strings .Replace (dir , "release-notes" , "Release notes" , - 1 )
138
+ indentLevel := strings .Count (dirDisplayName , "/" )
139
+ indent := strings .Repeat (" " , indentLevel )
140
+ content .WriteString (fmt .Sprintf ("%s- %s/\n " , indent , dirDisplayName ))
141
+ }
142
+ prevDir = dir
143
+
144
+ indent := strings .Repeat (" " , entry .indentLevel )
145
+ content .WriteString (fmt .Sprintf ("%s - [%s](%s)\n " , indent , entry .displayName , entry .relPath ))
146
+ }
147
+
148
+ if err := os .WriteFile (indexFilePath , []byte (content .String ()), 0644 ); err != nil {
149
+ log .Fatalf ("Failed to write index file: %v" , err )
150
+ }
151
+
152
+ fmt .Printf ("Index generated at %s\n " , indexFilePath )
153
+ }
154
+
155
+ func stripPrefixes (s string , prefixes []string ) string {
156
+ sLower := strings .ToLower (s )
157
+ for _ , prefix := range prefixes {
158
+ if strings .HasPrefix (sLower , strings .ToLower (prefix )) {
159
+ return s [len (prefix ):]
160
+ }
161
+ }
162
+ return s
163
+ }
164
+
165
+ func contains (slice []string , item string ) bool {
166
+ for _ , s := range slice {
167
+ if strings .EqualFold (s , item ) {
168
+ return true
169
+ }
170
+ }
171
+ return false
172
+ }
173
+
174
+ func extractDisplayName (filePath string ) string {
175
+ file , err := os .Open (filePath )
176
+ if err != nil {
177
+ fmt .Printf ("Error opening file %s: %v\n " , filePath , err )
178
+ return ""
179
+ }
180
+ defer file .Close ()
181
+
182
+ scanner := bufio .NewScanner (file )
183
+ for scanner .Scan () {
184
+ line := strings .TrimSpace (scanner .Text ())
185
+ if strings .HasPrefix (line , "# " ) {
186
+ return strings .TrimSpace (line [2 :])
187
+ }
188
+ }
189
+ if err := scanner .Err (); err != nil {
190
+ fmt .Printf ("Error reading file %s: %v\n " , filePath , err )
191
+ }
192
+ return ""
193
+ }
0 commit comments