├── .gitignore ├── .github └── dependabot.yml ├── go.mod ├── data.go ├── LICENSE ├── README.md ├── editorjs.go ├── editorjs_test.go ├── default.go ├── examples └── data.json └── go.sum /.gitignore: -------------------------------------------------------------------------------- 1 | vendor 2 | .realize.yaml -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: gomod 9 | directory: "/" 10 | target-branch: "main" 11 | schedule: 12 | interval: daily 13 | 14 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/si3nloong/go-editorjs 2 | 3 | go 1.17 4 | 5 | require ( 6 | github.com/PuerkitoBio/goquery v1.8.1 7 | github.com/stretchr/testify v1.8.4 8 | github.com/tidwall/gjson v1.17.0 9 | ) 10 | 11 | require ( 12 | github.com/andybalholm/cascadia v1.3.1 // indirect 13 | github.com/davecgh/go-spew v1.1.1 // indirect 14 | github.com/pmezard/go-difflib v1.0.0 // indirect 15 | github.com/tidwall/match v1.1.1 // indirect 16 | github.com/tidwall/pretty v1.2.0 // indirect 17 | golang.org/x/net v0.7.0 // indirect 18 | gopkg.in/yaml.v3 v3.0.1 // indirect 19 | ) 20 | -------------------------------------------------------------------------------- /data.go: -------------------------------------------------------------------------------- 1 | package editorjs 2 | 3 | import "encoding/json" 4 | 5 | // Data 6 | type Data struct { 7 | Blocks []Block `json:"blocks"` 8 | Version string `json:"version"` 9 | } 10 | 11 | type Block struct { 12 | Type string `json:"type"` 13 | Data json.RawMessage `json:"data"` 14 | } 15 | 16 | type List struct { 17 | Style string `json:"style"` 18 | Items []string `json:"items"` 19 | } 20 | 21 | type Image struct { 22 | File struct { 23 | Url string `json:"url"` 24 | } `json:"file"` 25 | Caption string `json:"caption"` 26 | Stretched bool `json:"stretched"` 27 | } 28 | 29 | type Table struct { 30 | WithHeadings bool `json:"withHeadings"` 31 | Content [][]string `json:"content"` 32 | } 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 SianLoong. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Editor.js (Go) 2 | 3 | Server-side implementation sample for the [Editor.js](https://github.com/codex-team/editor.js). It contains data validation and converts output from Editor.js to HTML. 4 | 5 | ## Installation 6 | 7 | ```bash 8 | go get github.com/si3nloong/go-editorjs 9 | ``` 10 | 11 | ## Basic usage 12 | 13 | ```go 14 | import ( 15 | editorjs "github.com/si3nloong/go-editorjs" 16 | ) 17 | 18 | func main() { 19 | b := []byte(` 20 | { 21 | "time": 1589098011153, 22 | "blocks": [ 23 | { 24 | "type": "header", 25 | "data": { 26 | "text": "Editor.js", 27 | "level": 2 28 | } 29 | } 30 | ], 31 | "version": "2.17.0" 32 | } 33 | `) 34 | 35 | ejs := editorjs.NewEditorJS() 36 | buf := new(bytes.Buffer) 37 | if err := ejs.ParseTo(b, buf); err != nil { 38 | panic(err) 39 | } 40 | 41 | log.Println("HTML result =>", buf.String()) 42 | } 43 | ``` 44 | 45 | ## Example of Editor.js output 46 | 47 | ```json 48 | { 49 | "time": 1589098011153, 50 | "blocks": [ 51 | { 52 | "type": "header", 53 | "data": { 54 | "text": "Editor.js", 55 | "level": 2 56 | } 57 | } 58 | ], 59 | "version": "2.17.0" 60 | } 61 | ``` 62 | -------------------------------------------------------------------------------- /editorjs.go: -------------------------------------------------------------------------------- 1 | package editorjs 2 | 3 | import ( 4 | "encoding/json" 5 | "io" 6 | "strings" 7 | "sync" 8 | 9 | "bytes" 10 | ) 11 | 12 | // Writer : 13 | type Writer interface { 14 | io.StringWriter 15 | io.Writer 16 | } 17 | 18 | // Flusher : 19 | type Flusher interface { 20 | Flush() error 21 | } 22 | 23 | // ParseFunc : 24 | type ParseFunc func(b []byte, w Writer) error 25 | 26 | // EditorJS : 27 | type EditorJS struct { 28 | // mutex lock, to prevent race condition 29 | mu sync.Mutex 30 | parsers map[string]ParseFunc 31 | } 32 | 33 | // NewEditorJS : create new Editorjs object 34 | func NewEditorJS() *EditorJS { 35 | ejs := new(EditorJS) 36 | ejs.parsers = make(map[string]ParseFunc) 37 | // register default parser 38 | registerDefaultParsers(ejs, DefaultParser{}) 39 | return ejs 40 | } 41 | 42 | // RegisterParser : 43 | func (ejs *EditorJS) RegisterParser(name string, p ParseFunc) { 44 | ejs.mu.Lock() 45 | defer ejs.mu.Unlock() 46 | ejs.parsers[name] = p 47 | } 48 | 49 | // ParseTo : convert editorjs data to HTML 50 | func (ejs *EditorJS) ParseTo(b []byte, w Writer) error { 51 | r := bytes.NewBuffer(b) 52 | data := Data{} 53 | if err := json.NewDecoder(r).Decode(&data); err != nil { 54 | return err 55 | } 56 | f, flusher := w.(Flusher) 57 | for _, blk := range data.Blocks { 58 | blk.Type = strings.ToLower(strings.TrimSpace(blk.Type)) 59 | parseData, ok := ejs.parsers[blk.Type] 60 | if !ok { 61 | continue 62 | } 63 | if err := parseData(blk.Data, w); err != nil { 64 | return err 65 | } 66 | if flusher { 67 | f.Flush() 68 | } 69 | } 70 | return nil 71 | } 72 | -------------------------------------------------------------------------------- /editorjs_test.go: -------------------------------------------------------------------------------- 1 | package editorjs 2 | 3 | import ( 4 | "bytes" 5 | "io/ioutil" 6 | "testing" 7 | 8 | "github.com/PuerkitoBio/goquery" 9 | "github.com/stretchr/testify/require" 10 | ) 11 | 12 | func TestEditorJS(t *testing.T) { 13 | var ( 14 | ejs = NewEditorJS() 15 | b []byte 16 | err error 17 | ) 18 | 19 | b, err = ioutil.ReadFile("./examples/data.json") 20 | require.NoError(t, err) 21 | 22 | w := new(bytes.Buffer) 23 | if err := ejs.ParseTo(b, w); err != nil { 24 | panic(err) 25 | } 26 | 27 | buf := new(bytes.Buffer) 28 | buf.WriteString(``) 29 | buf.WriteString(``) 30 | buf.WriteString(` 31 | WeTix 32 | 33 | 37 | 38 | 39 | 43 | `) 44 | buf.WriteString(``) 45 | buf.WriteString(``) 46 | buf.WriteString(``) 47 | buf.WriteString(`
`) 48 | err = ejs.ParseTo(b, buf) 49 | require.NoError(t, err) 50 | buf.WriteString(`
`) 51 | buf.WriteString(``) 52 | buf.WriteString(``) 53 | 54 | // Load the HTML document 55 | doc, err := goquery.NewDocumentFromReader(buf) 56 | require.NoError(t, err) 57 | 58 | // verify the html content is matched 59 | { 60 | require.True(t, len(doc.Find("p").Nodes) == 7) 61 | require.True(t, len(doc.Find("a").Nodes) == 2) 62 | require.True(t, len(doc.Find("code").Nodes) == 5) 63 | require.True(t, len(doc.Find("mark").Nodes) == 1) 64 | require.True(t, len(doc.Find("ol").Nodes) == 1) 65 | require.True(t, len(doc.Find("ul").Nodes) == 1) 66 | require.True(t, len(doc.Find("li").Nodes) == 7) 67 | require.True(t, len(doc.Find("table").Nodes) == 1) 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /default.go: -------------------------------------------------------------------------------- 1 | package editorjs 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | 7 | "github.com/tidwall/gjson" 8 | ) 9 | 10 | // DefaultParser : 11 | type DefaultParser struct{} 12 | 13 | func registerDefaultParsers(ejs *EditorJS, def DefaultParser) { 14 | ejs.RegisterParser("header", def.ParseHeader) 15 | ejs.RegisterParser("paragraph", def.ParseParagraph) 16 | ejs.RegisterParser("list", def.ParseList) 17 | ejs.RegisterParser("image", def.ParseImage) 18 | ejs.RegisterParser("table", def.ParseTable) 19 | } 20 | 21 | // ParseHeader : 22 | func (d DefaultParser) ParseHeader(b []byte, w Writer) error { 23 | paths := gjson.GetManyBytes(b, "text", "level") 24 | lvl := paths[1].Int() 25 | w.WriteString(fmt.Sprintf("", lvl)) 26 | w.WriteString(paths[0].String()) 27 | w.WriteString(fmt.Sprintf("", lvl)) 28 | return nil 29 | } 30 | 31 | // ParseParagraph : 32 | func (d DefaultParser) ParseParagraph(b []byte, w Writer) error { 33 | w.WriteString("

" + gjson.GetBytes(b, "text").String() + "

") 34 | return nil 35 | } 36 | 37 | // ParseList : 38 | func (d DefaultParser) ParseList(b []byte, w Writer) error { 39 | var list List 40 | if err := json.Unmarshal(b, &list); err != nil { 41 | return err 42 | } 43 | if list.Style == "ordered" { 44 | w.WriteString("
    ") 45 | defer w.WriteString("
") 46 | } else { 47 | w.WriteString("") 49 | } 50 | for _, itm := range list.Items { 51 | w.WriteString("
  • " + itm + "
  • ") 52 | } 53 | return nil 54 | } 55 | 56 | // ParseImage : 57 | func (d DefaultParser) ParseImage(b []byte, w Writer) error { 58 | var img Image 59 | w.WriteString("
    ") 60 | defer w.WriteString("
    ") 61 | if err := json.Unmarshal(b, &img); err != nil { 62 | return err 63 | } 64 | // alt string cannot have space 65 | w.WriteString(`no-image`) 66 | if img.Caption != "" { 67 | w.WriteString(`
    ` + img.Caption + `
    `) 68 | } 69 | return nil 70 | } 71 | 72 | // ParseTable : 73 | func (d DefaultParser) ParseTable(b []byte, w Writer) error { 74 | var table Table 75 | w.WriteString("") 76 | defer w.WriteString("
    ") 77 | if err := json.Unmarshal(b, &table); err != nil { 78 | return err 79 | } 80 | 81 | for i, line := range table.Content { 82 | w.WriteString("") 83 | tag := `td` 84 | if table.WithHeadings && i == 0 { 85 | tag = `th` 86 | } 87 | 88 | for _, info := range line { 89 | w.WriteString("<" + tag + ">" + info + "") 90 | 91 | } 92 | 93 | w.WriteString("") 94 | } 95 | return nil 96 | } 97 | -------------------------------------------------------------------------------- /examples/data.json: -------------------------------------------------------------------------------- 1 | { 2 | "time": 1589098011153, 3 | "blocks": [ 4 | { 5 | "type": "header", 6 | "data": { 7 | "text": "Editor.js", 8 | "level": 2 9 | } 10 | }, 11 | { 12 | "type": "paragraph", 13 | "data": { 14 | "text": "Hey. Meet the new Editor. On this page you can see it in action — try to edit this text." 15 | } 16 | }, 17 | { 18 | "type": "header", 19 | "data": { 20 | "text": "Key features", 21 | "level": 3 22 | } 23 | }, 24 | { 25 | "type": "list", 26 | "data": { 27 | "style": "unordered", 28 | "items": [ 29 | "It is a block-styled editor", 30 | "It returns clean data output in JSON", 31 | "Designed to be extendable and pluggable with a simple API" 32 | ] 33 | } 34 | }, 35 | { 36 | "type": "list", 37 | "data": { 38 | "style": "ordered", 39 | "items": [ 40 | "It is a block-styled editor", 41 | "What does it mean «block-styled editor»", 42 | "It returns clean data output in JSON", 43 | "Designed to be extendable and pluggable with a simple API" 44 | ] 45 | } 46 | }, 47 | { 48 | "type": "header", 49 | "data": { 50 | "text": "What does it mean «block-styled editor»", 51 | "level": 3 52 | } 53 | }, 54 | { 55 | "type": "paragraph", 56 | "data": { 57 | "text": "Workspace in classic editors is made of a single contenteditable element, used to create different HTML markups. Editor.js workspace consists of separate Blocks: paragraphs, headings, images, lists, quotes, etc. Each of them is an independent contenteditable element (or more complex structure) provided by Plugin and united by Editor's Core." 58 | } 59 | }, 60 | { 61 | "type": "paragraph", 62 | "data": { 63 | "text": "There are dozens of ready-to-use Blocks and the simple API for creation any Block you need. For example, you can implement Blocks for Tweets, Instagram posts, surveys and polls, CTA-buttons and even games." 64 | } 65 | }, 66 | { 67 | "type": "header", 68 | "data": { 69 | "text": "What does it mean clean data output", 70 | "level": 3 71 | } 72 | }, 73 | { 74 | "type": "paragraph", 75 | "data": { 76 | "text": "Classic WYSIWYG-editors produce raw HTML-markup with both content data and content appearance. On the contrary, Editor.js outputs JSON object with data of each Block. You can see an example below" 77 | } 78 | }, 79 | { 80 | "type": "paragraph", 81 | "data": { 82 | "text": "Given data can be used as you want: render with HTML for Web clients, render natively for mobile apps, create markup for Facebook Instant Articles or Google AMP, generate an audio version and so on." 83 | } 84 | }, 85 | { 86 | "type": "paragraph", 87 | "data": { 88 | "text": "Clean data is useful to sanitize, validate and process on the backend." 89 | } 90 | }, 91 | { 92 | "type": "delimiter", 93 | "data": {} 94 | }, 95 | { 96 | "type": "table", 97 | "data": { 98 | "withHeadings": false, 99 | "content": [ 100 | ["column 1", "column 2"], 101 | ["text 1", "text 2"], 102 | ["", "text 3"] 103 | ] 104 | } 105 | }, 106 | { 107 | "type": "paragraph", 108 | "data": { 109 | "text": "We have been working on this project more than three years. Several large media projects help us to test and debug the Editor, to make it's core more stable. At the same time we significantly improved the API. Now, it can be used to create any plugin for any task. Hope you enjoy. 😏" 110 | } 111 | }, 112 | { 113 | "type": "image", 114 | "data": { 115 | "file": { 116 | "url": "https://codex.so/public/app/img/external/codex2x.png" 117 | }, 118 | "caption": "", 119 | "withBorder": false, 120 | "stretched": false, 121 | "withBackground": false 122 | } 123 | } 124 | ], 125 | "version": "2.17.0" 126 | } 127 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/PuerkitoBio/goquery v1.8.1 h1:uQxhNlArOIdbrH1tr0UXwdVFgDcZDrZVdcpygAcwmWM= 2 | github.com/PuerkitoBio/goquery v1.8.1/go.mod h1:Q8ICL1kNUJ2sXGoAhPGUdYDJvgQgHzJsnnd3H7Ho5jQ= 3 | github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x004T2c= 4 | github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA= 5 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 6 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 7 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 8 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 9 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 10 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 11 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 12 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 13 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 14 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 15 | github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= 16 | github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 17 | github.com/tidwall/gjson v1.17.0 h1:/Jocvlh98kcTfpN2+JzGQWQcqrPQwDrVEMApx/M5ZwM= 18 | github.com/tidwall/gjson v1.17.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= 19 | github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= 20 | github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= 21 | github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= 22 | github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= 23 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 24 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 25 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 26 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 27 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 28 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 29 | golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 30 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 31 | golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= 32 | golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= 33 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 34 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 35 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 36 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 37 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 38 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 39 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 40 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 41 | golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 42 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 43 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 44 | golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= 45 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 46 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 47 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 48 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 49 | golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 50 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 51 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 52 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 53 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 54 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 55 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 56 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 57 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 58 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 59 | --------------------------------------------------------------------------------