jekyll-notion 2.4.3 → 3.0.0.beta1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,111 +7,121 @@ module JekyllNotion
7
7
  def generate(site)
8
8
  @site = site
9
9
 
10
- return unless notion_token? && config?
10
+ return unless config? && notion_token?
11
11
 
12
+ assert_configuration
12
13
  setup
13
14
 
14
- if fetch_on_watch? || cache_empty?
15
- read_notion_databases
16
- read_notion_pages
17
- else
18
- collections.each_pair { |key, val| @site.collections[key] = val }
19
- data.each_pair { |key, val| @site.data[key] = val }
20
- pages.each { |page| @site.pages << page }
21
- end
15
+ @notion_client = Notion::Client.new
16
+
17
+ import_notion_databases
18
+ import_notion_pages
22
19
  end
23
20
 
24
- def config_databases
25
- if config["database"]
26
- Jekyll.logger.warn("Jekyll Notion:",
27
- "database property is deprecated, use databases instead.")
28
- end
21
+ def config
22
+ @config ||= @site.config["notion"] || {}
23
+ end
29
24
 
25
+ def config_databases
30
26
  config["databases"] || []
31
27
  end
32
28
 
33
29
  def config_pages
34
- if config["page"]
35
- Jekyll.logger.warn("Jekyll Notion:",
36
- "page property is deprecated, use pages instead.")
37
- end
38
30
  config["pages"] || []
39
31
  end
40
32
 
41
- def collections
42
- @collections ||= {}
43
- end
44
-
45
- def data
46
- @data ||= {}
47
- end
48
-
49
- def pages
50
- @pages ||= []
51
- end
52
-
53
33
  protected
54
34
 
55
- def cache_empty?
56
- collections.empty? && pages.empty? && data.empty?
57
- end
58
-
59
- def read_notion_databases
35
+ def import_notion_databases
60
36
  config_databases.each do |db_config|
61
- db = NotionDatabase.new(:config => db_config)
62
- DatabaseFactory.for(:notion_resource => db, :site => @site, :plugin => self).generate
37
+ next if db_config["id"].nil?
38
+
39
+ notion_database = NotionToMd::Database.call(:id => db_config["id"],
40
+ :notion_client => @notion_client, :filter => db_config["filter"], :sorts => db_config["sorts"], :frontmatter => true)
41
+ Generators::Collection.call(:config => db_config, :site => @site,
42
+ :notion_pages => notion_database.pages)
63
43
  end
64
44
  end
65
45
 
66
- def read_notion_pages
46
+ def import_notion_pages
67
47
  config_pages.each do |page_config|
68
- page = NotionPage.new(:config => page_config)
69
- PageFactory.for(:notion_resource => page, :site => @site, :plugin => self).generate
70
- end
71
- end
48
+ next if page_config["id"].nil?
72
49
 
73
- def config
74
- @config ||= @site.config["notion"] || {}
50
+ notion_page = NotionToMd::Page.call(:id => page_config["id"], :notion_client => @notion_client,
51
+ :frontmatter => true)
52
+ Generators::Page.call(:config => page_config, :site => @site,
53
+ :notion_pages => [notion_page])
54
+ end
75
55
  end
76
56
 
77
- def fetch_on_watch?
78
- Jekyll.logger.warn("Jekyll Notion:",
79
- "[Warning] The fetch_on_watch feature is deprecated in preference to the cache mechanism. It will be removed in the next major release.")
80
-
81
- config["fetch_on_watch"] == true
57
+ def notion_token
58
+ ENV.fetch("NOTION_TOKEN", nil)
82
59
  end
83
60
 
84
61
  def notion_token?
85
62
  if ENV["NOTION_TOKEN"].nil? || ENV["NOTION_TOKEN"].empty?
86
- Jekyll.logger.warn("Jekyll Notion:",
87
- "Cannot read from Notion becuase NOTION_TOKEN was not provided")
63
+ Jekyll.logger.warn(
64
+ "Jekyll Notion:",
65
+ "Skipping import: NOTION_TOKEN is missing. Please set the NOTION_TOKEN environment variable to enable Notion integration."
66
+ )
67
+
88
68
  return false
