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