diff --git a/converter/convert.go b/converter/convert.go index b847a30..a5f7eaf 100644 --- a/converter/convert.go +++ b/converter/convert.go @@ -5,6 +5,7 @@ import ( "context" "errors" "io" + "slices" "strings" "golang.org/x/net/html" @@ -40,7 +41,8 @@ func (conv *Converter) getError() error { return conv.err } -var errNoRenderHandlers = errors.New("no render handlers are registered. did you forget to register the commonmark plugin?") +var errNoRenderHandlers = errors.New(`no render handlers are registered. did you forget to register the "commonmark" and "base" plugins?`) +var errBasePluginMissing = errors.New(`you registered the "commonmark" plugin but the "base" plugin is also required`) // ConvertNode converts a `*html.Node` to a markdown byte slice. // @@ -65,11 +67,15 @@ func (conv *Converter) ConvertNode(doc *html.Node, opts ...convertOptionFunc) ([ // If there are no render handlers registered this is // usually a user error - since people want the Commonmark Plugin in 99% of cases. if len(conv.getRenderHandlers()) == 0 { - // TODO: Add Name() to the interface & check for the presence of *both* the Base & Commonmark Plugin - // TODO: What if just the base plugin is registered? return nil, errNoRenderHandlers } + containsCommonmark := slices.Contains(conv.registeredPlugins, "commonmark") + containsBase := slices.Contains(conv.registeredPlugins, "base") + if containsCommonmark && !containsBase { + return nil, errBasePluginMissing + } + // - - - - - - - - - - - - - - - - - - - // state := newGlobalState() diff --git a/converter/convert_test.go b/converter/convert_test.go index 4e4499a..d930822 100644 --- a/converter/convert_test.go +++ b/converter/convert_test.go @@ -6,6 +6,7 @@ import ( "github.com/JohannesKaufmann/dom" "github.com/JohannesKaufmann/html-to-markdown/v2/converter" "github.com/JohannesKaufmann/html-to-markdown/v2/plugin/base" + "github.com/JohannesKaufmann/html-to-markdown/v2/plugin/commonmark" "golang.org/x/net/html" ) @@ -61,7 +62,7 @@ func TestConvertString_ErrNoRenderHandlers(t *testing.T) { if err == nil { t.Fatal("expected an error") } - if err.Error() != "no render handlers are registered. did you forget to register the commonmark plugin?" { + if err.Error() != `no render handlers are registered. did you forget to register the "commonmark" and "base" plugins?` { t.Fatal("expected a different error but got", err) } @@ -78,6 +79,22 @@ func TestConvertString_ErrNoRenderHandlers(t *testing.T) { } } +func TestConvertString_ErrBasePluginMissing(t *testing.T) { + conv := converter.NewConverter( + converter.WithPlugins( + commonmark.NewCommonmarkPlugin(), + ), + ) + + _, err := conv.ConvertString("bold text") + if err == nil { + t.Fatal("expected an error") + } + if err.Error() != `you registered the "commonmark" plugin but the "base" plugin is also required` { + t.Fatal("expected a different error but got", err) + } +} + func TestWithEscapeMode(t *testing.T) { mockRenderer := func(ctx converter.Context, w converter.Writer, n *html.Node) converter.RenderStatus { return converter.RenderTryNext diff --git a/converter/converter.go b/converter/converter.go index 9091442..9cacf9e 100644 --- a/converter/converter.go +++ b/converter/converter.go @@ -7,6 +7,8 @@ type Converter struct { err error + registeredPlugins []string + preRenderHandlers prioritizedSlice[HandlePreRenderFunc] renderHandlers prioritizedSlice[HandleRenderFunc] postRenderHandlers prioritizedSlice[HandlePostRenderFunc] diff --git a/converter/plugin.go b/converter/plugin.go index 8e91f57..e88b92d 100644 --- a/converter/plugin.go +++ b/converter/plugin.go @@ -1,8 +1,13 @@ package converter +import "errors" + // Plugin can be used to extends functionality beyond what // is offered by commonmark. type Plugin interface { + // The public name of the plugin, e.g. "strikethrough" + Name() string + // Init is called to initialize the plugin. It can be used to // *validate* the arguments and *register* the rules. Init(conv *Converter) error @@ -12,6 +17,12 @@ type Plugin interface { func WithPlugins(plugins ...Plugin) converterOption { return func(c *Converter) error { for _, plugin := range plugins { + pluginName := plugin.Name() + if pluginName == "" { + return errors.New("the plugin has no name") + } + c.registeredPlugins = append(c.registeredPlugins, pluginName) + err := plugin.Init(c) if err != nil { return err diff --git a/plugin/base/base.go b/plugin/base/base.go index 1d66b6a..fdd1293 100644 --- a/plugin/base/base.go +++ b/plugin/base/base.go @@ -24,6 +24,9 @@ func NewBasePlugin() converter.Plugin { return &base } +func (s *base) Name() string { + return "base" +} func (b *base) Init(conv *converter.Converter) error { conv.Register.TagType("#comment", converter.TagTypeRemove, converter.PriorityStandard) conv.Register.TagType("head", converter.TagTypeRemove, converter.PriorityStandard) diff --git a/plugin/commonmark/commonmark.go b/plugin/commonmark/commonmark.go index c232a8e..57a301f 100644 --- a/plugin/commonmark/commonmark.go +++ b/plugin/commonmark/commonmark.go @@ -102,6 +102,9 @@ func NewCommonmarkPlugin(opts ...OptionFunc) converter.Plugin { return &cm } +func (s *commonmark) Name() string { + return "commonmark" +} func (cm *commonmark) Init(conv *converter.Converter) error { if err := validateConfig(&cm.config); err != nil { return err diff --git a/plugin/strikethrough/strikethrough.go b/plugin/strikethrough/strikethrough.go index bf0a8a1..961212f 100644 --- a/plugin/strikethrough/strikethrough.go +++ b/plugin/strikethrough/strikethrough.go @@ -38,6 +38,9 @@ func NewStrikethroughPlugin(opts ...option) converter.Plugin { return plugin } +func (s *strikethroughPlugin) Name() string { + return "strikethrough" +} func (s *strikethroughPlugin) Init(conv *converter.Converter) error { conv.Register.PreRenderer(s.handlePreRender, converter.PriorityStandard)