89
69
  end
90
70
  true
91
71
  end
92
72
 
93
73
  def config?
94
- if config.empty?
95
- Jekyll.logger.warn("Jekyll Notion:", "No configuration provided")
74
+ return false unless @site.config.key?("notion")
75
+
76
+ if config.empty? || (config_databases.empty? && config_pages.empty?)
77
+ Jekyll.logger.warn("Jekyll Notion:",
78
+ "The `databases` or `pages` configuration are not declared. Skipping import.")
96
79
  return false
97
80
  end
81
+
98
82
  true
99
83
  end
100
84
 
101
85
  def setup
102
86
  # Cache Notion API responses
103
- if ENV["JEKYLL_ENV"] != "test" && cache?
104
- JekyllNotion::Cacheable.setup(config["cache_dir"])
105
- JekyllNotion::CollectionGenerator.prepend(JekyllNotion::Cacheable)
106
- JekyllNotion::PageGenerator.prepend(JekyllNotion::Cacheable)
107
- JekyllNotion::DataGenerator.prepend(JekyllNotion::Cacheable)
108
- end
87
+ JekyllNotion::Cacheable.configure(
88
+ :cache_dir => config["cache_dir"],
89
+ :cache_enabled => cache?
90
+ )
109
91
  end
110
92
 
111
93
  def cache?
112
- return true if config["cache"].nil?
94
+ value = if config.key?("cache")
95
+ config["cache"]
96
+ else
97
+ ENV.fetch("JEKYLL_NOTION_CACHE", nil)
98
+ end
99
+ value.nil? || !falsy?(value)
100
+ end
113
101
 
114
- config["cache"] == true.to_s
102
+ private
103
+
104
+ def falsy?(value)
105
+ %w(0 false no).include?(value.to_s.downcase)
106
+ end
107
+
108
+ def assert_configuration
109
+ if config.key?("fetch_on_watch")
110
+ Jekyll.logger.warn(
111
+ "Jekyll Notion:",
112
+ "The `fetch_on_watch` option was removed in v3. Please use the cache mechanism instead: https://github.com/emoriarty/jekyll-notion#cache"
113
+ )
114
+ end
115
+
116
+ if config.key?("database")
117
+ Jekyll.logger.warn("Jekyll Notion:",
118
+ "The `database` key is deprecated. Please use `databases` instead.")
119
+ end
120
+
121
+ if config["page"]
122
+ Jekyll.logger.warn("Jekyll Notion:",
123
+ "The `page` key is deprecated. Please use `pages` instead.")
124
+ end
115
125
  end
116
126
  end
117
127
  end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JekyllNotion
