Skip to content

Commit

Permalink
Fingerprint SPA to bust cache (#295).
Browse files Browse the repository at this point in the history
  • Loading branch information
jimafisk committed Nov 5, 2023
1 parent 4bc90e3 commit 9d084ad
Show file tree
Hide file tree
Showing 13 changed files with 101 additions and 70 deletions.
15 changes: 8 additions & 7 deletions cmd/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ func Build() error {

// Get the full path for the build directory of the site.
buildPath := filepath.Join(".", buildDir)
spaPath := buildPath + "/" + siteConfig.EntryPointJS + "/"

// Clear out any previous build dir of the same name.
if _, buildPathExistsErr := os.Stat(buildPath); buildPathExistsErr == nil {
Expand All @@ -129,7 +130,7 @@ func Build() error {
}

// Directly copy .js that don't need compiling to the build dir.
err = build.EjectCopy(buildPath, defaults.CoreFS)
err = build.EjectCopy(spaPath, defaults.CoreFS)
if err != nil {
log.Fatal("\nError in EjectCopy build step", err)
}
Expand All @@ -141,37 +142,37 @@ func Build() error {
}

// Directly copy media to the build dir.
err = build.MediaCopy(buildPath)
err = build.MediaCopy(buildPath, spaPath)
if err != nil {
log.Fatal("\nError in MediaCopy build step", err)
}

// Prep the client SPA.
err = build.Client(buildPath, defaults.CoreFS, defaults.CompilerFS)
err = build.Client(spaPath, defaults.CoreFS, defaults.CompilerFS)
if err != nil {
log.Fatal("\nError in Client build step", err)
}

// Build JSON from "content/" directory.
err = build.DataSource(buildPath, siteConfig)
err = build.DataSource(buildPath, spaPath, siteConfig)
if err != nil {
log.Fatal("\nError in DataSource build step", err)
}

// Run Gopack (custom Snowpack alternative) on app for ESM support.
err = build.Gopack(buildPath, buildPath+"/spa/core/main.js")
err = build.Gopack(buildPath, spaPath, spaPath+"core/main.js")
if err != nil {
log.Fatal("\nError in Gopack main.js build step", err)
}

// Run Gopack (custom Snowpack alternative) on dynamically imported adminMenu.
err = build.Gopack(buildPath, buildPath+"/spa/core/cms/admin_menu.js")
err = build.Gopack(buildPath, spaPath, spaPath+"core/cms/admin_menu.js")
if err != nil {
log.Fatal("\nError in Gopack admin_menu.svelte build step", err)
}

// Run Gopack manually on dynamic imports
err = build.GopackDynamic(buildPath)
err = build.GopackDynamic(buildPath, spaPath)
if err != nil {
log.Fatal("\nError in GopackDynamic build step", err)
}
Expand Down
24 changes: 12 additions & 12 deletions cmd/build/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,14 @@ import (
var SSRctx *v8go.Context

// Client builds the SPA.
func Client(buildPath string, coreFS embed.FS, compilerFS embed.FS) error {
func Client(spaPath string, coreFS embed.FS, compilerFS embed.FS) error {

defer Benchmark(time.Now(), "Compiling client SPA with Svelte")

Log("\nCompiling client SPA with svelte")

stylePath := buildPath + "/spa/bundle.css"
allLayoutsPath := buildPath + "/spa/generated/layouts.js"
stylePath := spaPath + "bundle.css"
allLayoutsPath := spaPath + "generated/layouts.js"
// Initialize string for layouts.js component list.
var allLayoutsStr string

Expand Down Expand Up @@ -125,7 +125,7 @@ func Client(buildPath string, coreFS embed.FS, compilerFS embed.FS) error {
}
componentStr = string(componentBytes)
}
destPath := buildPath + "/spa/" + strings.TrimSuffix(path, ".svelte") + ".js"
destPath := spaPath + strings.TrimSuffix(path, ".svelte") + ".js"
err = (compileSvelte(ctx, SSRctx, path, componentStr, destPath, stylePath))
if err != nil {
fmt.Printf("Could not compile '%s' Svelte component: %s", path, err)
Expand All @@ -140,11 +140,11 @@ func Client(buildPath string, coreFS embed.FS, compilerFS embed.FS) error {
if layoutFileInfo.IsDir() {
return nil
}
err = copyNonSvelteFiles(layoutPath, buildPath)
err = copyNonSvelteFiles(layoutPath, spaPath)
if err != nil {
return err
}
compiledComponentCounter, allLayoutsStr, err = compileComponent(err, layoutPath, layoutFileInfo, buildPath, ctx, SSRctx, stylePath, allLayoutsStr, compiledComponentCounter)
compiledComponentCounter, allLayoutsStr, err = compileComponent(err, layoutPath, layoutFileInfo, spaPath, ctx, SSRctx, stylePath, allLayoutsStr, compiledComponentCounter)
if err != nil {
return err
}
Expand All @@ -158,11 +158,11 @@ func Client(buildPath string, coreFS embed.FS, compilerFS embed.FS) error {
if layoutFileInfo.IsDir() {
return nil
}
err = copyNonSvelteFiles(layoutPath, buildPath)
err = copyNonSvelteFiles(layoutPath, spaPath)
if err != nil {
return err
}
compiledComponentCounter, allLayoutsStr, err = compileComponent(err, layoutPath, layoutFileInfo, buildPath, ctx, SSRctx, stylePath, allLayoutsStr, compiledComponentCounter)
compiledComponentCounter, allLayoutsStr, err = compileComponent(err, layoutPath, layoutFileInfo, spaPath, ctx, SSRctx, stylePath, allLayoutsStr, compiledComponentCounter)
if err != nil {
return err
}
Expand All @@ -182,15 +182,15 @@ func Client(buildPath string, coreFS embed.FS, compilerFS embed.FS) error {
return nil
}

func copyNonSvelteFiles(layoutPath string, buildPath string) error {
func copyNonSvelteFiles(layoutPath string, spaPath string) error {
if filepath.Ext(layoutPath) != ".svelte" {
from, err := os.Open(layoutPath)
if err != nil {
return fmt.Errorf("Could not open non-svelte layout %s for copying: %w\n", layoutPath, err)
}
defer from.Close()

destPath := buildPath + "/spa/" + layoutPath
destPath := spaPath + layoutPath
// Create any sub directories need for filepath.
if err := os.MkdirAll(filepath.Dir(destPath), os.ModePerm); err != nil {
return fmt.Errorf("can't make folders for '%s': %w\n", destPath, err)
Expand All @@ -210,12 +210,12 @@ func copyNonSvelteFiles(layoutPath string, buildPath string) error {
return nil
}

func compileComponent(err error, layoutPath string, layoutFileInfo os.FileInfo, buildPath string, ctx *v8go.Context, SSRctx *v8go.Context, stylePath string, allLayoutsStr string, compiledComponentCounter int) (int, string, error) {
func compileComponent(err error, layoutPath string, layoutFileInfo os.FileInfo, spaPath string, ctx *v8go.Context, SSRctx *v8go.Context, stylePath string, allLayoutsStr string, compiledComponentCounter int) (int, string, error) {
if err != nil {
return compiledComponentCounter, allLayoutsStr, fmt.Errorf("can't stat %s: %w", layoutPath, err)
}
// Create destination path.
destFile := buildPath + "/spa/" + layoutPath
destFile := spaPath + layoutPath
// If the file is in .svelte format, compile it to .js
if filepath.Ext(layoutPath) == ".svelte" {
// Replace .svelte file extension with .js.
Expand Down
36 changes: 20 additions & 16 deletions cmd/build/data_source.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,11 @@ type content struct {

// Holds sitewide environment variables.
type env struct {
local string
baseurl string
entrypoint string
cms cms
local string
baseurl string
entrypointHTML string
entrypointJS string
cms cms
}
type cms struct {
repo string
Expand All @@ -72,19 +73,20 @@ type cms struct {
}

// DataSource builds json list from "content/" directory.
func DataSource(buildPath string, siteConfig readers.SiteConfig) error {
func DataSource(buildPath string, spaPath string, siteConfig readers.SiteConfig) error {

defer Benchmark(time.Now(), "Creating data_source")

Log("\nGathering data source from 'content/' folder")

// Set some defaults
contentJSPath := buildPath + "/spa/generated/content.js"
envPath := buildPath + "/spa/generated/env.js"
contentJSPath := spaPath + "generated/content.js"
envPath := spaPath + "generated/env.js"
env := env{
local: strconv.FormatBool(Local),
baseurl: siteConfig.BaseURL,
entrypoint: siteConfig.EntryPoint,
local: strconv.FormatBool(Local),
baseurl: siteConfig.BaseURL,
entrypointHTML: siteConfig.EntryPointHTML,
entrypointJS: siteConfig.EntryPointJS,
cms: cms{
repo: siteConfig.CMS.Repo,
redirectUrl: siteConfig.CMS.RedirectUrl,
Expand All @@ -96,7 +98,8 @@ func DataSource(buildPath string, siteConfig readers.SiteConfig) error {
// Create env magic prop.
envStr := "export let env = { local: " + env.local +
", baseurl: '" + env.baseurl +
"', entrypoint: '" + env.entrypoint +
"', entrypointHTML: '" + env.entrypointHTML +
"', entrypointJS: '" + env.entrypointJS +
"', cms: { repo: '" + env.cms.repo +
"', redirectUrl: '" + env.cms.redirectUrl +
"', appId: '" + env.cms.appId +
Expand Down Expand Up @@ -164,25 +167,25 @@ func DataSource(buildPath string, siteConfig readers.SiteConfig) error {
allContentStr = strings.TrimSuffix(allContentStr, ",") + "]"
// End the string that will be used in allDefaults object.
allDefaultsStr = strings.TrimSuffix(allDefaultsStr, ",") + "];\n\nexport default allDefaults;"
err = writeContentJS(buildPath+"/spa/generated/defaults.js", allDefaultsStr)
err = writeContentJS(spaPath+"generated/defaults.js", allDefaultsStr)
if err != nil {
return fmt.Errorf("\nCould not write defaults.js file")
}
// End the string that will be used in allSchemas object.
allSchemasStr = strings.TrimSuffix(allSchemasStr, ",") + "\n};\n\nexport default allSchemas;"
err = writeContentJS(buildPath+"/spa/generated/schemas.js", allSchemasStr)
err = writeContentJS(spaPath+"generated/schemas.js", allSchemasStr)
if err != nil {
return fmt.Errorf("\nCould not write schemas.js file")
}
// End the string that will be used in allComponentDefaults object.
allComponentDefaultsStr = strings.TrimSuffix(allComponentDefaultsStr, ",") + "\n};\n\nexport default allComponentDefaults;"
err = writeContentJS(buildPath+"/spa/generated/component_defaults.js", allComponentDefaultsStr)
err = writeContentJS(spaPath+"generated/component_defaults.js", allComponentDefaultsStr)
if err != nil {
return fmt.Errorf("\nCould not write component_defaults.js file")
}
// End the string that will be used in allComponentSchemas object.
allComponentSchemasStr = strings.TrimSuffix(allComponentSchemasStr, ",") + "\n};\n\nexport default allComponentSchemas;"
err = writeContentJS(buildPath+"/spa/generated/component_schemas.js", allComponentSchemasStr)
err = writeContentJS(spaPath+"generated/component_schemas.js", allComponentSchemasStr)
if err != nil {
return fmt.Errorf("\nCould not write component_schemas.js file")
}
Expand Down Expand Up @@ -450,6 +453,7 @@ func createProps(currentContent content, allContentStr string, env env) error {
", shadowContent: {}"+
", env: {local: "+env.local+
", baseurl: '"+env.baseurl+
"', entrypointJS: '"+env.entrypointJS+
"', cms: { repo: '"+env.cms.repo+
"', redirectUrl: '"+env.cms.redirectUrl+
"', appId: '"+env.cms.appId+
Expand All @@ -461,7 +465,7 @@ func createProps(currentContent content, allContentStr string, env env) error {
// Render the HTML with props needed for the current content.
entrySignature := strings.ReplaceAll(
strings.ReplaceAll(
"layouts/"+env.entrypoint,
"layouts/"+env.entrypointHTML,
"/", "_"),
".", "_")
_, err = SSRctx.RunScript(fmt.Sprintf("var { html, css: staticCss} = %s.render(props);", entrySignature), "create_ssr")
Expand Down
3 changes: 1 addition & 2 deletions cmd/build/eject_copy.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
)

// EjectCopy does a direct copy of any ejectable js files needed in spa build dir.
func EjectCopy(buildPath string, coreFS embed.FS) error {
func EjectCopy(destPath string, coreFS embed.FS) error {

defer Benchmark(time.Now(), "Copying ejectable core files for build")

Expand All @@ -24,7 +24,6 @@ func EjectCopy(buildPath string, coreFS embed.FS) error {
return fmt.Errorf("Unable to get ejected defaults: %w\n", err)
}

destPath := buildPath + "/spa/"
ejectedFilesErr := fs.WalkDir(coreDefaults, ".", func(path string, d fs.DirEntry, err error) error {
if err != nil {
return fmt.Errorf("can't stat %s: %w", path, err)
Expand Down
14 changes: 7 additions & 7 deletions cmd/build/gopack.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ var (
var alreadyConvertedFiles []string

// Gopack ensures ESM support for NPM dependencies.
func Gopack(buildPath, entrypoint string) error {
func Gopack(buildPath, spaPath, entrypoint string) error {

defer Benchmark(time.Now(), "Running Gopack")

Expand All @@ -54,17 +54,17 @@ func Gopack(buildPath, entrypoint string) error {
alreadyConvertedFiles = []string{}

// Start at the entry point for the app
err := runPack(buildPath, entrypoint)
err := runPack(buildPath, spaPath, entrypoint)
if err != nil {
return err
}

return nil
}

func runPack(buildPath, convertPath string) error {
func runPack(buildPath, spaPath, convertPath string) error {
// Destination path for dependencies
gopackDir := buildPath + "/spa/web_modules"
gopackDir := spaPath + "web_modules"

// Get the actual contents of the file we want to convert
contentBytes, err := ioutil.ReadFile(convertPath)
Expand Down Expand Up @@ -152,7 +152,7 @@ func runPack(buildPath, convertPath string) error {
foundPath = pathStr
} else if strings.HasPrefix(convertPath, gopackDir) {
// The relative import is coming from a web_module itself
// Change out of public/spa/web_modules and go into node_modules
// Change out of public/{spaPath}/web_modules and go into node_modules
modulePath := "node_modules" + strings.TrimPrefix(fullPathStr, gopackDir)
// Get the module from npm
err = copyFile(modulePath, fullPathStr)
Expand All @@ -170,7 +170,7 @@ func runPack(buildPath, convertPath string) error {
// Make sure the import/export path doesn't start with a dot (.) or double dot (..)
// and make sure that the path doesn't have a file extension.
if len(pathStr) > 0 && pathStr[:1] != "." && filepath.Ext(pathStr) == "" {
// Copy the npm file from /node_modules to /spa/web_modules
// Copy the npm file from /node_modules to /{spaPath}/web_modules
fullPathStr, err = copyNpmModule(pathStr, gopackDir)
if err != nil {
fmt.Printf("Can't copy npm module: %s", err)
Expand All @@ -189,7 +189,7 @@ func runPack(buildPath, convertPath string) error {
// Add the current file to list of already converted files.
alreadyConvertedFiles = append(alreadyConvertedFiles, fullPathStr)
// Use fullPathStr recursively to find its imports.
err = runPack(buildPath, fullPathStr)
err = runPack(buildPath, spaPath, fullPathStr)
if err != nil {
return fmt.Errorf("\nCan't runPack on %s %w", fullPathStr, err)
}
Expand Down
11 changes: 6 additions & 5 deletions cmd/build/gopack_dynamic.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,26 @@ import (
)

// Create ESM support for files that don't have static imports in the app
func GopackDynamic(buildPath string) error {
func GopackDynamic(buildPath string, spaPath string) error {

defer Benchmark(time.Now(), "Running GopackDynamic")

Log("\nRunning Gopack manually on dynamic imports")

// Dynamically imported CMS FieldWidgets
fieldWidgetPath := "/layouts/_fields"
fieldWidgetPath := "layouts/_fields"
// Check if there are any custom FieldWidgets in the project
if _, err := os.Stat(buildPath + fieldWidgetPath); !os.IsNotExist(err) {
if _, err := os.Stat(buildPath + "/" + fieldWidgetPath); !os.IsNotExist(err) {
fmt.Println(buildPath + "/" + fieldWidgetPath)
// There are custom FieldWidgets, so add ESM support for each
err = filepath.Walk(buildPath+fieldWidgetPath, func(path string, info os.FileInfo, err error) error {
err = filepath.Walk(buildPath+"/"+fieldWidgetPath, func(path string, info os.FileInfo, err error) error {
if err != nil {
return fmt.Errorf("\nCan't walk ejected FieldWidget path %s: %w", path, err)
}
if info.IsDir() {
return nil
}
err = Gopack(buildPath, buildPath+"/spa"+fieldWidgetPath+"/"+path)
err = Gopack(buildPath, spaPath, spaPath+fieldWidgetPath+"/"+path)
if err != nil {
return fmt.Errorf("\nError running Gopack for custom FieldWidget: %w", err)
}
Expand Down
10 changes: 5 additions & 5 deletions cmd/build/media_copy.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import (
)

// MediaCopy does a direct copy of any media files (e.g. images, PDFs).
func MediaCopy(buildPath string) error {
func MediaCopy(buildPath string, spaPath string) error {

defer Benchmark(time.Now(), "Copying media files into build dir")

Expand All @@ -40,8 +40,8 @@ func MediaCopy(buildPath string) error {
}
}

// Create the spa/ejected/cms/media.js file
err = createMediaIndex(buildPath, index)
// Create the {spaPath}/ejected/cms/media.js file
err = createMediaIndex(spaPath, index)
if err != nil {
return err
}
Expand Down Expand Up @@ -144,7 +144,7 @@ func copyMediaFromProject(mediaDir string, buildPath string, index []string, cop
return index, copiedSourceCounter, nil
}

func createMediaIndex(buildPath string, index []string) error {
func createMediaIndex(spaPath string, index []string) error {
result, err := json.MarshalIndent(index, "", "\t")
if err != nil {
return fmt.Errorf("Unable to marshal JSON: %w", err)
Expand All @@ -153,7 +153,7 @@ func createMediaIndex(buildPath string, index []string) error {
result = []byte("[]")
}
result = append(append([]byte("let allMedia = "), result...), []byte(";\nexport default allMedia;")...)
mediaPath := buildPath + "/spa/generated/media.js"
mediaPath := spaPath + "generated/media.js"
// Create any sub directories need for filepath.
if err := os.MkdirAll(filepath.Dir(mediaPath), os.ModePerm); err != nil {
return fmt.Errorf("can't make folders for '%s': %w\n", mediaPath, err)
Expand Down

0 comments on commit 9d084ad

Please sign in to comment.