From d67cbb4e5fe197e9aa9de17f6f7f6a60ade41811 Mon Sep 17 00:00:00 2001 From: Ashwin Maroli Date: Sun, 28 Oct 2018 15:38:32 +0530 Subject: [PATCH] Load config file from within current theme-gem (#7304) Merge pull request 7304 --- docs/_docs/themes.md | 18 ++++++++++++++++++ features/theme_configuration.feature | 25 +++++++++++++++++++++++++ lib/jekyll/site.rb | 21 +++++++++++++++++++++ test/fixtures/test-theme/_config.yml | 14 ++++++++++++++ test/test_site.rb | 6 +++++- 5 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 features/theme_configuration.feature create mode 100644 test/fixtures/test-theme/_config.yml diff --git a/docs/_docs/themes.md b/docs/_docs/themes.md index 1730fd75..174579a8 100644 --- a/docs/_docs/themes.md +++ b/docs/_docs/themes.md @@ -274,6 +274,24 @@ Jekyll will automatically require all whitelisted `runtime_dependencies` of your With this, the end-user need not keep track of the plugins required to be included in their config file for their theme-gem to work as intended. +{% if site.version == '4.0.0' %} +{% comment %} Remove this encapsulation when `v4.0` ships {% endcomment %} + +### Pre-configuring Theme-gems {%- include docs_version_badge.html version="4.0.0" -%} + +Jekyll will read-in a `_config.yml` at the root of the theme-gem and merge its data into the site's existing configuration data. + +But unlike other entities loaded from within the theme, loading the config file comes with a few restrictions, as summarized below: + * Jekyll's default settings cannot be overridden by a theme-config. That *ball is still in the user's court.* + * The theme-config-file cannot be a symlink, irrespective of `safe mode` and whether the file pointed to by the symlink is a legitimate file within the theme-gem. + * The theme-config should be a set of key-value pairs. An empty config file, a config file that simply *lists items* under a key, or a config file with just a simple string of text will simply be ignored silently. Users will not get a warning or any log output regarding this discrepancy. + * Any settings defined by the theme-config can be overridden by the user. + +While this feature is to enable easier adoption of a theme, the restrictions ensure that a theme-config cannot affect the build in a concerning manner. Any plugins required by the theme will have to be listed manually by the user or provided by the theme's `gemspec` file. + +This feature will let the theme-gem to work with *theme-specific config variables* out-of-the-box. +{% endif %} + ### Documenting your theme Your theme should include a `/README.md` file, which explains how site authors can install and use your theme. What layouts are included? What includes? Do they need to add anything special to their site's configuration file? diff --git a/features/theme_configuration.feature b/features/theme_configuration.feature new file mode 100644 index 00000000..6ddfd4ae --- /dev/null +++ b/features/theme_configuration.feature @@ -0,0 +1,25 @@ +Feature: Bundling Config file with Theme gems + As a web developer who likes to share my expertise + I want to be able to pre-configure my gemified theme + In order to make it easier for other Jekyllites to use my theme + + Scenario: Easy onboarding with a pre-configured theme + Given I have a configuration file with "theme" set to "test-theme" + And I have an "index.md" page that contains "{{ site.test_theme.skin }}" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And I should see "aero" in "_site/index.html" + + Scenario: A pre-configured theme with valid config file overriding Jekyll defaults + Given I have a configuration file with "theme" set to "test-theme" + And I have an "index.md" page that contains "{{ site.baseurl }}" + And I have a node_modules directory + And I have a "node_modules/alert.js" file that contains "alert('foo');" + When I run jekyll build + Then I should get a zero exit status + And the _site directory should exist + And the "_site/index.html" file should exist + But the "_site/node_modules/alert.js" file should not exist + And the "_site/extras/banner.html" file should not exist + And I should not see "/test-theme" in "_site/index.html" diff --git a/lib/jekyll/site.rb b/lib/jekyll/site.rb index 57f6545e..32fb5eb3 100644 --- a/lib/jekyll/site.rb +++ b/lib/jekyll/site.rb @@ -59,6 +59,8 @@ module Jekyll self.permalink_style = config["permalink"].to_sym + # Read in a _config.yml from the current theme-gem at the very end. + @config = load_theme_configuration(config) if theme @config end @@ -409,6 +411,25 @@ module Jekyll private + def load_theme_configuration(config) + theme_config_file = in_theme_dir("_config.yml") + return config unless File.exist?(theme_config_file) + + # Bail out if the theme_config_file is a symlink file irrespective of safe mode + return config if File.symlink?(theme_config_file) + + theme_config = SafeYAML.load_file(theme_config_file) + return config unless theme_config.is_a?(Hash) + + Jekyll.logger.info "Theme Config file:", theme_config_file + + # theme_config should not be overriding Jekyll's defaults + theme_config.delete_if { |key, _| Configuration::DEFAULTS.key?(key) } + + # Override theme_config with existing config and return the result. + Utils.deep_merge_hashes(theme_config, config) + end + # Limits the current posts; removes the posts which exceed the limit_posts # # Returns nothing diff --git a/test/fixtures/test-theme/_config.yml b/test/fixtures/test-theme/_config.yml new file mode 100644 index 00000000..27c61828 --- /dev/null +++ b/test/fixtures/test-theme/_config.yml @@ -0,0 +1,14 @@ +title: Hello World +baseurl: "/test-theme" +include: ["_extras/banner.md"] +exclude: + - README.md + - CHANGELOG.md + - Rakefile + - test/**/* + +# theme-specific settings +test_theme: + skin: aero # aero / chrome / dark / neon + date_format: "%b -d %Y" # any format supported by strftime + header_links: true # generate header links automatically diff --git a/test/test_site.rb b/test/test_site.rb index 03e624ec..95955f88 100644 --- a/test/test_site.rb +++ b/test/test_site.rb @@ -588,7 +588,11 @@ class TestSite < JekyllUnitTest should "set a theme if the config is a string" do [:debug, :info, :warn, :error].each do |level| - expect(Jekyll.logger.writer).not_to receive(level) + if level == :info + expect(Jekyll.logger.writer).to receive(level) + else + expect(Jekyll.logger.writer).not_to receive(level) + end end site = fixture_site("theme" => "test-theme") assert_instance_of Jekyll::Theme, site.theme