Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add js.Batch #12641

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
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
14 changes: 14 additions & 0 deletions common/maps/scratch.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,20 @@ func (c *Scratch) Get(key string) any {
return val
}

// GetOrCreate returns the value for the given key if it exists, or creates it
// using the given func and stores that value in the map.
// For internal use.
func (c *Scratch) GetOrCreate(key string, create func() any) any {
c.mu.Lock()
defer c.mu.Unlock()
if val, found := c.values[key]; found {
return val
}
val := create()
c.values[key] = val
return val
}

// Values returns the raw backing map. Note that you should just use
// this method on the locally scoped Scratch instances you obtain via newScratch, not
// .Page.Scratch etc., as that will lead to concurrency issues.
Expand Down
6 changes: 6 additions & 0 deletions resources/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ var (
_ resource.Cloner = (*genericResource)(nil)
_ resource.ResourcesLanguageMerger = (*resource.Resources)(nil)
_ resource.Identifier = (*genericResource)(nil)
_ resource.PathProvider = (*genericResource)(nil)
_ identity.IdentityGroupProvider = (*genericResource)(nil)
_ identity.DependencyManagerProvider = (*genericResource)(nil)
_ identity.Identity = (*genericResource)(nil)
Expand Down Expand Up @@ -463,6 +464,11 @@ func (l *genericResource) Key() string {
return key
}

// TODO1 test and document this. Consider adding it to the Resource interface.
func (l *genericResource) Path() string {
return l.paths.TargetPath()
}

func (l *genericResource) MediaType() media.Type {
return l.sd.MediaType
}
Expand Down
7 changes: 7 additions & 0 deletions resources/resource/resourcetypes.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,13 @@ type MediaTypeProvider interface {
MediaType() media.Type
}

type PathProvider interface {
// Path is the relative path to this resource.
// In most cases this will be the same as the RelPermalink(),
// but it will not trigger any lazy publishing.
Path() string
}

type ResourceLinksProvider interface {
// Permalink represents the absolute link to this resource.
Permalink() string
Expand Down
118 changes: 57 additions & 61 deletions resources/resource_transformers/js/build.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2020 The Hugo Authors. All rights reserved.
// Copyright 2024 The Hugo Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand All @@ -16,25 +16,20 @@ package js
import (
"errors"
"fmt"
"io"
"os"
"path"
"path/filepath"
"regexp"
"strings"

"github.com/spf13/afero"

"github.com/gohugoio/hugo/hugofs"
"github.com/gohugoio/hugo/media"

"github.com/evanw/esbuild/pkg/api"
"github.com/gohugoio/hugo/common/herrors"
"github.com/gohugoio/hugo/common/text"

"github.com/gohugoio/hugo/hugofs"
"github.com/gohugoio/hugo/hugolib/filesystems"
"github.com/gohugoio/hugo/resources/internal"
"github.com/gohugoio/hugo/identity"
"github.com/spf13/afero"

"github.com/evanw/esbuild/pkg/api"
"github.com/gohugoio/hugo/resources"
"github.com/gohugoio/hugo/resources/resource"
)
Expand All @@ -53,46 +48,47 @@ func New(fs *filesystems.SourceFilesystem, rs *resources.Spec) *Client {
}
}

type buildTransformation struct {
optsm map[string]any
c *Client
// ProcessExernal processes a resource with the user provided options.
func (c *Client) ProcessExernal(res resources.ResourceTransformer, opts map[string]any) (resource.Resource, error) {
return res.Transform(
&buildTransformation{c: c, optsm: opts},
)
}

func (t *buildTransformation) Key() internal.ResourceTransformationKey {
return internal.NewResourceTransformationKey("jsbuild", t.optsm)
// ProcessExernal processes a resource with the given options.
func (c *Client) ProcessInternal(res resources.ResourceTransformer, opts Options) (resource.Resource, error) {
return res.Transform(
&buildTransformation{c: c, opts: opts},
)
}

func (t *buildTransformation) Transform(ctx *resources.ResourceTransformationCtx) error {
ctx.OutMediaType = media.Builtin.JavascriptType
func (c *Client) BuildBundle(opts Options) error {
return c.build(opts, nil)
}

opts, err := decodeOptions(t.optsm)
if err != nil {
return err
// Note that transformCtx may be nil.
func (c *Client) build(opts Options, transformCtx *resources.ResourceTransformationCtx) error {
dependencyManager := opts.DependencyManager
if transformCtx != nil {
dependencyManager = transformCtx.DependencyManager // TODO1
}

if opts.TargetPath != "" {
ctx.OutPath = opts.TargetPath
} else {
ctx.ReplaceOutPathExtension(".js")
if dependencyManager == nil {
dependencyManager = identity.NopManager
}

src, err := io.ReadAll(ctx.From)
if err != nil {
opts.ResolveDir = c.rs.Cfg.BaseConfig().WorkingDir // where node_modules gets resolved
opts.TsConfig = c.rs.ResolveJSConfigFile("tsconfig.json")

if err := opts.validate(); err != nil {
return err
}

opts.sourceDir = filepath.FromSlash(path.Dir(ctx.SourcePath))
opts.resolveDir = t.c.rs.Cfg.BaseConfig().WorkingDir // where node_modules gets resolved
opts.contents = string(src)
opts.mediaType = ctx.InMediaType
opts.tsConfig = t.c.rs.ResolveJSConfigFile("tsconfig.json")

buildOptions, err := toBuildOptions(opts)
if err != nil {
return err
}

buildOptions.Plugins, err = createBuildPlugins(ctx.DependencyManager, t.c, opts)
buildOptions.Plugins, err = createBuildPlugins(c, dependencyManager, opts)
if err != nil {
return err
}
Expand All @@ -113,7 +109,7 @@ func (t *buildTransformation) Transform(ctx *resources.ResourceTransformationCtx
return fmt.Errorf("inject: absolute paths not supported, must be relative to /assets")
}

m := resolveComponentInAssets(t.c.rs.Assets.Fs, impPath)
m := resolveComponentInAssets(c.rs.Assets.Fs, impPath)

if m == nil {
return fmt.Errorf("inject: file %q not found", ext)
Expand All @@ -138,7 +134,7 @@ func (t *buildTransformation) Transform(ctx *resources.ResourceTransformationCtx
}
path := loc.File
if path == stdinImporter {
path = ctx.SourcePath
path = transformCtx.SourcePath
}

errorMessage := msg.Text
Expand All @@ -154,7 +150,7 @@ func (t *buildTransformation) Transform(ctx *resources.ResourceTransformationCtx
f, err = hugofs.Os.Open(path)
} else {
var fi os.FileInfo
fi, err = t.c.sfs.Fs.Stat(path)
fi, err = c.sfs.Fs.Stat(path)
if err == nil {
m := fi.(hugofs.FileMetaInfo).Meta()
path = m.Filename
Expand Down Expand Up @@ -185,38 +181,38 @@ func (t *buildTransformation) Transform(ctx *resources.ResourceTransformationCtx
// Return 1, log the rest.
for i, err := range errors {
if i > 0 {
t.c.rs.Logger.Errorf("js.Build failed: %s", err)
c.rs.Logger.Errorf("js.Build failed: %s", err)
}
}

return errors[0]
}

if buildOptions.Sourcemap == api.SourceMapExternal {
content := string(result.OutputFiles[1].Contents)
symPath := path.Base(ctx.OutPath) + ".map"
re := regexp.MustCompile(`//# sourceMappingURL=.*\n?`)
content = re.ReplaceAllString(content, "//# sourceMappingURL="+symPath+"\n")
if transformCtx != nil {
if buildOptions.Sourcemap == api.SourceMapExternal {
content := string(result.OutputFiles[1].Contents)
symPath := path.Base(transformCtx.OutPath) + ".map"
re := regexp.MustCompile(`//# sourceMappingURL=.*\n?`)
content = re.ReplaceAllString(content, "//# sourceMappingURL="+symPath+"\n")

if err = transformCtx.PublishSourceMap(string(result.OutputFiles[0].Contents)); err != nil {
return err
}
_, err := transformCtx.To.Write([]byte(content))
if err != nil {
return err
}
} else {
_, err := transformCtx.To.Write(result.OutputFiles[0].Contents)
if err != nil {
return err
}

if err = ctx.PublishSourceMap(string(result.OutputFiles[0].Contents)); err != nil {
return err
}
_, err := ctx.To.Write([]byte(content))
if err != nil {
return err
}
} else {
_, err := ctx.To.Write(result.OutputFiles[0].Contents)
if err != nil {
return err
}

return nil
}
return nil
}

// Process process esbuild transform
func (c *Client) Process(res resources.ResourceTransformer, opts map[string]any) (resource.Resource, error) {
return res.Transform(
&buildTransformation{c: c, optsm: opts},
)
// TODO1
return nil
}
Loading
Loading