diff --git a/go.mod b/go.mod index 0f9e704..9f5ae20 100644 --- a/go.mod +++ b/go.mod @@ -5,24 +5,17 @@ go 1.21.3 require ( github.com/adrg/frontmatter v0.2.0 github.com/caarlos0/env/v10 v10.0.0 + github.com/google/go-cmp v0.6.0 github.com/valyala/quicktemplate v1.7.0 -) - -require github.com/google/go-cmp v0.6.0 - -require github.com/yuin/goldmark v1.6.0 - -require github.com/yuin/goldmark-emoji v1.0.2 - -require golang.org/x/image v0.14.0 - -require ( - github.com/BurntSushi/toml v1.3.2 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect + github.com/yuin/goldmark v1.7.0 + github.com/yuin/goldmark-emoji v1.0.2 + golang.org/x/image v0.15.0 + golang.org/x/text v0.14.0 gopkg.in/yaml.v3 v3.0.1 ) require ( + github.com/BurntSushi/toml v1.3.2 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect - golang.org/x/text v0.14.0 + gopkg.in/yaml.v2 v2.4.0 // indirect ) diff --git a/go.sum b/go.sum index b9ac771..45c6743 100644 --- a/go.sum +++ b/go.sum @@ -19,13 +19,13 @@ github.com/valyala/quicktemplate v1.7.0 h1:LUPTJmlVcb46OOUY3IeD9DojFpAVbsG+5WFTc github.com/valyala/quicktemplate v1.7.0/go.mod h1:sqKJnoaOF88V07vkO+9FL8fb9uZg/VPSJnLYn+LmLk8= github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= github.com/yuin/goldmark v1.3.7/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.6.0 h1:boZcn2GTjpsynOsC0iJHnBWa4Bi0qzfJjthwauItG68= -github.com/yuin/goldmark v1.6.0/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/yuin/goldmark v1.7.0 h1:EfOIvIMZIzHdB/R/zVrikYLPPwJlfMcNczJFMs1m6sA= +github.com/yuin/goldmark v1.7.0/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= github.com/yuin/goldmark-emoji v1.0.2 h1:c/RgTShNgHTtc6xdz2KKI74jJr6rWi7FPgnP9GAsO5s= github.com/yuin/goldmark-emoji v1.0.2/go.mod h1:RhP/RWpexdp+KHs7ghKnifRoIs/Bq4nDS7tRbCkOwKY= golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= -golang.org/x/image v0.14.0 h1:tNgSxAFe3jC4uYqvZdTr84SZoM1KfwdC9SKIFrLjFn4= -golang.org/x/image v0.14.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE= +golang.org/x/image v0.15.0 h1:kOELfmgrmJlw4Cdb7g/QGuB3CvDrXbqEIww/pNtNBm8= +golang.org/x/image v0.15.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/internal/cmd/home/home.go b/internal/cmd/home/home.go index 423bca8..a4bec81 100644 --- a/internal/cmd/home/home.go +++ b/internal/cmd/home/home.go @@ -18,10 +18,10 @@ import ( "source.toby3d.me/toby3d/home/internal/common" "source.toby3d.me/toby3d/home/internal/domain" + "source.toby3d.me/toby3d/home/internal/entry" + pagefsrepo "source.toby3d.me/toby3d/home/internal/entry/repository/fs" + pageucase "source.toby3d.me/toby3d/home/internal/entry/usecase" "source.toby3d.me/toby3d/home/internal/middleware" - "source.toby3d.me/toby3d/home/internal/page" - pagefsrepo "source.toby3d.me/toby3d/home/internal/page/repository/fs" - pageucase "source.toby3d.me/toby3d/home/internal/page/usecase" resourcefsrepo "source.toby3d.me/toby3d/home/internal/resource/repository/fs" resourceucase "source.toby3d.me/toby3d/home/internal/resource/usecase" servercase "source.toby3d.me/toby3d/home/internal/server/usecase" @@ -40,11 +40,11 @@ type App struct { func NewApp(logger *log.Logger, config *domain.Config) (*App, error) { themeDir := os.DirFS(config.ThemeDir) - partialsDir, err := fs.Sub(themeDir, "partials") if err != nil { return nil, fmt.Errorf("cannot substitute into partials subdirectory: %w", err) } + contentDir := os.DirFS(config.ContentDir) resources := resourcefsrepo.NewFileServerResourceRepository(contentDir) sites := sitefsrepo.NewFileSystemSiteRepository(contentDir) @@ -55,8 +55,8 @@ func NewApp(logger *log.Logger, config *domain.Config) (*App, error) { resourcer := resourceucase.NewResourceUseCase(resources) themes := themefsrepo.NewFileSystemThemeRepository(themeDir) themer := themeucase.NewThemeUseCase(partialsDir, themes) - pages := pagefsrepo.NewFileSystemPageRepository(contentDir) - pager := pageucase.NewPageUseCase(pages, resources) + entries := pagefsrepo.NewFileSystemPageRepository(contentDir) + entrier := pageucase.NewEntryUseCase(entries, resources) serverer := servercase.NewServerUseCase(sites) handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // INFO(toby3d): any static file is public and unprotected by design, so it's safe to search it @@ -115,9 +115,9 @@ func NewApp(logger *log.Logger, config *domain.Config) (*App, error) { return } - p, err := pager.Do(r.Context(), lang, r.URL.Path) + e, err := entrier.Do(r.Context(), lang, r.URL.Path) if err != nil { - if !errors.Is(err, page.ErrNotExist) { + if !errors.Is(err, entry.ErrNotExist) { http.Error(w, err.Error(), http.StatusInternalServerError) return @@ -158,14 +158,14 @@ func NewApp(logger *log.Logger, config *domain.Config) (*App, error) { return } - contentLanguage := make([]string, len(p.Translations)) - for i := range p.Translations { - contentLanguage[i] = p.Translations[i].Language.Code() + contentLanguage := make([]string, len(e.Translations)) + for i := range e.Translations { + contentLanguage[i] = e.Translations[i].Language.Code() } w.Header().Set(common.HeaderContentLanguage, strings.Join(contentLanguage, ", ")) - template, err := themer.Do(r.Context(), s, p) + template, err := themer.Do(r.Context(), s, e) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) diff --git a/internal/common/common.go b/internal/common/common.go index a2a8d5c..8e26421 100644 --- a/internal/common/common.go +++ b/internal/common/common.go @@ -7,8 +7,10 @@ const ( ) const ( - MIMETextHTML string = "text/html" - MIMETextHTMLCharsetUTF8 string = MIMETextHTML + "; " + charsetUTF8 + MIMETextHTML string = "text/html" + MIMETextHTMLCharsetUTF8 string = MIMETextHTML + "; " + charsetUTF8 + MIMETextPlain string = "text/plain" + MIMETextPlainCharsetUTF8 string = MIMETextPlain + "; " + charsetUTF8 ) const Und string = "und" diff --git a/internal/domain/path.go b/internal/domain/path.go index 651bacd..b5af346 100644 --- a/internal/domain/path.go +++ b/internal/domain/path.go @@ -41,11 +41,15 @@ func NewPath(path string) Path { parts := strings.Split(out.baseFileName, ".") out.Language = NewLanguage(parts[len(parts)-1]) out.translationBaseName = strings.Join(parts[:len(parts)-1], ".") - out.contentBaseName = out.translationBaseName + + if len(parts) == 1 { + out.translationBaseName = parts[0] + out.contentBaseName = filepath.Base(out.dir) + } else { + out.contentBaseName = out.translationBaseName + } switch out.translationBaseName { - default: - out.contentBaseName = out.translationBaseName case "_index", "index": out.contentBaseName = filepath.Base(out.dir) } @@ -62,6 +66,7 @@ func NewPath(path string) Path { // /news/a.en.md => a.en // /news/b/index.en.md => index.en // /news/_index.en.md => _index.en +// /news/b/photo.jpg => photo func (p Path) BaseFileName() string { return p.baseFileName } @@ -71,6 +76,7 @@ func (p Path) BaseFileName() string { // /news/a.en.md => a // /news/b/index.en.md => b // /news/_index.en.md => news +// /news/b/photo.jpg => b func (p Path) ContentBaseName() string { return p.contentBaseName } @@ -80,6 +86,7 @@ func (p Path) ContentBaseName() string { // /news/a.en.md => news/ // /news/b/index.en.md => news/b/ // /news/_index.en.md => news/ +// /news/b/photo.jpg => news/b/ func (p Path) Dir() string { return p.dir } @@ -87,6 +94,7 @@ func (p Path) Dir() string { // Ext returns file extention: // // /news/b/index.en.md => md +// /news/b/photo.jpg => jpg func (p Path) Ext() string { return p.ext } @@ -95,11 +103,12 @@ func (p Path) Filename() string { return p.filename } -// LogicalName returns fille file name in directory: +// LogicalName returns file name in directory: // // /news/a.en.md => a.en.md // /news/b/index.en.md => index.en.md // /news/_index.en.md => _index.en.md +// /news/b/photo.jpg => photo.jpg func (p Path) LogicalName() string { return p.logicalName } @@ -113,6 +122,7 @@ func (p Path) Path() string { // /news/a.en.md => a // /news/b/index.en.md => index // /news/_index.en.md => _index +// /news/b/photo.jpg => photo func (p Path) TranslationBaseName() string { return p.translationBaseName } diff --git a/internal/domain/path_test.go b/internal/domain/path_test.go index 67a83dd..306094a 100644 --- a/internal/domain/path_test.go +++ b/internal/domain/path_test.go @@ -11,6 +11,7 @@ var ( testRegularPath string = filepath.Join("news", "a.en.md") testLeafPath string = filepath.Join("news", "b", "index.en.md") testBranchPath string = filepath.Join("news", "_index.en.md") + testResource string = filepath.Join("news", "b", "photo.jpg") ) func TestPath_BaseFileName(t *testing.T) { @@ -19,9 +20,10 @@ func TestPath_BaseFileName(t *testing.T) { for name, tc := range map[string]struct { input, expect string }{ - "regular": {testRegularPath, "a.en"}, - "leaf": {testLeafPath, "index.en"}, - "branch": {testBranchPath, "_index.en"}, + "regular": {testRegularPath, "a.en"}, + "leaf": {testLeafPath, "index.en"}, + "branch": {testBranchPath, "_index.en"}, + "resource": {testResource, "photo"}, } { name, tc := name, tc @@ -41,9 +43,10 @@ func TestPath_ContentBaseName(t *testing.T) { for name, tc := range map[string]struct { input, expect string }{ - "regular": {testRegularPath, "a"}, - "leaf": {testLeafPath, "b"}, - "branch": {testBranchPath, "news"}, + "regular": {testRegularPath, "a"}, + "leaf": {testLeafPath, "b"}, + "branch": {testBranchPath, "news"}, + "resource": {testResource, "b"}, } { name, tc := name, tc @@ -63,9 +66,10 @@ func TestPath_Dir(t *testing.T) { for name, tc := range map[string]struct { input, expect string }{ - "regular": {testRegularPath, "news/"}, - "leaf": {testLeafPath, "news/b/"}, - "branch": {testBranchPath, "news/"}, + "regular": {testRegularPath, "news/"}, + "leaf": {testLeafPath, "news/b/"}, + "branch": {testBranchPath, "news/"}, + "resource": {testResource, "news/b/"}, } { name, tc := name, tc @@ -84,18 +88,21 @@ func TestPath_Ext(t *testing.T) { const expect string = "md" - for name, input := range map[string]string{ - "regular": testRegularPath, - "leaf": testLeafPath, - "branch": testBranchPath, + for name, tc := range map[string]struct { + input, expect string + }{ + "regular": {testRegularPath, "md"}, + "leaf": {testLeafPath, "md"}, + "branch": {testBranchPath, "md"}, + "resource": {testResource, "jpg"}, } { - name, input := name, input + name, tc := name, tc t.Run(name, func(t *testing.T) { t.Parallel() - if actual := domain.NewPath(input).Ext(); actual != expect { - t.Errorf("Ext() = '%s', want '%s'", actual, expect) + if actual := domain.NewPath(tc.input).Ext(); actual != tc.expect { + t.Errorf("Ext() = '%s', want '%s'", actual, tc.expect) } }) } @@ -129,9 +136,10 @@ func TestPath_LogicalName(t *testing.T) { for name, tc := range map[string]struct { input, expect string }{ - "regular": {testRegularPath, "a.en.md"}, - "leaf": {testLeafPath, "index.en.md"}, - "branch": {testBranchPath, "_index.en.md"}, + "regular": {testRegularPath, "a.en.md"}, + "leaf": {testLeafPath, "index.en.md"}, + "branch": {testBranchPath, "_index.en.md"}, + "resource": {testResource, "photo.jpg"}, } { name, tc := name, tc @@ -151,9 +159,10 @@ func TestPath_TranslationBaseName(t *testing.T) { for name, tc := range map[string]struct { input, expect string }{ - "regular": {testRegularPath, "a"}, - "leaf": {testLeafPath, "index"}, - "branch": {testBranchPath, "_index"}, + "regular": {testRegularPath, "a"}, + "leaf": {testLeafPath, "index"}, + "branch": {testBranchPath, "_index"}, + "resource": {testResource, "photo"}, } { name, tc := name, tc diff --git a/internal/domain/resource.go b/internal/domain/resource.go index ff96a60..a855fbf 100644 --- a/internal/domain/resource.go +++ b/internal/domain/resource.go @@ -15,15 +15,14 @@ import ( ) type Resource struct { - File Path - modTime time.Time - params map[string]any // TODO(toby3d): set from Page configuration + params map[string]any + File Path mediaType MediaType key string - name string // TODO(toby3d): set from Page configuration - title string // TODO(toby3d): set from Page configuration + name string resourceType ResourceType + title string image image.Config } @@ -69,13 +68,13 @@ func (r Resource) MediaType() MediaType { } // Width returns width if current r is an image. -func (r Resource) Width() int { - return r.image.Width +func (r Resource) Width() uint { + return uint(r.image.Width) } // Height returns height if current r is an image. -func (r Resource) Height() int { - return r.image.Height +func (r Resource) Height() uint { + return uint(r.image.Height) } func (r Resource) ResourceType() ResourceType { diff --git a/internal/entry/repository.go b/internal/entry/repository.go new file mode 100644 index 0000000..6a3edd5 --- /dev/null +++ b/internal/entry/repository.go @@ -0,0 +1,18 @@ +package entry + +import ( + "context" + "errors" + + "source.toby3d.me/toby3d/home/internal/domain" +) + +type Repository interface { + Get(ctx context.Context, lang domain.Language, path string) (*domain.Page, error) + + // Stat checks for the existence of a page on the specified path without + // parsing its contents. + Stat(ctx context.Context, lang domain.Language, path string) (bool, error) +} + +var ErrNotExist error = errors.New("entry not exists") diff --git a/internal/page/repository/fs/fs_page.go b/internal/entry/repository/fs/fs_page.go similarity index 70% rename from internal/page/repository/fs/fs_page.go rename to internal/entry/repository/fs/fs_page.go index 90d8aa9..847f19a 100644 --- a/internal/page/repository/fs/fs_page.go +++ b/internal/entry/repository/fs/fs_page.go @@ -2,6 +2,7 @@ package fs import ( "context" + "errors" "fmt" "io/fs" @@ -9,7 +10,7 @@ import ( "gopkg.in/yaml.v3" "source.toby3d.me/toby3d/home/internal/domain" - "source.toby3d.me/toby3d/home/internal/page" + "source.toby3d.me/toby3d/home/internal/entry" ) type ( @@ -29,7 +30,7 @@ var FrontMatterFormats = []*frontmatter.Format{ frontmatter.NewFormat(`---`, `---`, yaml.Unmarshal), } -func NewFileSystemPageRepository(dir fs.FS) page.Repository { +func NewFileSystemPageRepository(dir fs.FS) entry.Repository { return &fileSystemPageRepository{ dir: dir, } @@ -45,7 +46,7 @@ func (repo *fileSystemPageRepository) Get(ctx context.Context, lang domain.Langu f, err := repo.dir.Open(target) if err != nil { - return nil, fmt.Errorf("cannot open '%s' page file: %w", target, err) + return nil, fmt.Errorf("cannot open '%s' entry file: %w", target, err) } defer f.Close() @@ -53,7 +54,7 @@ func (repo *fileSystemPageRepository) Get(ctx context.Context, lang domain.Langu Params: make(map[string]any), } if data.Content, err = frontmatter.Parse(f, data, FrontMatterFormats...); err != nil { - return nil, fmt.Errorf("cannot parse page content as FrontMatter: %w", err) + return nil, fmt.Errorf("cannot parse entry content as FrontMatter: %w", err) } return &domain.Page{ @@ -67,3 +68,14 @@ func (repo *fileSystemPageRepository) Get(ctx context.Context, lang domain.Langu Translations: make([]*domain.Page, 0), }, nil } + +func (repo *fileSystemPageRepository) Stat(_ context.Context, l domain.Language, p string) (bool, error) { + ext := ".md" + if l != domain.LanguageUnd { + ext = "." + l.Lang() + ext + } + + _, err := fs.Stat(repo.dir, p+ext) + + return errors.Is(err, fs.ErrExist), nil +} diff --git a/internal/page/repository/fs/fs_page_test.go b/internal/entry/repository/fs/fs_page_test.go similarity index 97% rename from internal/page/repository/fs/fs_page_test.go rename to internal/entry/repository/fs/fs_page_test.go index cfc1a2e..72f4e7f 100644 --- a/internal/page/repository/fs/fs_page_test.go +++ b/internal/entry/repository/fs/fs_page_test.go @@ -10,7 +10,7 @@ import ( "github.com/google/go-cmp/cmp" "source.toby3d.me/toby3d/home/internal/domain" - repository "source.toby3d.me/toby3d/home/internal/page/repository/fs" + repository "source.toby3d.me/toby3d/home/internal/entry/repository/fs" ) func TestGet(t *testing.T) { diff --git a/internal/entry/repository/memory/memory_page.go b/internal/entry/repository/memory/memory_page.go new file mode 100644 index 0000000..2cd2851 --- /dev/null +++ b/internal/entry/repository/memory/memory_page.go @@ -0,0 +1,41 @@ +package memory + +import ( + "context" + "errors" + "fmt" + "io/fs" + "sync" + "testing/fstest" + + "source.toby3d.me/toby3d/home/internal/domain" + "source.toby3d.me/toby3d/home/internal/entry" +) + +type memoryEntryRepository struct { + mutex *sync.RWMutex + files fstest.MapFS +} + +func (repo *memoryEntryRepository) Get(_ context.Context, _ domain.Language, p string) (*domain.Page, error) { + f, err := repo.files.Open(p) + if err != nil { + return nil, fmt.Errorf("cannot get entry from memory: %w", err) + } + defer f.Close() + + return nil, nil +} + +func (repo *memoryEntryRepository) Stat(_ context.Context, l domain.Language, p string) (bool, error) { + _, err := fs.Stat(repo.files, p) + + return errors.Is(err, fs.ErrExist), nil +} + +func NewMemoryEntryRepository(files fstest.MapFS) entry.Repository { + return &memoryEntryRepository{ + mutex: new(sync.RWMutex), + files: files, + } +} diff --git a/internal/page/usecase.go b/internal/entry/usecase.go similarity index 70% rename from internal/page/usecase.go rename to internal/entry/usecase.go index 7c46270..b37631f 100644 --- a/internal/page/usecase.go +++ b/internal/entry/usecase.go @@ -1,8 +1,7 @@ -package page +package entry import ( "context" - "errors" "source.toby3d.me/toby3d/home/internal/domain" ) @@ -10,5 +9,3 @@ import ( type UseCase interface { Do(ctx context.Context, lang domain.Language, path string) (*domain.Page, error) } - -var ErrNotExist error = errors.New("page not exists") diff --git a/internal/entry/usecase/page_ucase.go b/internal/entry/usecase/page_ucase.go new file mode 100644 index 0000000..19ce2fc --- /dev/null +++ b/internal/entry/usecase/page_ucase.go @@ -0,0 +1,80 @@ +package usecase + +import ( + "context" + "fmt" + "path" + "strings" + + "source.toby3d.me/toby3d/home/internal/domain" + "source.toby3d.me/toby3d/home/internal/entry" + "source.toby3d.me/toby3d/home/internal/resource" + "source.toby3d.me/toby3d/home/internal/urlutil" +) + +type entryUseCase struct { + entries entry.Repository + resources resource.Repository +} + +func NewEntryUseCase(entries entry.Repository, resources resource.Repository) entry.UseCase { + return &entryUseCase{ + entries: entries, + resources: resources, + } +} + +func (ucase *entryUseCase) Do(ctx context.Context, lang domain.Language, p string) (*domain.Page, error) { + targets := make([]string, 0) + hasExt := path.Ext(p) != "" + head, tail := urlutil.ShiftPath(p) + + if tail == "/" { + if head = strings.TrimSuffix(head, path.Ext(head)); head == "" { + head = "index" + } + + targets = append(targets, head) + } + + if head != "index" { + tail = strings.TrimSuffix(tail, path.Ext(tail)) + if !strings.HasSuffix(tail, "/index") { + if hasExt { + targets = append([]string{path.Join(head, tail, "index")}, targets...) + } else { + targets = append(targets, path.Join(head, tail, "index")) + } + } else { + targets = append(targets, path.Join(head, tail)) + } + } + + for i := len(targets) - 1; 0 <= i; i-- { + result, err := ucase.entries.Get(ctx, lang, targets[i]) + if err != nil { + continue + } + + if result.Resources, _, err = ucase.resources.Fetch(ctx, result.File.Dir()+"*"); err != nil { + return result, nil + } + + for _, res := range result.Resources.GetType(domain.ResourceTypePage) { + if res.File.TranslationBaseName() != result.File.TranslationBaseName() { + continue + } + + translation, err := ucase.entries.Get(ctx, res.File.Language, targets[i]) + if err != nil { + continue + } + + result.Translations = append(result.Translations, translation) + } + + return result, nil + } + + return nil, fmt.Errorf("cannot find page on path '%s': %w", p, entry.ErrNotExist) +} diff --git a/internal/entry/usecase/page_ucase_test.go b/internal/entry/usecase/page_ucase_test.go new file mode 100644 index 0000000..aae1884 --- /dev/null +++ b/internal/entry/usecase/page_ucase_test.go @@ -0,0 +1,70 @@ +package usecase_test + +import ( + "context" + "path/filepath" + "testing" + "testing/fstest" + + "github.com/google/go-cmp/cmp" + + "source.toby3d.me/toby3d/home/internal/domain" + entryfsrepo "source.toby3d.me/toby3d/home/internal/entry/repository/fs" + "source.toby3d.me/toby3d/home/internal/entry/usecase" + resourcedummyrepo "source.toby3d.me/toby3d/home/internal/resource/repository/dummy" +) + +func TestDo(t *testing.T) { + t.Parallel() + + pages := entryfsrepo.NewFileSystemPageRepository(fstest.MapFS{ + filepath.Join("both", "index.md"): &fstest.MapFile{Data: []byte(`both/index.md`)}, + filepath.Join("both.md"): &fstest.MapFile{Data: []byte(`both.md`)}, + filepath.Join("file.md"): &fstest.MapFile{Data: []byte(`file.md`)}, + filepath.Join("folder", "index.md"): &fstest.MapFile{Data: []byte(`folder/index.md`)}, + filepath.Join("foo", "bar", "index.md"): &fstest.MapFile{Data: []byte(`foo/bar/index.md`)}, + filepath.Join("foo", "bar.md"): &fstest.MapFile{Data: []byte(`foo/bar.md`)}, + filepath.Join("index.md"): &fstest.MapFile{Data: []byte(`index.md`)}, + }) + + ucase := usecase.NewEntryUseCase(pages, resourcedummyrepo.NewDummyResourceRepository()) + + for name, tc := range map[string]struct { + input string + expect []byte + }{ + "root": {"/", []byte(`index.md`)}, + "index": {"/index", []byte(`index.md`)}, + "index-ext": {"/index.html", []byte(`index.md`)}, + "file": {"/file", []byte(`file.md`)}, + "file-slash": {"/file/", []byte(`file.md`)}, + "file-ext": {"/file.html", []byte(`file.md`)}, + "both-ext": {"/both.html", []byte(`both.md`)}, + "folder": {"/folder", []byte(`folder/index.md`)}, + "folder-slash": {"/folder/", []byte(`folder/index.md`)}, + "folder-index": {"/folder/index", []byte(`folder/index.md`)}, + "folder-ext": {"/folder/index.html", []byte(`folder/index.md`)}, + "both": {"/both", []byte(`both/index.md`)}, + "both-slash": {"/both/", []byte(`both/index.md`)}, + "both-index": {"/both/index", []byte(`both/index.md`)}, + "both-index-ext": {"/both/index.html", []byte(`both/index.md`)}, + "sub-folder-index": {"/foo/bar/index", []byte(`foo/bar/index.md`)}, + "sub-folder-ext": {"/foo/bar/index.html", []byte(`foo/bar/index.md`)}, + "sub-folder-slash": {"/foo/bar/", []byte(`foo/bar/index.md`)}, + } { + name, tc := name, tc + + t.Run(name, func(t *testing.T) { + t.Parallel() + + actual, err := ucase.Do(context.Background(), domain.LanguageUnd, tc.input) + if err != nil { + t.Fatal(err) + } + + if diff := cmp.Diff(string(actual.Content), string(tc.expect)); diff != "" { + t.Error(diff) + } + }) + } +} diff --git a/internal/page/repository.go b/internal/page/repository.go deleted file mode 100644 index 9c76990..0000000 --- a/internal/page/repository.go +++ /dev/null @@ -1,11 +0,0 @@ -package page - -import ( - "context" - - "source.toby3d.me/toby3d/home/internal/domain" -) - -type Repository interface { - Get(ctx context.Context, lang domain.Language, path string) (*domain.Page, error) -} diff --git a/internal/page/usecase/page_ucase.go b/internal/page/usecase/page_ucase.go deleted file mode 100644 index c18a76a..0000000 --- a/internal/page/usecase/page_ucase.go +++ /dev/null @@ -1,71 +0,0 @@ -package usecase - -import ( - "context" - "fmt" - "path" - - "source.toby3d.me/toby3d/home/internal/domain" - "source.toby3d.me/toby3d/home/internal/page" - "source.toby3d.me/toby3d/home/internal/resource" - "source.toby3d.me/toby3d/home/internal/urlutil" -) - -type pageUseCase struct { - pages page.Repository - resources resource.Repository -} - -func NewPageUseCase(pages page.Repository, resources resource.Repository) page.UseCase { - return &pageUseCase{ - pages: pages, - resources: resources, - } -} - -func (ucase *pageUseCase) Do(ctx context.Context, lang domain.Language, p string) (*domain.Page, error) { - ext := path.Ext(p) - if ext == ".html" { - p = p[:len(p)-len(ext)] - } - - hasTrailingSlash := p[len(p)-1] == '/' - head, tail := urlutil.ShiftPath(p) - targets := []string{path.Join(head, tail)} - - if tail == "/" { - if hasTrailingSlash || ext == "" { - targets = append([]string{path.Join(head, "index")}, targets...) - } - - targets = append(targets, head) - } - - for i := range targets { - out, err := ucase.pages.Get(ctx, lang, targets[i]) - if err != nil { - continue - } - - if out.Resources, _, err = ucase.resources.Fetch(ctx, out.File.Dir()+"*"); err != nil { - return out, nil - } - - for _, res := range out.Resources.GetType(domain.ResourceTypePage) { - if res.File.TranslationBaseName() != out.File.TranslationBaseName() { - continue - } - - translation, err := ucase.pages.Get(ctx, res.File.Language, targets[i]) - if err != nil { - continue - } - - out.Translations = append(out.Translations, translation) - } - - return out, nil - } - - return nil, fmt.Errorf("cannot find page on path '%s': %w", p, page.ErrNotExist) -} diff --git a/internal/page/usecase/page_ucase_test.go b/internal/page/usecase/page_ucase_test.go deleted file mode 100644 index 51391d8..0000000 --- a/internal/page/usecase/page_ucase_test.go +++ /dev/null @@ -1,62 +0,0 @@ -package usecase_test - -import ( - "context" - "path/filepath" - "testing" - "testing/fstest" - - "github.com/google/go-cmp/cmp" - - "source.toby3d.me/toby3d/home/internal/domain" - pagefsrepo "source.toby3d.me/toby3d/home/internal/page/repository/fs" - "source.toby3d.me/toby3d/home/internal/page/usecase" - "source.toby3d.me/toby3d/home/internal/resource" -) - -func TestDo(t *testing.T) { - t.Parallel() - - pages := pagefsrepo.NewFileSystemPageRepository(fstest.MapFS{ - filepath.Join("both", "index.md"): &fstest.MapFile{Data: []byte(`both/index.md`)}, - filepath.Join("folder", "index.md"): &fstest.MapFile{Data: []byte(`folder/index.md`)}, - filepath.Join("both.md"): &fstest.MapFile{Data: []byte(`both.md`)}, - filepath.Join("file.md"): &fstest.MapFile{Data: []byte(`file.md`)}, - filepath.Join("index.md"): &fstest.MapFile{Data: []byte(`index.md`)}, - }) - - ucase := usecase.NewPageUseCase(pages, resource.NewDummyRepository()) - - for name, tc := range map[string]struct { - input string - expect []byte - }{ - "index": {input: "/", expect: []byte(`index.md`)}, - "index-ext": {input: "/index.html", expect: []byte(`index.md`)}, - "file": {input: "/file", expect: []byte(`file.md`)}, - "file-slash": {input: "/file/", expect: []byte(`file.md`)}, - "file-ext": {input: "/file.html", expect: []byte(`file.md`)}, - "folder": {input: "/folder", expect: []byte(`folder/index.md`)}, - "folder-slash": {input: "/folder/", expect: []byte(`folder/index.md`)}, - "folder-index": {input: "/folder/index.html", expect: []byte(`folder/index.md`)}, - "both": {input: "/both", expect: []byte(`both/index.md`)}, - "both-slash": {input: "/both/", expect: []byte(`both/index.md`)}, - "both-ext": {input: "/both.html", expect: []byte(`both.md`)}, - "both-index": {input: "/both/index.html", expect: []byte(`both/index.md`)}, - } { - name, tc := name, tc - - t.Run(name, func(t *testing.T) { - t.Parallel() - - actual, err := ucase.Do(context.Background(), domain.LanguageUnd, tc.input) - if err != nil { - t.Fatal(err) - } - - if diff := cmp.Diff(string(actual.Content), string(tc.expect)); diff != "" { - t.Error(diff) - } - }) - } -} diff --git a/internal/resource/repository.go b/internal/resource/repository.go index a1042cf..8258f13 100644 --- a/internal/resource/repository.go +++ b/internal/resource/repository.go @@ -6,26 +6,10 @@ import ( "source.toby3d.me/toby3d/home/internal/domain" ) -type ( - Repository interface { - // Get returns Resource on path if exists. - Get(ctx context.Context, path string) (*domain.Resource, error) +type Repository interface { + // Get returns Resource on path if exists. + Get(ctx context.Context, path string) (*domain.Resource, error) - // Fetch returns all resources from dir recursevly. - Fetch(ctx context.Context, pattern string) (domain.Resources, int, error) - } - - dummyRepository struct{} -) - -func NewDummyRepository() dummyRepository { - return dummyRepository{} -} - -func (dummyRepository) Get(ctx context.Context, path string) (*domain.Resource, error) { - return nil, nil -} - -func (dummyRepository) Fetch(ctx context.Context, pattern string) (domain.Resources, int, error) { - return nil, 0, nil + // Fetch returns all resources from dir recursevly. + Fetch(ctx context.Context, pattern string) (domain.Resources, int, error) } diff --git a/internal/resource/repository/dummy/dummy_resource.go b/internal/resource/repository/dummy/dummy_resource.go new file mode 100644 index 0000000..be373b3 --- /dev/null +++ b/internal/resource/repository/dummy/dummy_resource.go @@ -0,0 +1,21 @@ +package dummy + +import ( + "context" + + "source.toby3d.me/toby3d/home/internal/domain" +) + +type dummyResourceRepository struct{} + +func NewDummyResourceRepository() dummyResourceRepository { + return dummyResourceRepository{} +} + +func (dummyResourceRepository) Get(_ context.Context, _ string) (*domain.Resource, error) { + return nil, nil +} + +func (dummyResourceRepository) Fetch(_ context.Context, _ string) (domain.Resources, int, error) { + return nil, 0, nil +} diff --git a/internal/resource/repository/fs/fs_resource.go b/internal/resource/repository/fs/fs_resource.go index 9413e47..ab25d3d 100644 --- a/internal/resource/repository/fs/fs_resource.go +++ b/internal/resource/repository/fs/fs_resource.go @@ -3,14 +3,8 @@ package fs import ( "context" "fmt" - _ "image/gif" - _ "image/jpeg" - _ "image/png" "io/fs" - _ "golang.org/x/image/bmp" - _ "golang.org/x/image/webp" - "source.toby3d.me/toby3d/home/internal/domain" "source.toby3d.me/toby3d/home/internal/resource" ) diff --git a/internal/static/repository/fs/fs_static.go b/internal/static/repository/fs/fs_static.go index 83c4a6e..d4221b6 100644 --- a/internal/static/repository/fs/fs_static.go +++ b/internal/static/repository/fs/fs_static.go @@ -5,17 +5,11 @@ import ( "context" "errors" "fmt" - _ "image/gif" - _ "image/jpeg" - _ "image/png" "io" "io/fs" "os" "path/filepath" - _ "golang.org/x/image/bmp" - _ "golang.org/x/image/webp" - "source.toby3d.me/toby3d/home/internal/domain" "source.toby3d.me/toby3d/home/internal/static" ) diff --git a/vendor/github.com/yuin/goldmark/README.md b/vendor/github.com/yuin/goldmark/README.md index 8d9d83f..a836445 100644 --- a/vendor/github.com/yuin/goldmark/README.md +++ b/vendor/github.com/yuin/goldmark/README.md @@ -8,7 +8,7 @@ goldmark > A Markdown parser written in Go. Easy to extend, standards-compliant, well-structured. -goldmark is compliant with CommonMark 0.30. +goldmark is compliant with CommonMark 0.31.2. Motivation ---------------------- @@ -260,7 +260,7 @@ You can override autolinking patterns via options. | Functional option | Type | Description | | ----------------- | ---- | ----------- | -| `extension.WithLinkifyAllowedProtocols` | `[][]byte` | List of allowed protocols such as `[][]byte{ []byte("http:") }` | +| `extension.WithLinkifyAllowedProtocols` | `[][]byte \| []string` | List of allowed protocols such as `[]string{ "http:" }` | | `extension.WithLinkifyURLRegexp` | `*regexp.Regexp` | Regexp that defines URLs, including protocols | | `extension.WithLinkifyWWWRegexp` | `*regexp.Regexp` | Regexp that defines URL starting with `www.`. This pattern corresponds to [the extended www autolink](https://github.github.com/gfm/#extended-www-autolink) | | `extension.WithLinkifyEmailRegexp` | `*regexp.Regexp` | Regexp that defines email addresses` | @@ -277,9 +277,9 @@ markdown := goldmark.New( ), goldmark.WithExtensions( extension.NewLinkify( - extension.WithLinkifyAllowedProtocols([][]byte{ - []byte("http:"), - []byte("https:"), + extension.WithLinkifyAllowedProtocols([]string{ + "http:", + "https:", }), extension.WithLinkifyURLRegexp( xurls.Strict, @@ -297,13 +297,13 @@ This extension has some options: | Functional option | Type | Description | | ----------------- | ---- | ----------- | -| `extension.WithFootnoteIDPrefix` | `[]byte` | a prefix for the id attributes.| +| `extension.WithFootnoteIDPrefix` | `[]byte \| string` | a prefix for the id attributes.| | `extension.WithFootnoteIDPrefixFunction` | `func(gast.Node) []byte` | a function that determines the id attribute for given Node.| -| `extension.WithFootnoteLinkTitle` | `[]byte` | an optional title attribute for footnote links.| -| `extension.WithFootnoteBacklinkTitle` | `[]byte` | an optional title attribute for footnote backlinks. | -| `extension.WithFootnoteLinkClass` | `[]byte` | a class for footnote links. This defaults to `footnote-ref`. | -| `extension.WithFootnoteBacklinkClass` | `[]byte` | a class for footnote backlinks. This defaults to `footnote-backref`. | -| `extension.WithFootnoteBacklinkHTML` | `[]byte` | a class for footnote backlinks. This defaults to `↩︎`. | +| `extension.WithFootnoteLinkTitle` | `[]byte \| string` | an optional title attribute for footnote links.| +| `extension.WithFootnoteBacklinkTitle` | `[]byte \| string` | an optional title attribute for footnote backlinks. | +| `extension.WithFootnoteLinkClass` | `[]byte \| string` | a class for footnote links. This defaults to `footnote-ref`. | +| `extension.WithFootnoteBacklinkClass` | `[]byte \| string` | a class for footnote backlinks. This defaults to `footnote-backref`. | +| `extension.WithFootnoteBacklinkHTML` | `[]byte \| string` | a class for footnote backlinks. This defaults to `↩︎`. | Some options can have special substitutions. Occurrences of “^^” in the string will be replaced by the corresponding footnote number in the HTML output. Occurrences of “%%” will be replaced by a number for the reference (footnotes can have multiple references). @@ -319,7 +319,7 @@ for _, path := range files { markdown := goldmark.New( goldmark.WithExtensions( NewFootnote( - WithFootnoteIDPrefix([]byte(path)), + WithFootnoteIDPrefix(path), ), ), ) @@ -379,7 +379,7 @@ This extension provides additional options for CJK users. | Functional option | Type | Description | | ----------------- | ---- | ----------- | -| `extension.WithEastAsianLineBreaks` | `...extension.EastAsianLineBreaksStyle` | Soft line breaks are rendered as a newline. Some asian users will see it as an unnecessary space. With this option, soft line breaks between east asian wide characters will be ignored. | +| `extension.WithEastAsianLineBreaks` | `...extension.EastAsianLineBreaksStyle` | Soft line breaks are rendered as a newline. Some asian users will see it as an unnecessary space. With this option, soft line breaks between east asian wide characters will be ignored. This defaults to `EastAsianLineBreaksStyleSimple`. | | `extension.WithEscapedSpace` | `-` | Without spaces around an emphasis started with east asian punctuations, it is not interpreted as an emphasis(as defined in CommonMark spec). With this option, you can avoid this inconvenient behavior by putting 'not rendered' spaces around an emphasis like `太郎は\ **「こんにちわ」**\ といった`. | #### Styles of Line Breaking @@ -467,6 +467,7 @@ As you can see, goldmark's performance is on par with cmark's. Extensions -------------------- +### List of extensions - [goldmark-meta](https://github.com/yuin/goldmark-meta): A YAML metadata extension for the goldmark Markdown parser. @@ -490,6 +491,13 @@ Extensions - [goldmark-d2](https://github.com/FurqanSoftware/goldmark-d2): Adds support for [D2](https://d2lang.com/) diagrams. - [goldmark-katex](https://github.com/FurqanSoftware/goldmark-katex): Adds support for [KaTeX](https://katex.org/) math and equations. - [goldmark-img64](https://github.com/tenkoh/goldmark-img64): Adds support for embedding images into the document as DataURL (base64 encoded). +- [goldmark-enclave](https://github.com/quail-ink/goldmark-enclave): Adds support for embedding youtube/bilibili video, X's [oembed tweet](https://publish.twitter.com/), [tradingview](https://www.tradingview.com/widget/)'s chart, [quail](https://quail.ink)'s widget into the document. +- [goldmark-wiki-table](https://github.com/movsb/goldmark-wiki-table): Adds support for embedding Wiki Tables. + +### Loading extensions at runtime +[goldmark-dynamic](https://github.com/yuin/goldmark-dynamic) allows you to write a goldmark extension in Lua and load it at runtime without re-compilation. + +Please refer to [goldmark-dynamic](https://github.com/yuin/goldmark-dynamic) for details. goldmark internal(for extension developers) diff --git a/vendor/github.com/yuin/goldmark/extension/footnote.go b/vendor/github.com/yuin/goldmark/extension/footnote.go index d1b67aa..2e22526 100644 --- a/vendor/github.com/yuin/goldmark/extension/footnote.go +++ b/vendor/github.com/yuin/goldmark/extension/footnote.go @@ -382,8 +382,8 @@ func (o *withFootnoteIDPrefix) SetFootnoteOption(c *FootnoteConfig) { } // WithFootnoteIDPrefix is a functional option that is a prefix for the id attributes generated by footnotes. -func WithFootnoteIDPrefix(a []byte) FootnoteOption { - return &withFootnoteIDPrefix{a} +func WithFootnoteIDPrefix[T []byte | string](a T) FootnoteOption { + return &withFootnoteIDPrefix{[]byte(a)} } const optFootnoteIDPrefixFunction renderer.OptionName = "FootnoteIDPrefixFunction" @@ -420,8 +420,8 @@ func (o *withFootnoteLinkTitle) SetFootnoteOption(c *FootnoteConfig) { } // WithFootnoteLinkTitle is a functional option that is an optional title attribute for footnote links. -func WithFootnoteLinkTitle(a []byte) FootnoteOption { - return &withFootnoteLinkTitle{a} +func WithFootnoteLinkTitle[T []byte | string](a T) FootnoteOption { + return &withFootnoteLinkTitle{[]byte(a)} } const optFootnoteBacklinkTitle renderer.OptionName = "FootnoteBacklinkTitle" @@ -439,8 +439,8 @@ func (o *withFootnoteBacklinkTitle) SetFootnoteOption(c *FootnoteConfig) { } // WithFootnoteBacklinkTitle is a functional option that is an optional title attribute for footnote backlinks. -func WithFootnoteBacklinkTitle(a []byte) FootnoteOption { - return &withFootnoteBacklinkTitle{a} +func WithFootnoteBacklinkTitle[T []byte | string](a T) FootnoteOption { + return &withFootnoteBacklinkTitle{[]byte(a)} } const optFootnoteLinkClass renderer.OptionName = "FootnoteLinkClass" @@ -458,8 +458,8 @@ func (o *withFootnoteLinkClass) SetFootnoteOption(c *FootnoteConfig) { } // WithFootnoteLinkClass is a functional option that is a class for footnote links. -func WithFootnoteLinkClass(a []byte) FootnoteOption { - return &withFootnoteLinkClass{a} +func WithFootnoteLinkClass[T []byte | string](a T) FootnoteOption { + return &withFootnoteLinkClass{[]byte(a)} } const optFootnoteBacklinkClass renderer.OptionName = "FootnoteBacklinkClass" @@ -477,8 +477,8 @@ func (o *withFootnoteBacklinkClass) SetFootnoteOption(c *FootnoteConfig) { } // WithFootnoteBacklinkClass is a functional option that is a class for footnote backlinks. -func WithFootnoteBacklinkClass(a []byte) FootnoteOption { - return &withFootnoteBacklinkClass{a} +func WithFootnoteBacklinkClass[T []byte | string](a T) FootnoteOption { + return &withFootnoteBacklinkClass{[]byte(a)} } const optFootnoteBacklinkHTML renderer.OptionName = "FootnoteBacklinkHTML" @@ -496,8 +496,8 @@ func (o *withFootnoteBacklinkHTML) SetFootnoteOption(c *FootnoteConfig) { } // WithFootnoteBacklinkHTML is an HTML content for footnote backlinks. -func WithFootnoteBacklinkHTML(a []byte) FootnoteOption { - return &withFootnoteBacklinkHTML{a} +func WithFootnoteBacklinkHTML[T []byte | string](a T) FootnoteOption { + return &withFootnoteBacklinkHTML{[]byte(a)} } // FootnoteHTMLRenderer is a renderer.NodeRenderer implementation that diff --git a/vendor/github.com/yuin/goldmark/extension/linkify.go b/vendor/github.com/yuin/goldmark/extension/linkify.go index 0f23e90..ad88933 100644 --- a/vendor/github.com/yuin/goldmark/extension/linkify.go +++ b/vendor/github.com/yuin/goldmark/extension/linkify.go @@ -66,10 +66,12 @@ func (o *withLinkifyAllowedProtocols) SetLinkifyOption(p *LinkifyConfig) { // WithLinkifyAllowedProtocols is a functional option that specify allowed // protocols in autolinks. Each protocol must end with ':' like // 'http:' . -func WithLinkifyAllowedProtocols(value [][]byte) LinkifyOption { - return &withLinkifyAllowedProtocols{ - value: value, +func WithLinkifyAllowedProtocols[T []byte | string](value []T) LinkifyOption { + opt := &withLinkifyAllowedProtocols{} + for _, v := range value { + opt.value = append(opt.value, []byte(v)) } + return opt } type withLinkifyURLRegexp struct { diff --git a/vendor/github.com/yuin/goldmark/extension/typographer.go b/vendor/github.com/yuin/goldmark/extension/typographer.go index 259c4f7..44c15eb 100644 --- a/vendor/github.com/yuin/goldmark/extension/typographer.go +++ b/vendor/github.com/yuin/goldmark/extension/typographer.go @@ -115,10 +115,10 @@ func (o *withTypographicSubstitutions) SetTypographerOption(p *TypographerConfig // WithTypographicSubstitutions is a functional otpion that specify replacement text // for punctuations. -func WithTypographicSubstitutions(values map[TypographicPunctuation][]byte) TypographerOption { +func WithTypographicSubstitutions[T []byte | string](values map[TypographicPunctuation]T) TypographerOption { replacements := newDefaultSubstitutions() for k, v := range values { - replacements[k] = v + replacements[k] = []byte(v) } return &withTypographicSubstitutions{replacements} diff --git a/vendor/github.com/yuin/goldmark/parser/html_block.go b/vendor/github.com/yuin/goldmark/parser/html_block.go index bf0258b..09dc21f 100644 --- a/vendor/github.com/yuin/goldmark/parser/html_block.go +++ b/vendor/github.com/yuin/goldmark/parser/html_block.go @@ -61,8 +61,8 @@ var allowedBlockTags = map[string]bool{ "option": true, "p": true, "param": true, + "search": true, "section": true, - "source": true, "summary": true, "table": true, "tbody": true, diff --git a/vendor/github.com/yuin/goldmark/parser/raw_html.go b/vendor/github.com/yuin/goldmark/parser/raw_html.go index 2b3dbc2..1d582a7 100644 --- a/vendor/github.com/yuin/goldmark/parser/raw_html.go +++ b/vendor/github.com/yuin/goldmark/parser/raw_html.go @@ -58,47 +58,38 @@ var closeProcessingInstruction = []byte("?>") var openCDATA = []byte("") var closeDecl = []byte(">") -var emptyComment = []byte("") -var invalidComment1 = []byte("") -var invalidComment2 = []byte("") +var emptyComment1 = []byte("") +var emptyComment2 = []byte("") var openComment = []byte("") -var doubleHyphen = []byte("--") func (s *rawHTMLParser) parseComment(block text.Reader, pc Context) ast.Node { savedLine, savedSegment := block.Position() node := ast.NewRawHTML() line, segment := block.PeekLine() - if bytes.HasPrefix(line, emptyComment) { - node.Segments.Append(segment.WithStop(segment.Start + len(emptyComment))) - block.Advance(len(emptyComment)) + if bytes.HasPrefix(line, emptyComment1) { + node.Segments.Append(segment.WithStop(segment.Start + len(emptyComment1))) + block.Advance(len(emptyComment1)) return node } - if bytes.HasPrefix(line, invalidComment1) || bytes.HasPrefix(line, invalidComment2) { - return nil + if bytes.HasPrefix(line, emptyComment2) { + node.Segments.Append(segment.WithStop(segment.Start + len(emptyComment2))) + block.Advance(len(emptyComment2)) + return node } offset := len(openComment) line = line[offset:] for { - hindex := bytes.Index(line, doubleHyphen) - if hindex > -1 { - hindex += offset - } - index := bytes.Index(line, closeComment) + offset - if index > -1 && hindex == index { - if index == 0 || len(line) < 2 || line[index-offset-1] != '-' { - node.Segments.Append(segment.WithStop(segment.Start + index + len(closeComment))) - block.Advance(index + len(closeComment)) - return node - } - } - if hindex > 0 { - break + index := bytes.Index(line, closeComment) + if index > -1 { + node.Segments.Append(segment.WithStop(segment.Start + offset + index + len(closeComment))) + block.Advance(offset + index + len(closeComment)) + return node } + offset = 0 node.Segments.Append(segment) block.AdvanceLine() line, segment = block.PeekLine() - offset = 0 if line == nil { break } diff --git a/vendor/github.com/yuin/goldmark/util/util.go b/vendor/github.com/yuin/goldmark/util/util.go index 9bf09ad..e2c92c6 100644 --- a/vendor/github.com/yuin/goldmark/util/util.go +++ b/vendor/github.com/yuin/goldmark/util/util.go @@ -808,7 +808,7 @@ func IsPunct(c byte) bool { // IsPunctRune returns true if the given rune is a punctuation, otherwise false. func IsPunctRune(r rune) bool { - return int32(r) <= 256 && IsPunct(byte(r)) || unicode.IsPunct(r) + return unicode.IsSymbol(r) || unicode.IsPunct(r) } // IsSpace returns true if the given character is a space, otherwise false. diff --git a/vendor/golang.org/x/image/webp/decode.go b/vendor/golang.org/x/image/webp/decode.go index d6eefd5..e211c7d 100644 --- a/vendor/golang.org/x/image/webp/decode.go +++ b/vendor/golang.org/x/image/webp/decode.go @@ -39,6 +39,7 @@ func decode(r io.Reader, configOnly bool) (image.Image, image.Config, error) { alpha []byte alphaStride int wantAlpha bool + seenVP8X bool widthMinusOne uint32 heightMinusOne uint32 buf [10]byte @@ -113,6 +114,10 @@ func decode(r io.Reader, configOnly bool) (image.Image, image.Config, error) { return m, image.Config{}, err case fccVP8X: + if seenVP8X { + return nil, image.Config{}, errInvalidFormat + } + seenVP8X = true if chunkLen != 10 { return nil, image.Config{}, errInvalidFormat } diff --git a/vendor/modules.txt b/vendor/modules.txt index 61731a4..e3249a5 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -21,8 +21,8 @@ github.com/valyala/bytebufferpool # github.com/valyala/quicktemplate v1.7.0 ## explicit; go 1.11 github.com/valyala/quicktemplate -# github.com/yuin/goldmark v1.6.0 -## explicit; go 1.18 +# github.com/yuin/goldmark v1.7.0 +## explicit; go 1.19 github.com/yuin/goldmark github.com/yuin/goldmark/ast github.com/yuin/goldmark/extension @@ -37,7 +37,7 @@ github.com/yuin/goldmark/util github.com/yuin/goldmark-emoji github.com/yuin/goldmark-emoji/ast github.com/yuin/goldmark-emoji/definition -# golang.org/x/image v0.14.0 +# golang.org/x/image v0.15.0 ## explicit; go 1.18 golang.org/x/image/bmp golang.org/x/image/riff