diff --git a/README.md b/README.md index 1b5f2cc..5728e2e 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,7 @@ You can configure this plugin in `_config.yml` under the `relative_links` key. T relative_links: enabled: true collections: false + validate_links: false ``` ### Excluding files @@ -104,3 +105,14 @@ File | Link ### Disabling Even if the plugin is enabled (e.g., via the `:jekyll_plugins` group in your Gemfile) you can disable it by setting the `enabled` key to `false`. + +### Validating Links + +By default, the plugin doesn't validate if linked files exist. It will silently keep the original Markdown links when the target file doesn't exist. + +Setting the `validate_links` option to `true` enables link validation, causing Jekyll to raise an error when attempting to link to a nonexistent file, similar to Jekyll's native `{% link %}` tag. + +```yml +relative_links: + validate_links: true +``` diff --git a/lib/jekyll-relative-links/generator.rb b/lib/jekyll-relative-links/generator.rb index 8f9d643..cab3e5a 100644 --- a/lib/jekyll-relative-links/generator.rb +++ b/lib/jekyll-relative-links/generator.rb @@ -18,6 +18,7 @@ class Generator < Jekyll::Generator CONFIG_KEY = "relative_links" ENABLED_KEY = "enabled" COLLECTIONS_KEY = "collections" + VALIDATE_LINKS_KEY = "validate_links" LOG_KEY = "Relative Links:" safe true @@ -45,6 +46,7 @@ def generate(site) end end + # rubocop:disable Metrics/AbcSize def replace_relative_links!(document) url_base = File.dirname(document.relative_path) return document if document.content.nil? @@ -55,6 +57,12 @@ def replace_relative_links!(document) path = path_from_root(link.path, url_base) url = url_for_path(path) + + if url.nil? && validate_links? + source = document.relative_path + raise "Invalid reference to '#{link.path}' in '#{source}'" + end + next original unless url link.path = url @@ -65,6 +73,7 @@ def replace_relative_links!(document) rescue ArgumentError => e raise e unless e.to_s.start_with?("invalid byte sequence in UTF-8") end + # rubocop:enable Metrics/AbcSize private @@ -151,6 +160,10 @@ def collections? option(COLLECTIONS_KEY) == true end + def validate_links? + option(VALIDATE_LINKS_KEY) == true + end + def excluded?(document) return false unless option("exclude") diff --git a/spec/jekyll-relative-links/generator_spec.rb b/spec/jekyll-relative-links/generator_spec.rb index c30f539..cebe4e7 100644 --- a/spec/jekyll-relative-links/generator_spec.rb +++ b/spec/jekyll-relative-links/generator_spec.rb @@ -340,6 +340,43 @@ end end + context "link validation" do + it "knows when validation is enabled" do + plugin_config = { "validate_links" => true } + generator = described_class.new({ "relative_links" => plugin_config }) + expect(generator.send(:validate_links?)).to be true + end + + it "knows when validation is disabled" do + plugin_config = { "validate_links" => false } + generator = described_class.new({ "relative_links" => plugin_config }) + expect(generator.send(:validate_links?)).to be false + end + + it "has validation disabled by default" do + generator = described_class.new({}) + expect(generator.send(:validate_links?)).to be false + end + + context "when validation is enabled" do + let(:plugin_config) { { "validate_links" => true } } + + before do + site.reset + site.read + subject.instance_variable_set :@site, site + end + + it "raises an error when a file doesn't exist" do + page.content = "[Missing](./nonexistent.md)" + + expect do + subject.replace_relative_links!(page) + end.to raise_error(%r!Invalid reference to './nonexistent.md' in 'page.md'!) + end + end + end + context "a page without content" do before { page_by_path(site, "page.md").content = nil }