4
+ module Generators
5
+ class Collection < Generator
6
+ include Collectionable
7
+
8
+ def call
9
+ if config["data"].nil?
10
+ notion_pages.each { |notion_page| generate_document(notion_page) }
11
+ else
12
+ Data.call(:config => config, :site => site,
13
+ :notion_pages => notion_pages)
14
+ end
15
+ end
16
+
17
+ private
18
+
19
+ def generate_document(notion_page)
20
+ return if page_exists?(site_collection.docs, notion_page)
21
+
22
+ document = make_doc(notion_page)
23
+
24
+ site_collection.docs << document
25
+
26
+ log_page(document)
27
+ end
28
+
29
+ def site_collection
30
+ @site.collections[collection_name]
31
+ end
32
+
33
+ def make_doc(page)
34
+ new_post = DocumentWithoutAFile.new(
35
+ make_path(page),
36
+ { :site => @site, :collection => site_collection }
37
+ )
38
+ new_post.content = page.to_md
39
+ new_post.read
40
+ new_post
41
+ end
42
+
43
+ def make_path(page)
44
+ "_#{collection_name}/#{make_filename(page)}"
45
+ end
46
+
47
+ def collection_name
48
+ config["collection"] || "posts"
49
+ end
50
+
51
+ def make_filename(page)
52
+ if collection_name == "posts"
53
+ "#{date_for(page)}-#{Jekyll::Utils.slugify(page.title, :mode => "latin")}.md"
54
+ else
55
+ "#{Jekyll::Utils.slugify(page.title, :mode => "latin")}.md"
56
+ end
57
+ end
58
+
59
+ def date_for(page)
60
+ # The "date" property overwrites the Jekyll::Document#data["date"] key
61
+ # which is the date used by Jekyll to set the post date.
62
+ Time.parse(page.props["date"]).to_date
63
+ rescue TypeError, NoMethodError
64
+ # Because the "date" property is not required,
65
+ # it fallbacks to the created_time which is always present.
66
+ Time.parse(page.created_time).to_date
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JekyllNotion
4
+ module Generators
5
+ module Collectionable
6
+ def page_exists?(collection, notion_page)
7
+ page_exists = collection.any? do |page|
8
+ page.data["title"]&.downcase == notion_page.title.downcase
9
+ end
10
+
11
+ if page_exists
12
+ Jekyll.logger.warn("Jekyll Notion:",
13
+ "Page `#{notion_page.title}` exists — skipping Notion import.")
14
+ end
15
+
16
+ page_exists
17
+ end
18
+
19
+ def log_page(page)
20
+ Jekyll.logger.info("Jekyll Notion:", "Page => #{page.data["title"]}")
21
+ Jekyll.logger.info("", "URL => #{page.url}")
22
+ Jekyll.logger.debug("", "Props => #{page.data.keys.inspect}")
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JekyllNotion
4
+ module Generators
5
+ class Data < Generator
6
+ # pages => Array of NotionToMd::Page
7
+ def call
8
+ data = if notion_pages.size > 1
9
+ notion_pages.map do |page|
10
+ page.send(:frontmatter_properties).merge({ "content" => convert(page) })
11
+ end
12
+ else
13
+ notion_pages.first.send(:frontmatter_properties).merge({ "content" => convert(notion_pages.first) })
14
+ end
15
+
16
+ @site.data[config["data"]] = data
17
+
18
+ log_data(data)
19
+ end
20
+
21
+ protected
22
+
23
+ # Convert the notion page body using the site.converters.
24
+ #
25
+ # Returns String the converted content.
26
+ def convert(page)
27
+ converters.reduce(page.body) do |output, converter|
28
+ converter.convert(output)
29
+ rescue StandardError => e
30
+ Jekyll.logger.error "Conversion error:",
31
+ "#{converter.class} encountered an error while " \
32
+ "converting notion page '#{page.title}':"
33
+ Jekyll.logger.error("", e.to_s)
34
+ raise e
35
+ end
36
+ end
37
+
38
+ def converters
39
+ @converters ||= @site.converters.select { |c| c.matches(".md") }.tap(&:sort!)
40
+ end
41
+
42
+ def log_data(data)
43
+ if data.is_a?(Array)
44
+ data.each { |page| _log_data(page, Array.to_s) }
45
+ else
46
+ _log_data(data, Hash.to_s)
47
+ end
48
+ end
49
+
50
+ def _log_data(page, type)
51
+ Jekyll.logger.info("Jekyll Notion:", "Page => #{page["title"]}")
52
+ Jekyll.logger.info("", "#{type} => site.data.#{config["data"]}")
53
+ Jekyll.logger.debug("", "Props => #{page.keys.inspect}")
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JekyllNotion
4
+ module Generators
5
+ # Abstract base class for Notion content generators.
6
+ #
7
+ # This class provides a common interface and factory method for creating
8
+ # Jekyll content from Notion pages. Subclasses must implement the {#call}
9
+ # method to define their specific generation behavior.
10
+ #
11
+ # @abstract Subclass and override {#call} to implement content generation logic
12
+ class Generator
13
+ class << self
14
+ # Factory method to create and execute a generator instance.
15
+ #
16
+ # @param config [Hash] Configuration hash for the generator
17
+ # @param site [Jekyll::Site] The Jekyll site instance
18
+ # @param notion_pages [Array<NotionToMd::Page>] Array of Notion pages to process
19
+ # @return [void]
20
+ def call(config:, site:, notion_pages:)
21
+ new(:config => config, :site => site,
22
+ :notion_pages => notion_pages).call
23
+ end
24
+ end
25
+
26
+ # Initialize a new generator instance.
27
+ #
28
+ # @param config [Hash] Configuration hash for the generator
29
+ # @param site [Jekyll::Site] The Jekyll site instance
30
+ # @param notion_pages [Array<NotionToMd::Page>] Array of Notion pages to process
31
+ def initialize(config:, site:, notion_pages:)
32
+ @notion_pages = notion_pages
33
+ @config = config
34
+ @site = site
35
+ end
36
+
37
+ attr_reader :config, :notion_pages, :site
38
+
39
+ # Generate Jekyll content from Notion pages.
40
+ #
41
+ # @abstract Subclasses must implement this method to define their
42
+ # specific content generation logic.
43
+ # @raise [NotImplementedError] if called on the abstract base class
44
+ # @return [void]
45
+ def call
46
+ raise NotImplementedError, "Subclasses must implement #call"
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JekyllNotion
4
+ module Generators
5
+ class Page < Generator
6
+ include Collectionable
7
+
8
+ def call
9
+ if config["data"].nil?
10
+ notion_pages.each { |notion_page| generate_page(notion_page) }
11
+ else
12
+ Data.call(:config => config, :site => site,
13
+ :notion_pages => notion_pages)
14
+ end
15
+ end
16
+
17
+ private
18
+
19
+ def generate_page(notion_page)
20
+ return if page_exists?(site.pages, notion_page)
21
+
22
+ page = make_page(notion_page)
23
+
24
+ site.pages << page
25
+
26
+ log_page(page)
27
+ end
28
+
29
+ def make_page(notion_page)
30
+ JekyllNotion::PageWithoutAFile.new(@site, @site.source, "", "#{notion_page.title}.md",
31
+ notion_page.to_md)
32
+ end
33
+ end
34
+ end
35
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module JekyllNotion
4
- VERSION = "2.4.3"
4
+ VERSION = "3.0.0.beta1"
5
5
  end
