Merge pull request #5364 from jekyll/themes-asset-folder

Merge pull request 5364
This commit is contained in:
jekyllbot 2016-09-20 13:40:11 -07:00 committed by GitHub
commit 3a45bf7d26
20 changed files with 163 additions and 14 deletions

View File

@ -16,7 +16,7 @@ Lint/UnreachableCode:
Lint/UselessAccessModifier:
Enabled: false
Metrics/AbcSize:
Max: 20
Max: 21
Metrics/ClassLength:
Exclude:
- !ruby/regexp /features\/.*.rb$/

View File

@ -17,7 +17,7 @@ Feature: Writing themes
Scenario: A theme with SCSS
Given I have a configuration file with "theme" set to "test-theme"
And I have a css directory
And I have a "css/main.scss" page that contains "@import 'style';"
And I have a "css/main.scss" page that contains "@import 'test-theme-black';"
When I run jekyll build
Then I should get a zero exit status
And the _site directory should exist

View File

@ -55,6 +55,7 @@ module Jekyll
autoload :PostReader, "jekyll/readers/post_reader"
autoload :PageReader, "jekyll/readers/page_reader"
autoload :StaticFileReader, "jekyll/readers/static_file_reader"
autoload :ThemeAssetsReader, "jekyll/readers/theme_assets_reader"
autoload :LogAdapter, "jekyll/log_adapter"
autoload :Page, "jekyll/page"
autoload :PluginManager, "jekyll/plugin_manager"

View File

@ -40,7 +40,11 @@ module Jekyll
@base = base
@dir = dir
@name = name
@path = site.in_source_dir(base, dir, name)
@path = if site.in_theme_dir(base) == base # we're in a theme
site.in_theme_dir(base, dir, name)
else
site.in_source_dir(base, dir, name)
end
process(name)
read_yaml(File.join(base, dir), name)

View File

@ -18,6 +18,7 @@ module Jekyll
sort_files!
@site.data = DataReader.new(site).read(site.config["data_dir"])
CollectionReader.new(site).read
ThemeAssetsReader.new(site).read
end
# Sorts posts, pages, and static files.

View File

@ -0,0 +1,47 @@
module Jekyll
class ThemeAssetsReader
attr_reader :site
def initialize(site)
@site = site
end
def read
return unless site.theme && site.theme.assets_path
Find.find(site.theme.assets_path) do |path|
next if File.directory?(path)
if File.symlink?(path)
Jekyll.logger.warn "Theme reader:", "Ignored symlinked asset: #{path}"
else
read_theme_asset(path)
end
end
end
private
def read_theme_asset(path)
base = site.theme.root
dir = File.dirname(path.sub("#{site.theme.root}/", ""))
name = File.basename(path)
if Utils.has_yaml_header?(path)
append_unless_exists site.pages,
Jekyll::Page.new(site, base, dir, name)
else
append_unless_exists site.static_files,
Jekyll::StaticFile.new(site, base, dir, name)
end
end
def append_unless_exists(haystack, new_item)
if haystack.any? { |file| file.relative_path == new_item.relative_path }
Jekyll.logger.debug "Theme:",
"Ignoring #{new_item.relative_path} in theme due to existing file " \
"with that path in site."
return
end
haystack << new_item
end
end
end

View File

@ -18,15 +18,19 @@ module Jekyll
end
def includes_path
path_for :includes
path_for "_includes".freeze
end
def layouts_path
path_for :layouts
path_for "_layouts".freeze
end
def sass_path
path_for :sass
path_for "_sass".freeze
end
def assets_path
path_for "assets".freeze
end
def configure_sass
@ -43,7 +47,7 @@ module Jekyll
end
def realpath_for(folder)
File.realpath(Jekyll.sanitized_path(root, "_#{folder}"))
File.realpath(Jekyll.sanitized_path(root, folder.to_s))
rescue Errno::ENOENT, Errno::EACCES, Errno::ELOOP
nil
end

View File

@ -1,6 +1,6 @@
class Jekyll::ThemeBuilder
SCAFFOLD_DIRECTORIES = %w(
_layouts _includes _sass
assets _layouts _includes _sass
).freeze
attr_reader :name, :path, :code_of_conduct

View File

