notion_to_md 2.2.4 → 2.3.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1fb7426b97e6a8fdd9cd27cd1254939c5ccd3cb3455920ff35071d5c3429a4f1
4
- data.tar.gz: 523b5ff29517de54208a7506970115f11d087c9bb314e4b0a626650760ec89f3
3
+ metadata.gz: 26fb275bacfb3f94bc62bf1f15de2de6636f043a2a7d11b66ecba0f5df2a39ec
4
+ data.tar.gz: 53308f1bc92d17a1125969e030f6a070cc82384808b34787f6ed0c853fdbaca9
5
5
  SHA512:
6
- metadata.gz: f9f181ba6bdbece59d9f8f5ad81c05afe86e942fcca913674ca42f04876a158091e8dc21b4eb392d9226c8839a8eea021c223a9d40c681093c9a16f153ec9793
7
- data.tar.gz: 4999b3781d9bf53357f8e5f8585beef8c4c3b501c6b35d360f2593b8596ae5b615979bb20246b2aee911342f5d97824a0c1da882c8d54a544fc14c1e0e61dbb1
6
+ metadata.gz: b0522c546ae98279d0a20aff3a8bf5d225dc74eea6c5504a0f35bdf6bb692e870c6719ea088251556fb49ad3318031f200754ac2691254fa3d0d4287035e4045
7
+ data.tar.gz: 2430ca47d1fe39e9f73cc9aad187d6457440edb94cd60dea9d1d82d562a057339acdef9c85ac8b1db79d34acd9db754493a62876643deab50af310472e125b06
data/README.md CHANGED
@@ -25,6 +25,14 @@ notion_converter = NotionToMd::Converter.new(page_id: 'b91d5...', token: 'secret
25
25
  md = notion_converter.convert
26
26
  ```
27
27
 
28
+ Since v2.3 you can also use the convenient `convert` method from the root module.
29
+
30
+ ```ruby
31
+ require 'notion_to_md'
32
+
33
+ md = NotionToMd.convert(page_id: 'b91d5...', token: 'secret_...')
34
+ ```
35
+
28
36
  If the secret token is provided as an environment variable —`NOTION_TOKEN`—, there's no need to pass it as an argument to the constructor.
29
37
 
30
38
  ```bash
@@ -36,6 +44,8 @@ require 'notion_to_md'
36
44
 
37
45
  notion_converter = NotionToMd::Converter.new(page_id: 'b91d5...')
38
46
  md = notion_converter.convert
47
+ # or
48
+ md = NotionToMd.convert(page_id: 'b91d5...')
39
49
  ```
40
50
 
41
51
  And that's all. The `md` is a string variable containing the notion page formatted in markdown.
@@ -49,7 +59,7 @@ Everything in a notion page body is a [block object](https://developers.notion.c
49
59
  * `heading_2`
50
60
  * `heading_3`
51
61
  * `bulleted_list_item`
52
- * `numbered_list_item` as `bulleted_list_item`
62
+ * `numbered_list_item` (supported since v2.3, in previous versions is displayed as `bulleted_list_item`)
53
63
  * `to_do`
54
64
  * `image`
55
65
  * `bookmark`
@@ -77,6 +87,8 @@ By default, the front matter section is not included to the document. To do so,
77
87
 
78
88
  ```ruby
79
89
  NotionToMd::Converter.new(page_id: 'b91d5...').convert(frontmatter: tue)
90
+ # or
91
+ NotionToMd.convert(page_id: 'b91d5...', frontmatter: true) # Since v2.3
80
92
  ```
81
93
 
82
94
  Default notion [properties](https://developers.notion.com/reference/page#all-pages) are page `id`, `title`, `created_time`, `last_edited_time`, `icon`, `archived` and `cover`.
@@ -9,6 +9,8 @@ module NotionToMd
9
9
 
10
10
  attr_reader :block, :children
11
11
 
12
+ def_delegators :block, :type
13
+
12
14
  # === Parameters:
13
15
  # block::
14
16
  # A {Notion::Messages::Message}[https://github.com/orbit-love/notion-ruby-client/blob/main/lib/notion/messages/message.rb] object.
@@ -32,14 +34,18 @@ module NotionToMd
32
34
  #
33
35
  def to_md(tab_width: 0)
34
36
  block_type = block.type.to_sym
35
- md = Types.send(block_type, block[block_type])
37
+ md = Types.send(block_type, block[block_type]) + newline
36
38
  md + build_nested_blocks(tab_width + 1)
37
39
  rescue NoMethodError
38
40
  Logger.info("Unsupported block type: #{block_type}")
39
41
  nil
40
42
  end
41
43
 
42
- private
44
+ protected
45
+
46
+ def newline
47
+ "\n\n"
48
+ end
43
49
 
44
50
  def build_nested_blocks(tab_width)
45
51
  mds = markdownify_children(tab_width).compact
@@ -54,7 +60,7 @@ module NotionToMd
54
60
 
55
61
  def indent_children(mds, tab_width)
56
62
  mds.map do |md|
57
- "\n\n#{"\t" * tab_width}#{md}"
63
+ "#{"\t" * tab_width}#{md}"
58
64
  end
59
65
  end
60
66
  end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module NotionToMd
4
+ module Blocks
5
+ class Builder
6
+ ##
7
+ # Array containing the block types allowed to have nested blocks (children).
8
+ BLOCKS_WITH_PERMITTED_CHILDREN = %i[
9
+ bulleted_list_item
10
+ numbered_list_item
11
+ paragraph
12
+ to_do
13
+ table
14
+ ].freeze
15
+
16
+ # === Parameters
17
+ # block::
18
+ # A {Notion::Messages::Message}[https://github.com/orbit-love/notion-ruby-client/blob/main/lib/notion/messages/message.rb] object.
19
+ #
20
+ # === Returns
21
+ # A boolean indicating if the blocked passed in
22
+ # is permitted to have children based on its type.
23
+ #
24
+ def self.permitted_children_for?(block:)
25
+ BLOCKS_WITH_PERMITTED_CHILDREN.include?(block.type.to_sym) && block.has_children
26
+ end
27
+
28
+ attr_reader :block_id, :fetch_blocks
29
+
30
+ # === Parameters
31
+ # block_id::
32
+ # A string representing a notion block id .
33
+ # fetch_blocks::
34
+ # A block that fetches the blocks from the Notion API.
35
+ #
36
+ # === Returns
37
+ # An array of NotionToMd::Blocks::Block.
38
+ #
39
+ def initialize(block_id:, &fetch_blocks)
40
+ @block_id = block_id
41
+ @fetch_blocks = fetch_blocks
42
+ end
43
+
44
+ # === Parameters
45
+ #
46
+ # === Returns
47
+ # An array of NotionToMd::Blocks::Block.
48
+ #
49
+ def build
50
+ notion_messages = fetch_blocks.call(block_id)
51
+ blocks = notion_messages.results.map do |block|
52
+ children = if Builder.permitted_children_for?(block: block)
53
+ Builder.new(block_id: block.id, &fetch_blocks).build
54
+ else
55
+ []
56
+ end
57
+ Factory.build(block: block, children: children)
58
+ end
59
+
60
+ Normalizer.normalize(blocks: blocks)
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module NotionToMd
4
+ module Blocks
5
+ class BulletedListBlock < Block
6
+ def initialize(children: [])
7
+ @children = children
8
+ end
9
+
10
+ # === Parameters:
11
+ # tab_width::
12
+ # The number of tabs used to indent the block.
13
+ #
14
+ # === Returns
15
+ # The current block (and its children) converted to a markdown string.
16
+ #
17
+ def to_md(tab_width: 0)
18
+ if tab_width.zero?
19
+ build_nested_blocks(tab_width) + newline
20
+ else
21
+ build_nested_blocks(tab_width)
22
+ end
23
+ end
24
+
25
+ def type
26
+ 'bulleted_list'
27
+ end
28
+
29
+ def newline
30
+ "\n"
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module NotionToMd
4
+ module Blocks
5
+ class BulletedListItemBlock < Block
6
+ def newline
7
+ "\n"
8
+ end
9
+
10
+ def indent_children(mds, _tab_width)
11
+ mds
12
+ end
13
+ end
14
+ end
15
+ end
@@ -7,6 +7,14 @@ module NotionToMd
7
7
  case block.type.to_sym
8
8
  when :table
9
9
  TableBlock.new(block: block, children: children)
10
+ when :table_row
11
+ TableRowBlock.new(block: block, children: children)
12
+ when :bulleted_list_item
13
+ BulletedListItemBlock.new(block: block, children: children)
14
+ when :numbered_list_item
15
+ NumberedListItemBlock.new(block: block, children: children)
16
+ when :to_do
17
+ ToDoListItemBlock.new(block: block, children: children)
10
18
  else
11
19
  Blocks::Block.new(block: block, children: children)
12
20
  end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ module NotionToMd
4
+ module Blocks
5
+ class Normalizer
6
+ # === Parameters
7
+ # blocks::
8
+ # An array of NotionToMd::Blocks::Block.
9
+ #
10
+ def self.normalize(blocks:)
11
+ new(blocks: blocks).normalize
12
+ end
13
+
14
+ attr_reader :normalized_blocks
15
+
16
+ def initialize(blocks:)
17
+ @normalized_blocks = blocks.dup
18
+ end
19
+
20
+ def normalize
21
+ normalize_for :bulleted_list_item
22
+ normalize_for :numbered_list_item
23
+ normalize_for :to_do
24
+ end
25
+
26
+ def normalize_for(type)
27
+ new_blocks = []
28
+
29
+ normalized_blocks.each do |block|
30
+ if block.type.to_sym == type
31
+ blocks_to_normalize << block
32
+ else
33
+ # When we encounter a block that is not of the provided type,
34
+ # we need to normalize the blocks we've collected so far.
35
+ # Then we add the current block to the new blocks array.
36
+ # This is because we want to keep the order of the blocks.
37
+ new_blocks << new_block_and_reset_blocks_to_normalize(type) unless blocks_to_normalize.empty?
38
+ new_blocks << block
39
+ end
40
+ end
41
+
42
+ # If the last block is the provided type, it won't be added to the new blocks array.
43
+ # So, we need to normalize the blocks we've collected so far.
44
+ new_blocks << new_block_and_reset_blocks_to_normalize(type) unless blocks_to_normalize.empty?
45
+
46
+ normalized_blocks.replace(new_blocks)
47
+ end
48
+
49
+ private
50
+
51
+ def new_block_and_reset_blocks_to_normalize(type)
52
+ new_block = new_block_for(type, blocks_to_normalize)
53
+ @blocks_to_normalize = []
54
+ new_block
55
+ end
56
+
57
+ def blocks_to_normalize
58
+ @blocks_to_normalize ||= []
59
+ end
60
+
61
+ def new_block_for(type, children)
62
+ case type
63
+ when :bulleted_list_item
64
+ BulletedListBlock.new(children: children)
65
+ when :numbered_list_item
66
+ NumberedListBlock.new(children: children)
67
+ when :to_do
68
+ ToDoListBlock.new(children: children)
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module NotionToMd
4
+ module Blocks
5
+ class NumberedListBlock < BulletedListBlock
6
+ def type
7
+ 'numbered_list'
8
+ end
9
+
10
+ protected
11
+
12
+ def markdownify_children(tab_width)
13
+ children.map.with_index do |nested_block, index|
14
+ nested_block.to_md(tab_width: tab_width, index: index + 1)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './bulleted_list_item_block'
4
+
5
+ module NotionToMd
6
+ module Blocks
7
+ class NumberedListItemBlock < BulletedListItemBlock
8
+ # === Parameters:
9
+ # tab_width::
10
+ # The number of tabs used to indent the block.
11
+ # index::
12
+ # The index of the block in its parent's children array used to number the item.
13
+ #
14
+ # === Returns
15
+ # The current block (and its children) converted to a markdown string.
16
+ #
17
+ def to_md(tab_width: 0, index: nil)
18
+ md = Types.numbered_list_item(block[block.type.to_sym], index) + newline
19
+ md + build_nested_blocks(tab_width + 1)
20
+ rescue NoMethodError
21
+ Logger.info("Unsupported block type: #{block.type}")
22
+ nil
23
+ end
24
+ end
25
+ end
26
+ end
@@ -10,17 +10,21 @@ module NotionToMd
10
10
  table_aligment = markdownify_aligment
11
11
  table_body = table[1..table.size]
12
12
 
13
- [table_header, table_aligment, table_body.join("\n")].compact.join("\n")
13
+ [table_header, table_aligment, table_body].compact.join + newline
14
14
  end
15
15
 
16
- private
16
+ protected
17
17
 
18
18
  def row_size
19
19
  @row_size ||= children.first.block.table_row.cells.size
20
20
  end
21
21
 
22
22
  def markdownify_aligment
23
- "|#{row_size.times.map { '---' }.join('|')}|" if block.table.has_column_header
23
+ "|#{row_size.times.map { '---' }.join('|')}|\n" if block.table.has_column_header
24
+ end
25
+
26
+ def newline
27
+ "\n"
24
28
  end
25
29
  end
26
30
  end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module NotionToMd
4
+ module Blocks
5
+ class TableRowBlock < Block
6
+ def newline
7
+ "\n"
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module NotionToMd
4
+ module Blocks
5
+ class ToDoListBlock < BulletedListBlock
6
+ def type
7
+ 'to_do_list'
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './bulleted_list_item_block'
4
+
5
+ module NotionToMd
6
+ module Blocks
7
+ class ToDoListItemBlock < BulletedListItemBlock
8
+ def newline
9
+ "\n"
10
+ end
11
+ end
12
+ end
13
+ end
@@ -36,9 +36,10 @@ module NotionToMd
36
36
  "- #{convert_text(block)}"
37
37
  end
38
38
 
39
- def numbered_list_item(block)
40
- Logger.info('numbered_list_item type not supported. Shown as bulleted_list_item.')
41
- bulleted_list_item(block)
39
+ def numbered_list_item(block, index = nil)
40
+ return bulleted_list_item(block) if index.nil?
41
+
42
+ "#{index}. #{convert_text(block)}"
42
43
  end
43
44
 
44
45
  def to_do(block)
@@ -1,51 +1,32 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative './blocks/block'
3
+ require_relative './blocks/builder'
4
+ require_relative './blocks/normalizer'
4
5
  require_relative './blocks/factory'
5
- require_relative './blocks/table_block'
6
6
  require_relative './blocks/types'
7
+ require_relative './blocks/block'
8
+ require_relative './blocks/table_block'
9
+ require_relative './blocks/table_row_block'
10
+ require_relative './blocks/bulleted_list_block'
11
+ require_relative './blocks/bulleted_list_item_block'
12
+ require_relative './blocks/numbered_list_block'
13
+ require_relative './blocks/numbered_list_item_block'
14
+ require_relative './blocks/to_do_list_block'
15
+ require_relative './blocks/to_do_list_item_block'
7
16
 
8
17
  module NotionToMd
9
18
  module Blocks
10
- ##
11
- # Array containing the block types allowed to have nested blocks (children).
12
- PERMITTED_CHILDREN = [
13
- Types.method(:bulleted_list_item).name,
14
- Types.method(:numbered_list_item).name,
15
- Types.method(:paragraph).name,
16
- Types.method(:to_do).name,
17
- :table
18
- ].freeze
19
-
20
- # === Parameters
21
- # block::
22
- # A {Notion::Messages::Message}[https://github.com/orbit-love/notion-ruby-client/blob/main/lib/notion/messages/message.rb] object.
23
- #
24
- # === Returns
25
- # A boolean indicating if the blocked passed in
26
- # is permitted to have children based on its type.
27
- #
28
- def self.permitted_children?(block:)
29
- PERMITTED_CHILDREN.include?(block.type.to_sym) && block.has_children
30
- end
31
-
32
19
  # === Parameters
33
20
  # block_id::
34
21
  # A string representing a notion block id .
22
+ # fetch_blocks::
23
+ # A block that fetches the blocks from the Notion API.
35
24
  #
36
25
  # === Returns
37
26
  # An array of NotionToMd::Blocks::Block.
38
27
  #
39
28
  def self.build(block_id:, &fetch_blocks)
40
- blocks = fetch_blocks.call(block_id)
41
- blocks.results.map do |block|
42
- children = if permitted_children?(block: block)
43
- build(block_id: block.id, &fetch_blocks)
44
- else
45
- []
46
- end
47
- Factory.build(block: block, children: children)
48
- end
29
+ Builder.new(block_id: block_id, &fetch_blocks).build
49
30
  end
50
31
  end
51
32
  end
@@ -0,0 +1,17 @@
1
+ module NotionToMd
2
+ module Helpers
3
+ module YamlSanitizer
4
+ # Escape the frontmatter value if it contains a colon or a dash followed by a space
5
+ # @param value [String] the value to escape
6
+ # @return [String] the escaped value
7
+ def escape_frontmatter_value(value)
8
+ if value.match?(/: |-\s/)
9
+ # Escape the double quotes inside the string
10
+ "\"#{value.gsub('"', '\"')}\""
11
+ else
12
+ value
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1 @@
1
+ require_relative './helpers/yaml_sanitizer'
@@ -2,6 +2,8 @@
2
2
 
3
3
  module NotionToMd
4
4
  class Page
5
+ include Helpers::YamlSanitizer
6
+
5
7
  attr_reader :page, :blocks
6
8
 
7
9
  def initialize(page:, blocks:)
@@ -45,7 +47,7 @@ module NotionToMd
45
47
  end
46
48
 
47
49
  def body
48
- @body ||= blocks.map(&:to_md).compact.join("\n\n")
50
+ @body ||= blocks.map(&:to_md).compact.join
49
51
  end
50
52
 
51
53
  def frontmatter
@@ -77,11 +79,11 @@ module NotionToMd
77
79
  def default_props
78
80
  @default_props ||= {
79
81
  'id' => id,
80
- 'title' => title,
82
+ 'title' => escape_frontmatter_value(title),
81
83
  'created_time' => created_time,
82
84
  'cover' => cover,
83
85
  'icon' => icon,
84
- 'last_edited_time' => last_edited_time,
86
+ 'last_edited_time' => last_edited_time.to_s.dump,
85
87
  'archived' => archived
86
88
  }
87
89
  end
@@ -3,6 +3,8 @@
3
3
  module NotionToMd
4
4
  class PageProperty
5
5
  class << self
6
+ include Helpers::YamlSanitizer
7
+
6
8
  def file(prop)
7
9
  prop.dig(:file, :url)
8
10
  rescue NoMethodError
@@ -28,7 +30,7 @@ module NotionToMd
28
30
  end
29
31
 
30
32
  def select(prop)
31
- prop.dig(:select, :name)
33
+ escape_frontmatter_value(prop.dig(:select, :name))
32
34
  rescue NoMethodError
33
35
  nil
34
36
  end
@@ -85,7 +87,8 @@ module NotionToMd
85
87
  end
86
88
 
87
89
  def rich_text(prop)
88
- prop[:rich_text].map { |text| text[:plain_text] }.join
90
+ text = prop[:rich_text].map { |text| text[:plain_text] }.join
91
+ text.blank? ? nil : escape_frontmatter_value(text)
89
92
  rescue NoMethodError
90
93
  nil
91
94
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module NotionToMd
4
- VERSION = '2.2.4'
4
+ VERSION = '2.3.1'
5
5
  end
data/lib/notion_to_md.rb CHANGED
@@ -4,6 +4,7 @@ require 'notion'
4
4
  require 'logger'
5
5
  require 'active_support/inflector'
6
6
  require 'active_support/core_ext/object/blank'
7
+ require_relative './notion_to_md/helpers'
7
8
  require_relative './notion_to_md/version'
8
9
  require_relative './notion_to_md/converter'
9
10
  require_relative './notion_to_md/logger'
@@ -12,3 +13,20 @@ require_relative './notion_to_md/page'
12
13
  require_relative './notion_to_md/blocks'
13
14
  require_relative './notion_to_md/text'
14
15
  require_relative './notion_to_md/text_annotation'
16
+
17
+ module NotionToMd
18
+ # === Parameters
19
+ # page_id::
20
+ # A string representing the notion page id.
21
+ # token::
22
+ # The notion API secret token. The token can replaced by the environment variable NOTION_TOKEN.
23
+ # frontmatter::
24
+ # A boolean indicating whether to include the page properties as frontmatter.
25
+ #
26
+ # === Returns
27
+ # The string that represent the markdown document.
28
+ #
29
+ def self.convert(page_id:, token: nil, frontmatter: false)
30
+ Converter.new(page_id: page_id, token: token).convert(frontmatter: frontmatter)
31
+ end
32
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: notion_to_md
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.2.4
4
+ version: 2.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Enrique Arias
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-10-22 00:00:00.000000000 Z
11
+ date: 2023-12-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -108,6 +108,20 @@ dependencies:
108
108
  - - ">="
109
109
  - !ruby/object:Gem::Version
110
110
  version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: vcr
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
111
125
  description: Notion Markdown Exporter in Ruby
112
126
  email: emoriarty81@gmail.com
113
127
  executables: []
@@ -118,10 +132,21 @@ files:
118
132
  - lib/notion_to_md.rb
119
133
  - lib/notion_to_md/blocks.rb
120
134
  - lib/notion_to_md/blocks/block.rb
135
+ - lib/notion_to_md/blocks/builder.rb
136
+ - lib/notion_to_md/blocks/bulleted_list_block.rb
137
+ - lib/notion_to_md/blocks/bulleted_list_item_block.rb
121
138
  - lib/notion_to_md/blocks/factory.rb
139
+ - lib/notion_to_md/blocks/normalizer.rb
140
+ - lib/notion_to_md/blocks/numbered_list_block.rb
141
+ - lib/notion_to_md/blocks/numbered_list_item_block.rb
122
142
  - lib/notion_to_md/blocks/table_block.rb
143
+ - lib/notion_to_md/blocks/table_row_block.rb
144
+ - lib/notion_to_md/blocks/to_do_list_block.rb
145
+ - lib/notion_to_md/blocks/to_do_list_item_block.rb
123
146
  - lib/notion_to_md/blocks/types.rb
124
147
  - lib/notion_to_md/converter.rb
148
+ - lib/notion_to_md/helpers.rb
149
+ - lib/notion_to_md/helpers/yaml_sanitizer.rb
125
150
  - lib/notion_to_md/logger.rb
126
151
  - lib/notion_to_md/page.rb
127
152
  - lib/notion_to_md/page_property.rb