data/lib/jekyll-notion.rb CHANGED
@@ -10,20 +10,23 @@ require "vcr"
10
10
  NotionToMd::Logger.level = Logger::ERROR
11
11
 
12
12
  Notion.configure do |config|
13
- config.token = ENV["NOTION_TOKEN"]
13
+ config.token = ENV.fetch("NOTION_TOKEN", nil)
14
14
  end
15
15
 
16
16
  module JekyllNotion
17
- autoload :DatabaseFactory, "jekyll-notion/factories/database_factory"
18
- autoload :PageFactory, "jekyll-notion/factories/page_factory"
19
- autoload :AbstractGenerator, "jekyll-notion/generators/abstract_generator"
20
- autoload :DataGenerator, "jekyll-notion/generators/data_generator"
21
- autoload :PageGenerator, "jekyll-notion/generators/page_generator"
22
- autoload :CollectionGenerator, "jekyll-notion/generators/collection_generator"
23
17
  autoload :DocumentWithoutAFile, "jekyll-notion/document_without_a_file"
24
18
  autoload :PageWithoutAFile, "jekyll-notion/page_without_a_file"
25
- autoload :AbstractNotionResource, "jekyll-notion/abstract_notion_resource"
26
- autoload :NotionDatabase, "jekyll-notion/notion_database"
27
- autoload :NotionPage, "jekyll-notion/notion_page"
28
19
  autoload :Cacheable, "jekyll-notion/cacheable"