@ -27,6 +27,7 @@ Jekyll themes set default layouts, includes, and stylesheets, that can be overri
Jekyll will look first to your site's content, before looking to the theme's defaults, for any requested file in the following folders:
* `/assets`
* `/_layouts`
* `/_includes`
* `/_sass`
@ -68,6 +69,12 @@ Theme layouts and includes work just like they work in any Jekyll site. Place la
For example, if your theme has a `/_layouts/page.html` file, and a page has `layout: page` in its YAML front matter, Jekyll will first look to the site's `_layouts` folder for a the `page` layout, and if none exists, will use your theme's `page` layout.
### Assets
Any file in `/assets` will be copied over to the user's site upon build unless they have a file with the same relative path. You may ship any kind of asset here: SCSS, an image, a webfont, etc. These files behave just like pages and static files in Jekyll: if the file has [YAML front matter]({{ site.baseurl }}/docs/frontmatter/) at the top, then it will be rendered. If it does not have YAML front matter, it will simply be copied over into the resulting site. This allows theme creators to ship a default `/assets/styles.scss` file which their layouts can depend on as `/assets/styles.css`.
All files in `/assets` will be output into the compiled site in the `/assets` folder just as you'd expect from using Jekyll on your sites.
### Stylesheets
Your theme's stylesheets should be placed in your theme's `/_sass` folder, again, just as you would when authoring a Jekyll site. Your theme's styles can be included in the user's stylesheet using the `@import` directive.

View File

@ -0,0 +1,3 @@
.sample {
color: red;
}

View File

@ -0,0 +1,3 @@
---
---
alert "From your theme."

View File

@ -0,0 +1 @@
logo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

@ -0,0 +1,3 @@
---
---
@import "test-theme-{{ site.theme-color | default: "red" }}";

View File

@ -0,0 +1,3 @@
---
---
alert "From your site."

View File

@ -475,7 +475,7 @@ class TestFilters < JekyllUnitTest
g["items"].is_a?(Array),
"The list of grouped items for '' is not an Array."
)
assert_equal 14, g["items"].size
assert_equal 15, g["items"].size
end
end
end

View File

@ -180,6 +180,7 @@ class TestSite < JekyllUnitTest
%#\ +.md
.htaccess
about.html
application.coffee
bar.html
coffeescript.coffee
contacts.html

View File

@ -34,16 +34,16 @@ class TestTheme < JekyllUnitTest
end
context "path generation" do
[:layouts, :includes, :sass].each do |folder|
[:assets, :_layouts, :_includes, :_sass].each do |folder|
should "know the #{folder} path" do
expected = File.expand_path("_#{folder}", @expected_root)
assert_equal expected, @theme.public_send("#{folder}_path")
expected = File.expand_path(folder.to_s, @expected_root)
assert_equal expected, @theme.public_send("#{folder.to_s.tr("_", "")}_path")
end
end
should "generate folder paths" do
expected = File.expand_path("./_sass", @expected_root)
assert_equal expected, @theme.send(:path_for, :sass)
assert_equal expected, @theme.send(:path_for, :_sass)
end
should "not allow paths outside of the theme root" do
@ -56,7 +56,7 @@ class TestTheme < JekyllUnitTest
should "return the resolved path when a symlink & resolved path exists" do
expected = File.expand_path("./_layouts", @expected_root)
assert_equal expected, @theme.send(:path_for, :symlink)
assert_equal expected, @theme.send(:path_for, :_symlink)
end
end

View File

@ -0,0 +1,71 @@
require "helper"
class TestThemeAssetsReader < JekyllUnitTest
def setup
@site = fixture_site(
"theme" => "test-theme",
"theme-color" => "black"
)
assert @site.theme
end
def assert_file_with_relative_path(haystack, relative_path)
assert haystack.any? { |f|
f.relative_path == relative_path
}, "Site should read in the #{relative_path} file, " \
"but it was not found in #{haystack.inspect}"
end
def refute_file_with_relative_path(haystack, relative_path)
refute haystack.any? { |f|
f.relative_path == relative_path
}, "Site should not have read in the #{relative_path} file, " \
"but it was found in #{haystack.inspect}"
end
context "with a valid theme" do
should "read all assets" do
@site.reset
ThemeAssetsReader.new(@site).read
assert_file_with_relative_path @site.static_files, "assets/img/logo.png"
assert_file_with_relative_path @site.pages, "assets/style.scss"
end
should "convert pages" do
@site.process
file = @site.pages.find { |f| f.relative_path == "assets/style.scss" }
refute_nil file
assert_equal @site.in_dest_dir("assets/style.css"), file.destination(@site.dest)
assert_includes file.output, ".sample {\n color: black; }"
end
should "not overwrite site content with the same relative path" do
@site.reset
@site.read
file = @site.pages.find { |f| f.relative_path == "assets/application.coffee" }
refute_nil file
assert_includes file.content, "alert \"From your site.\""
end
end
context "with a valid theme without an assets dir" do
should "not read any assets" do
site = fixture_site("theme" => "test-theme")
allow(site.theme).to receive(:assets_path).and_return(nil)
ThemeAssetsReader.new(site).read
refute_file_with_relative_path site.static_files, "assets/img/logo.png"
refute_file_with_relative_path site.pages, "assets/style.scss"
end
end
context "with no theme" do
should "not read any assets" do
site = fixture_site("theme" => nil)
ThemeAssetsReader.new(site).read
refute_file_with_relative_path site.static_files, "assets/img/logo.png"
refute_file_with_relative_path site.pages, "assets/style.scss"
end
end
end