Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 77 additions & 0 deletions importmap.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package importmap
import (
"context"
"encoding/json"
"errors"
"fmt"
"html/template"
"log/slog"
Expand Down Expand Up @@ -123,6 +124,82 @@ func (im *ImportMap) ShimPath(sp string) *ImportMap {
return im
}

func (im *ImportMap) CacheOrFetch(ctx context.Context) error {
if im.logger != nil {
im.logger.InfoContext(ctx, "checking cache and assets for packages")
}

if im.cacheDir == nil || im.assetsDir == nil {
return errors.New("cacheDir and assetsDir must be set")
}

for _, pkg := range im.packages {
if im.logger != nil {
im.logger.InfoContext(ctx, "checking package cache and assets", "package", pkg.Name)
}

cacheExists := pkg.HasCache(im.rootDir, *im.cacheDir)
assetsExist := pkg.HasAssets(im.rootDir, *im.assetsDir)

if !cacheExists {
if im.logger != nil {
im.logger.InfoContext(ctx, "cache not found, fetching", "package", pkg.Name)
}
// Fetch will build cache and assets
err := im.Fetch(ctx)
if err != nil {
return err
}
continue
}

if !assetsExist {
if im.logger != nil {
im.logger.InfoContext(ctx, "assets not found, building from cache", "package", pkg.Name)
}
// Build assets from cache
allFiles, _, err := pkg.Assets(*im.cacheDir, "")
if err != nil {
if im.logger != nil {
im.logger.ErrorContext(ctx, "error reading cache for assets", "package", pkg.Name, "error", err)
}
return err
}
for _, file := range allFiles {
if !pkg.HasAssetFile(im.rootDir, *im.assetsDir, file.LocalPath) {
err = pkg.MakeAssets(im.rootDir, *im.cacheDir, *im.assetsDir, file.LocalPath, file.Path)
if err != nil {
return err
}
}
}
}

// Always update Structure.Imports with asset paths
allFiles, _, err := pkg.Assets(*im.assetsDir, "")
if err != nil {
if im.logger != nil {
im.logger.ErrorContext(ctx, "error reading assets", "package", pkg.Name, "error", err)
}
return err
}
for _, file := range allFiles {
var as string
if len(pkg.Require) > 0 {
req := pkg.Require.Get(file.LocalPath)
if req == nil {
continue
}
as = req.Name()
} else {
as = file.LocalPath
}
im.Structure.Imports[as] = file.Path
}
}
return nil
}

func (im *ImportMap) Fetch(ctx context.Context) error {
for _, pkg := range im.packages {
if im.logger != nil {
Expand Down
45 changes: 45 additions & 0 deletions library/package.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,51 @@ func (p *Package) HasAssets(rootDir string, assetsDir string) bool {
return true
}

func (p *Package) Assets(assetsDir string, filePath string) (Files, string, error) {
// If filePath is empty, list all files in the package's asset directory
baseDir := p.AssetsDir(assetsDir)
var fullPath string
if filePath == "" {
fullPath = path.Join(baseDir)
} else {
// Remove leading slash if present
filePath = strings.TrimPrefix(filePath, "/")
fullPath = path.Join(baseDir, filePath)
}

if _, err := os.Stat(fullPath); errors.Is(err, os.ErrNotExist) {
return nil, "", fmt.Errorf("asset file %s does not exist", fullPath)
}

files, err := os.ReadDir(fullPath)
if err != nil {
return nil, "", fmt.Errorf("failed to read assets directory %s: %w", fullPath, err)
}

var assetFiles Files
for _, file := range files {
if file.IsDir() {
continue // Skip directories
}

assetPath := path.Join(baseDir, filePath, file.Name())
localPath := path.Join(filePath, file.Name())

// Ensure assetPath has a leading slash for URL usage
if !strings.HasPrefix(assetPath, "/") {
assetPath = "/" + assetPath
}

assetFiles = append(assetFiles, File{
Path: assetPath,
LocalPath: localPath,
Type: ExtractFileType(file.Name()),
})
}

return assetFiles, baseDir, nil
}

func (p *Package) HasAssetFile(rootDir string, assetsDir string, filePath string) bool {
fullPath := path.Join(rootDir, p.AssetsDir(assetsDir), filePath)

Expand Down
Loading