20
+ autoload :CassetteManager, "jekyll-notion/cassette_manager"
21
+
22
+ module Generators
23
+ autoload :Generator, "jekyll-notion/generators/generator"
24
+ autoload :Collectionable, "jekyll-notion/generators/collectionable"
25
+ autoload :Data, "jekyll-notion/generators/data"
26
+ autoload :Page, "jekyll-notion/generators/page"
27
+ autoload :Collection, "jekyll-notion/generators/collection"
28
+ end
29
29
  end
30
+
31
+ # Prepend Cacheable module to NotionToMd::Page for instance method caching
32
+ NotionToMd::Page.prepend(JekyllNotion::Cacheable)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jekyll-notion
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.4.3
4
+ version: 3.0.0.beta1
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-12-23 00:00:00.000000000 Z
11
+ date: 2025-09-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: jekyll
@@ -48,86 +48,44 @@ dependencies:
48
48
  name: notion_to_md
49
49
  requirement: !ruby/object:Gem::Requirement
50
50
  requirements:
51
- - - "~>"
51
+ - - '='
52
52
  - !ruby/object:Gem::Version
53
- version: 2.4.0
53
+ version: 3.0.0.beta2
54
54
  type: :runtime
55
55
  prerelease: false
56
56
  version_requirements: !ruby/object:Gem::Requirement
57
57
  requirements:
58
- - - "~>"
58
+ - - '='
59
59
  - !ruby/object:Gem::Version
60
- version: 2.4.0
60
+ version: 3.0.0.beta2
61
61
  - !ruby/object:Gem::Dependency
62
62
  name: vcr
63
63
  requirement: !ruby/object:Gem::Requirement
64
64
  requirements:
65
65
  - - "~>"
66
66
  - !ruby/object:Gem::Version
67
- version: 6.2.0
67
+ version: 6.3.1
68
68
  type: :runtime
69
69
  prerelease: false
70
70
  version_requirements: !ruby/object:Gem::Requirement
71
71
  requirements:
72
72
  - - "~>"
73
73
  - !ruby/object:Gem::Version
74
- version: 6.2.0
75
- - !ruby/object:Gem::Dependency
76
- name: bundler
77
- requirement: !ruby/object:Gem::Requirement
78
- requirements:
79
- - - "~>"
80
- - !ruby/object:Gem::Version
81
- version: '2'
82
- type: :development
83
- prerelease: false
84
- version_requirements: !ruby/object:Gem::Requirement
85
- requirements:
86
- - - "~>"
87
- - !ruby/object:Gem::Version
88
- version: '2'
89
- - !ruby/object:Gem::Dependency
90
- name: rspec
91
- requirement: !ruby/object:Gem::Requirement
92
- requirements:
93
- - - "~>"
94
- - !ruby/object:Gem::Version
95
- version: '3.0'
96
- type: :development
97
- prerelease: false
98
- version_requirements: !ruby/object:Gem::Requirement
99
- requirements:
100
- - - "~>"
101
- - !ruby/object:Gem::Version
102
- version: '3.0'
74
+ version: 6.3.1
103
75
  - !ruby/object:Gem::Dependency
104
- name: rubocop-jekyll
76
+ name: zeitwerk
105
77
  requirement: !ruby/object:Gem::Requirement
106
78
  requirements:
107
79
  - - "~>"
108
80
  - !ruby/object:Gem::Version
109
- version: '0.12'
110
- type: :development
111
- prerelease: false
112
- version_requirements: !ruby/object:Gem::Requirement
113
- requirements:
114
- - - "~>"
115
- - !ruby/object:Gem::Version
116
- version: '0.12'
117
- - !ruby/object:Gem::Dependency
118
- name: simplecov
119
- requirement: !ruby/object:Gem::Requirement
120
- requirements:
121
- - - "~>"
122
- - !ruby/object:Gem::Version
123
- version: '0.21'
124
- type: :development
81
+ version: '2.6'
82
+ type: :runtime
125
83
  prerelease: false
126
84
  version_requirements: !ruby/object:Gem::Requirement
127
85
  requirements:
128
86
  - - "~>"
