-
-
Notifications
You must be signed in to change notification settings - Fork 49
/
data_source.go
220 lines (186 loc) · 7.89 KB
/
data_source.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
package build
import (
"fmt"
"io/ioutil"
"log"
"os"
"path/filepath"
"plenti/readers"
"regexp"
"strconv"
"strings"
"time"
)
type content struct {
contentType string
contentPath string
contentDest string
contentDetails string
}
// DataSource builds json list from "content/" directory.
func DataSource(buildPath string, siteConfig readers.SiteConfig) {
defer Benchmark(time.Now(), "Creating data_source")
Log("\nGathering data source from 'content/' folder")
contentJSPath := buildPath + "/spa/ejected/content.js"
os.MkdirAll(buildPath+"/spa/ejected", os.ModePerm)
// Set up counter for logging output.
contentFileCounter := 0
// Start the string that will be used for allContent object.
allContentStr := "["
// Store each content file in array we can iterate over for creating static html.
allContent := []content{}
// Start the new content.js file.
err := ioutil.WriteFile(contentJSPath, []byte(`const contentSource = [`), 0755)
if err != nil {
fmt.Printf("Unable to write content.js file: %v", err)
}
// Go through all sub directories in "content/" folder.
contentFilesErr := filepath.Walk("content", func(path string, info os.FileInfo, err error) error {
if !info.IsDir() {
// Get individual path arguments.
parts := strings.Split(path, "/")
contentType := parts[1]
fileName := parts[len(parts)-1]
// Don't add _blueprint.json or other special named files starting with underscores.
if fileName[:1] != "_" {
// Get the contents of the file.
fileContentBytes, readFileErr := ioutil.ReadFile(path)
if readFileErr != nil {
fmt.Printf("Could not read content file: %s\n", readFileErr)
}
fileContentStr := string(fileContentBytes)
// Remove the "content" folder from path.
path = strings.TrimPrefix(path, "content")
// Check for index file at any level.
if fileName == "index.json" {
// Remove entire filename from path.
path = strings.TrimSuffix(path, fileName)
// Remove trailing slash, unless it's the homepage.
if path != "/" && path[len(path)-1:] == "/" {
path = strings.TrimSuffix(path, "/")
}
} else {
// Remove file extension only from path for files other than index.json.
path = strings.TrimSuffix(path, filepath.Ext(path))
}
// Get field key/values from content source.
typeFields := readers.GetTypeFields(fileContentBytes)
// Setup regex to find field name.
reField := regexp.MustCompile(`:field\((.*?)\)`)
// Create regex for allowed characters when slugifying path.
reSlugify := regexp.MustCompile("[^a-z0-9/]+")
// Check for path overrides from plenti.json config file.
for configContentType, slug := range siteConfig.Types {
if configContentType == contentType {
// Replace :filename.
slug = strings.Replace(slug, ":filename", strings.TrimSuffix(fileName, filepath.Ext(fileName)), -1)
// Replace :field().
fieldReplacements := reField.FindAllStringSubmatch(slug, -1)
// Loop through all :field() replacements found in config file.
for _, replacement := range fieldReplacements {
// Loop through all top level keys found in content source file.
for field, fieldValue := range typeFields.Fields {
// Check if field name in the replacement pattern is found in data source.
if replacement[1] == field {
// Use the field value in the path.
slug = strings.ReplaceAll(slug, replacement[0], fieldValue)
}
}
}
// Slugify output using reSlugify regex defined above.
slug = strings.Trim(reSlugify.ReplaceAllString(strings.ToLower(slug), "-"), "-")
path = slug
}
}
// Check for files outside of a type declaration.
if len(parts) == 2 {
// Remove the extension since the filename = the type name.
contentType = strings.TrimSuffix(contentType, filepath.Ext(contentType))
}
destPath := buildPath + path + "/index.html"
contentDetailsStr := "{\n" +
"\"path\": \"" + path + "\",\n" +
"\"type\": \"" + contentType + "\",\n" +
"\"filename\": \"" + fileName + "\",\n" +
"\"fields\": " + fileContentStr + "\n}"
// Create new content.js file if it doesn't already exist, or add to it if it does.
contentJSFile, openContentJSErr := os.OpenFile(contentJSPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if openContentJSErr != nil {
fmt.Printf("Could not open content.js for writing: %s", openContentJSErr)
}
// Write to the file with info from current file in "/content" folder.
defer contentJSFile.Close()
if _, err := contentJSFile.WriteString(contentDetailsStr + ","); err != nil {
log.Println(err)
}
// Encode html so it can be used as props.
encodedContentDetails := contentDetailsStr
// Remove newlines.
reN := regexp.MustCompile(`\r?\n`)
encodedContentDetails = reN.ReplaceAllString(encodedContentDetails, " ")
// Remove tabs.
reT := regexp.MustCompile(`\t`)
encodedContentDetails = reT.ReplaceAllString(encodedContentDetails, " ")
// Reduce extra whitespace to a single space.
reS := regexp.MustCompile(`\s+`)
encodedContentDetails = reS.ReplaceAllString(encodedContentDetails, " ")
// Add info for being referenced in allContent object.
allContentStr = allContentStr + encodedContentDetails + ","
content := content{
contentType: contentType,
contentPath: path,
contentDest: destPath,
contentDetails: encodedContentDetails,
}
allContent = append(allContent, content)
// Increment counter for logging purposes.
contentFileCounter++
}
}
return nil
})
if contentFilesErr != nil {
fmt.Printf("Could not get layout file: %s", contentFilesErr)
}
// End the string that will be used in allContent object.
allContentStr = strings.TrimSuffix(allContentStr, ",") + "]"
for _, currentContent := range allContent {
_, createPropsErr := SSRctx.RunScript("var props = {route: layout_content_"+currentContent.contentType+"_svelte, content: "+currentContent.contentDetails+", allContent: "+allContentStr+"};", "create_ssr")
if createPropsErr != nil {
fmt.Printf("Could not create props: %v\n", createPropsErr)
}
// Render the HTML with props needed for the current content.
_, renderHTMLErr := SSRctx.RunScript("var { html, css: staticCss} = layout_global_html_svelte.render(props);", "create_ssr")
if renderHTMLErr != nil {
fmt.Printf("Can't render htmlComponent: %v\n", renderHTMLErr)
}
// Get the rendered HTML from v8go.
renderedHTML, err := SSRctx.RunScript("html;", "create_ssr")
if err != nil {
fmt.Printf("V8go could not execute js default: %v\n", err)
}
// Get the string value of the static HTML.
renderedHTMLStr := renderedHTML.String()
// Inject the main.js script the starts the client-side app.
renderedHTMLStr = strings.Replace(renderedHTMLStr, "</head>", "<script type='module' src='https://unpkg.com/dimport?module' data-main='/spa/ejected/main.js'></script><script nomodule src='https://unpkg.com/dimport/nomodule' data-main='/spa/ejected/main.js'></script></head>", 1)
// Convert the string to byte array that can be written to file system.
htmlBytes := []byte(renderedHTMLStr)
// Create any folders need to write file.
os.MkdirAll(buildPath+currentContent.contentPath, os.ModePerm)
// Write static HTML to the filesystem.
htmlWriteErr := ioutil.WriteFile(currentContent.contentDest, htmlBytes, 0755)
if htmlWriteErr != nil {
fmt.Printf("Unable to write SSR file: %v\n", htmlWriteErr)
}
}
// Complete the content.js file.
contentJSFile, openContentJSErr := os.OpenFile(contentJSPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if openContentJSErr != nil {
fmt.Printf("Could not open content.js for writing: %s", openContentJSErr)
}
defer contentJSFile.Close()
if _, err := contentJSFile.WriteString("];\n\nexport default contentSource;"); err != nil {
log.Println(err)
}
Log("Number of content files used: " + strconv.Itoa(contentFileCounter))
}