129
87
  - !ruby/object:Gem::Version
130
- version: '0.21'
88
+ version: '2.6'
131
89
  description:
132
90
  email:
133
91
  - emoriarty81@gmail.com
@@ -138,18 +96,15 @@ extra_rdoc_files:
138
96
  files:
139
97
  - README.md
140
98
  - lib/jekyll-notion.rb
141
- - lib/jekyll-notion/abstract_notion_resource.rb
142
99
  - lib/jekyll-notion/cacheable.rb
100
+ - lib/jekyll-notion/cassette_manager.rb
143
101
  - lib/jekyll-notion/document_without_a_file.rb
144
- - lib/jekyll-notion/factories/database_factory.rb
145
- - lib/jekyll-notion/factories/page_factory.rb
146
102
  - lib/jekyll-notion/generator.rb
147
- - lib/jekyll-notion/generators/abstract_generator.rb
148
- - lib/jekyll-notion/generators/collection_generator.rb
149
- - lib/jekyll-notion/generators/data_generator.rb
150
- - lib/jekyll-notion/generators/page_generator.rb
151
- - lib/jekyll-notion/notion_database.rb
152
- - lib/jekyll-notion/notion_page.rb
103
+ - lib/jekyll-notion/generators/collection.rb
104
+ - lib/jekyll-notion/generators/collectionable.rb
105
+ - lib/jekyll-notion/generators/data.rb
106
+ - lib/jekyll-notion/generators/generator.rb
107
+ - lib/jekyll-notion/generators/page.rb
153
108
  - lib/jekyll-notion/page_without_a_file.rb
154
109
  - lib/jekyll-notion/version.rb
155
110
  homepage: https://github.com/emoriarty/jekyll-notion
@@ -164,12 +119,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
164
119
  requirements:
165
120
  - - ">="
166
121
  - !ruby/object:Gem::Version
167
- version: 2.5.0
122
+ version: 2.7.0
168
123
  required_rubygems_version: !ruby/object:Gem::Requirement
169
124
  requirements:
170
- - - ">="
125
+ - - ">"
171
126
  - !ruby/object:Gem::Version
172
- version: '0'
127
+ version: 1.3.1
173
128
  requirements: []
174
129
  rubygems_version: 3.1.6
175
130
  signing_key:
@@ -1,47 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module JekyllNotion
4
- class AbstractNotionResource
5
- def initialize(config:)
6
- @notion = Notion::Client.new
7
- @config = config
8
- end
9
-
10
- def config
11
- @config || {}
12
- end
13
-
14
- def id
15
- config["id"]
16
- end
17
-
18
- def fetch
19
- raise "Do not use the AbstractNotionResource class. Implement the fetch method in a subclass."
20
- end
21
-
22
- def collection_name
23
- raise "Do not use the AbstractGenerator class. Implement the collection_name method in a subclass."
24
- end
25
-
26
- def data_name
27
- raise "Do not use the AbstractGenerator class. Implement the data_name method in a subclass."
28
- end
29
-
30
- protected
31
-
32
- def id?
33
- if id.nil? || id.empty?
34
- Jekyll.logger.warn("Jekyll Notion:",
35
- "Database or page id is not provided. Cannot read from Notion.")
36
- return false
37
- end
38
- true
39
- end
40
-
41
- def build_blocks(block_id)
42
- NotionToMd::Blocks.build(:block_id => block_id) do |nested_id|
43
- @notion.block_children({ :block_id => nested_id })
44
- end
45
- end
46
- end
47
- end
@@ -1,14 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module JekyllNotion
4
- class DatabaseFactory
5
- def self.for(notion_resource:, site:, plugin:)
6
- if notion_resource.data_name.nil?
7
- CollectionGenerator.new(:notion_resource => notion_resource, :site => site,
8
- :plugin => plugin)
9
- else
10
- DataGenerator.new(:notion_resource => notion_resource, :site => site, :plugin => plugin)
11
- end
12
- end
13
- end
14
- end