I am Outer
I am Inner
The Article Content
"
+
Scenario: A page uses an inner layout when uses an outer layout (haml)
Given the Server is running at "nested-layout-app"
When I go to "/haml-test.html"
Then I should see:
"""
- Master
+ Master Haml
New Article Title
- Outer
- Inner
+ I am Outer
+ I am Inner
The Article Content
"""
@@ -43,13 +43,16 @@ Feature: Allow nesting of layouts
Given the Server is running at "nested-layout-app"
When I go to "/data-one.html"
Then I should see "Page Number One"
- And I should see "Inner"
+ And I should see "Page #1"
+ And I should see "I am Inner"
+ And I should see "I am Outer"
+ And I should see "Master Erb"
When I go to "/data-two.html"
Then I should see "Page Number Two"
- And I should not see "Inner"
+ And I should not see "I am Inner"
When I go to "/data-one.html"
Then I should see "Page Number One"
- And I should see "Inner"
+ And I should see "I am Inner"
When I go to "/data-two.html"
Then I should see "Page Number Two"
- And I should not see "Inner"
+ And I should not see "I am Inner"
diff --git a/middleman-core/features/page-id.feature b/middleman-core/features/page-id.feature
new file mode 100644
index 00000000..74d08cb5
--- /dev/null
+++ b/middleman-core/features/page-id.feature
@@ -0,0 +1,72 @@
+Feature: Page IDs
+
+ Scenario: link_to works with blocks (erb)
+ Given the Server is running at "page-id-app"
+ When I go to "/index.html"
+ Then I should see "I am: index"
+ And I should see "URL1: /fm.html"
+ And I should see "URL2: /2.html"
+ And I should see 'URL3:
Hi'
+ And I should see 'URL4:
Sym'
+ And I should see 'URL5:
Imp'
+ And I should see 'URL6:
Foldern'
+ And I should see 'URL7:
Feed'
+
+ When I go to "/fm.html"
+ Then I should see "I am: frontmatter"
+ When I go to "/implicit.html"
+ Then I should see "I am: implicit"
+ When I go to "/feed.xml"
+ Then I should see "I am: feed.xml"
+ When I go to "/folder/foldern.html"
+ Then I should see "I am: folder/foldern"
+
+ When I go to "/1.html"
+ Then I should see "I am: page1"
+ When I go to "/2.html"
+ Then I should see "I am: page2"
+ When I go to "/3.html"
+ Then I should see "I am: page3"
+
+ When I go to "/overwrites/from-default.html"
+ Then I should see "I am: something-else"
+
+ When I go to "/overwrites/from-frontmatter.html"
+ Then I should see "I am: from_frontmatter"
+
+ Scenario: Override page ID derivation with a proc
+ Given a fixture app "page-id-app"
+ And app "page-id-app" is using config "proc"
+ And the Server is running at "page-id-app"
+
+ When I go to "/index.html"
+ Then I should see "I am: index.html-foo"
+ And I should see "URL1: /fm.html"
+ And I should see "URL2: /2.html"
+ And I should see 'URL3:
Hi'
+ And I should see 'URL4:
Sym'
+ And I should see 'URL8:
Imp'
+ And I should see 'URL9:
Foldern'
+ And I should see 'URL10:
Feed'
+
+ When I go to "/fm.html"
+ Then I should see "I am: frontmatter"
+ When I go to "/implicit.html"
+ Then I should see "I am: implicit.html-foo"
+ When I go to "/feed.xml"
+ Then I should see "I am: feed.xml-foo"
+ When I go to "/folder/foldern.html"
+ Then I should see "I am: folder/foldern.html-foo"
+
+ When I go to "/1.html"
+ Then I should see "I am: page1"
+ When I go to "/2.html"
+ Then I should see "I am: page2"
+ When I go to "/3.html"
+ Then I should see "I am: page3"
+
+ When I go to "/overwrites/from-default.html"
+ Then I should see "I am: something-else"
+
+ When I go to "/overwrites/from-frontmatter.html"
+ Then I should see "I am: from_frontmatter"
diff --git a/middleman-core/features/partials.feature b/middleman-core/features/partials.feature
index 86509027..8a0154c2 100644
--- a/middleman-core/features/partials.feature
+++ b/middleman-core/features/partials.feature
@@ -5,25 +5,30 @@ Feature: Provide Sane Defaults for Partial Behavior
When I go to "/index.html"
Then I should see "Header"
And I should see "Footer"
-
+
Scenario: Finds shared partials relative to the root (sub)
Given the Server is running at "partials-app"
When I go to "/sub/index.html"
Then I should see "Header"
And I should see "Footer"
-
+
+ Scenario: Flags error when partial is not found
+ Given the Server is running at "partials-app"
+ When I go to "/index_missing.html"
+ Then I should see "Error: Could not locate partial"
+
Scenario: Prefers partials of the same engine type
Given the Server is running at "partials-app"
When I go to "/index.html"
Then I should see "ERb Main"
-
+
Scenario: Prefers partials of the same engine type
Given the Server is running at "partials-app"
When I go to "/second.html"
Then I should see "Str Main"
And I should see "Header"
And I should see "Footer"
-
+
Scenario: Finds partial relative to template
Given the Server is running at "partials-app"
When I go to "/sub/index.html"
@@ -33,7 +38,7 @@ Feature: Provide Sane Defaults for Partial Behavior
Given the Server is running at "partials-app"
When I go to "/locals.html"
Then I should see "Local var is bar"
-
+
Scenario: Partial and Layout use different engines
Given the Server is running at "different-engine-partial"
When I go to "/index.html"
@@ -50,3 +55,10 @@ Feature: Provide Sane Defaults for Partial Behavior
Then I should see "File Not Found"
When I go to "/_code_snippet.html"
Then I should see "File Not Found"
+
+Scenario: Works with blocks
+ Given the Server is running at "partials-app"
+ When I go to "/block.html"
+ Then I should see "Start"
+ And I should see "Contents"
+ And I should see "End"
diff --git a/middleman-core/features/relative_assets.feature b/middleman-core/features/relative_assets.feature
index ea6cbff0..ba131396 100644
--- a/middleman-core/features/relative_assets.feature
+++ b/middleman-core/features/relative_assets.feature
@@ -120,7 +120,7 @@ Feature: Relative Assets
"""
And the Server is running at "relative-assets-app"
When I go to "/sub/image_tag.html"
- Then I should see '

'
+ Then I should see '

")
+ h2
+ background: url("<%= asset_url('/images/blank2.gif') %>")
+ """
+ And a file named "source/javascripts/application.js.erb" with:
+ """
+ function foo() {
+ var img = document.createElement('img');
+ img.src = '<%= asset_url("images/100px.jpg") %>';
+ var body = document.getElementsByTagName('body')[0];
+ body.insertBefore(img, body.firstChild);
+ }
+
+ window.onload = foo;
+ """
+ And a file named "source/stylesheets/fonts3.css.erb" with:
+ """
+ @font-face {
+ font-family: 'Roboto2';
+ src: url(<%= asset_url("/fonts/roboto/roboto-regular-webfont.eot") %>);
+ src: url(<%= asset_url("/fonts/roboto/roboto-regular-webfont.eot?#iefix") %>) format('embedded-opentype'),
+ url(<%= asset_url("/fonts/roboto/roboto-regular-webfont.woff") %>) format('woff'),
+ url(<%= asset_url("/fonts/roboto/roboto-regular-webfont.ttf") %>) format('truetype'),
+ url(<%= asset_url("/fonts/roboto/roboto-regular-webfont.svg#robotoregular") %>) format('svg');
+ font-weight: normal;
+ font-style: normal;
+ }
+ """
+ And the Server is running at "relative-assets-app"
+ When I go to "/stylesheets/relative_assets.css"
+ Then I should see 'url("../images/blank.gif'
+ And I should see 'url("../images/blank2.gif'
+ When I go to "/javascripts/application.js"
+ Then I should not see "../"
+ When I go to "/stylesheets/fonts3.css"
+ Then I should see 'url(../fonts/roboto/roboto-regular-webfont.eot'
+ And I should see 'url(../fonts/roboto/roboto-regular-webfont.woff'
+ And I should see 'url(../fonts/roboto/roboto-regular-webfont.ttf'
+ And I should see 'url(../fonts/roboto/roboto-regular-webfont.svg'
+
+ Scenario: Relative css reference with directory indexes
+ Given a fixture app "relative-assets-app"
+ And a file named "config.rb" with:
+ """
+ activate :directory_indexes
+ activate :relative_assets, helpers_only: true
+ """
+ And the Server is running at "relative-assets-app"
+ When I go to "/relative_image/index.html"
+ Then I should see "../stylesheets/relative_assets.css"
+
+ Scenario: Relative assets via image_tag
+ Given a fixture app "relative-assets-app"
+ And a file named "config.rb" with:
+ """
+ activate :relative_assets, helpers_only: true
+ """
+ And a file named "source/sub/image_tag.html.erb" with:
+ """
+ <%= image_tag '/img/blank.gif' %>
+ """
+ And the Server is running at "relative-assets-app"
+ When I go to "/sub/image_tag.html"
+ Then I should see '

+ """
+ And the Server is running at "relative-assets-app"
+ When I go to "/sub/image_tag.html"
+ Then I should see '

'
+
+ Scenario: URLs are not rewritten for rewrite ignored paths
+ Given a fixture app "relative-assets-app"
+ And a file named "config.rb" with:
+ """
+ activate :relative_assets, rewrite_ignore: [
+ '/stylesheets/fonts3.css',
+ ], helpers_only: true
+ """
+ And a file named "source/stylesheets/relative_assets.css.sass.erb" with:
+ """
+ h1
+ background: url("<%= asset_url('images/blank.gif') %>")
+ h2
+ background: url("<%= asset_url('/images/blank2.gif') %>")
+ """
+ And a file named "source/stylesheets/fonts3.css.erb" with:
+ """
+ @font-face {
+ font-family: 'Roboto2';
+ src: url(<%= asset_url("/fonts/roboto/roboto-regular-webfont.eot") %>);
+ src: url(<%= asset_url("/fonts/roboto/roboto-regular-webfont.eot?#iefix") %>) format('embedded-opentype'),
+ url(<%= asset_url("/fonts/roboto/roboto-regular-webfont.woff") %>) format('woff'),
+ url(<%= asset_url("/fonts/roboto/roboto-regular-webfont.ttf") %>) format('truetype'),
+ url(<%= asset_url("/fonts/roboto/roboto-regular-webfont.svg#robotoregular") %>) format('svg');
+ font-weight: normal;
+ font-style: normal;
+ }
+ """
+ And the Server is running at "relative-assets-app"
+ When I go to "/stylesheets/relative_assets.css"
+ Then I should see 'url("../images/blank.gif'
+ When I go to "/stylesheets/fonts3.css"
+ Then I should see 'url(/fonts/roboto/roboto-regular-webfont.eot'
diff --git a/middleman-core/features/sass_in_slim.feature b/middleman-core/features/sass_in_slim.feature
new file mode 100644
index 00000000..d04055e4
--- /dev/null
+++ b/middleman-core/features/sass_in_slim.feature
@@ -0,0 +1,40 @@
+Feature: Sass/SCSS support in Slim
+ In order to test support of the Slim sass and scss filters
+
+ Scenario: Sass filter in Slim works
+ Given a fixture app "sass-in-slim-app"
+ And a file named "config.rb" with:
+ """
+ activate :directory_indexes
+ """
+ And a file named "source/sass_filter.html.slim" with:
+ """
+ sass:
+ .sass
+ margin: 0
+ """
+ Given the Server is running at "sass-in-slim-app"
+ When I go to "/sass_filter/"
+ Then I should see "text/css"
+ Then I should see ".sass"
+ Then I should see "margin:0"
+
+
+ Scenario: SCSS filter in Slim works
+ Given a fixture app "sass-in-slim-app"
+ And a file named "config.rb" with:
+ """
+ activate :directory_indexes
+ """
+ And a file named "source/scss_filter.html.slim" with:
+ """
+ scss:
+ .scss {
+ margin: 0;
+ }
+ """
+ Given the Server is running at "sass-in-slim-app"
+ When I go to "/scss_filter/"
+ Then I should see "text/css"
+ Then I should see ".scss"
+ Then I should see "margin:0"
diff --git a/middleman-core/features/template-key-collision.feature b/middleman-core/features/template-key-collision.feature
new file mode 100644
index 00000000..9e530dc1
--- /dev/null
+++ b/middleman-core/features/template-key-collision.feature
@@ -0,0 +1,26 @@
+Feature: Don't allow template locals to overwrite template helpers
+
+ Scenario: Normal Template
+ Given an empty app
+ And a file named "config.rb" with:
+ """
+ class TestExt < ::Middleman::Extension
+ expose_to_template foo: :foo
+
+ def foo
+ "bar"
+ end
+ end
+
+ ::Middleman::Extensions.register :test, TestExt
+
+ activate :test
+
+ page "/index.html", locals: { foo: false }
+ """
+ And a file named "source/index.html.erb" with:
+ """
+ <%= foo %>
+ """
+ Given a built app at "empty_app"
+ Then the exit status should be 1
diff --git a/middleman-core/fixtures/asset-hash-host-app/config.rb b/middleman-core/fixtures/asset-hash-host-app/config.rb
deleted file mode 100644
index 085caeb7..00000000
--- a/middleman-core/fixtures/asset-hash-host-app/config.rb
+++ /dev/null
@@ -1,4 +0,0 @@
-
-activate :asset_hash
-activate :directory_indexes
-activate :asset_host, host: 'http://middlemanapp.com'
diff --git a/middleman-core/fixtures/asset-hash-host-app/source/index.html.erb b/middleman-core/fixtures/asset-hash-host-app/source/index.html.erb
index ea81c790..c9fefc2a 100644
--- a/middleman-core/fixtures/asset-hash-host-app/source/index.html.erb
+++ b/middleman-core/fixtures/asset-hash-host-app/source/index.html.erb
@@ -3,7 +3,7 @@
<% end %>
Image url:
-<%= image_tag('100px.jpg') %>
+

<%= image_tag('100px.jpg?test') %>
<%= image_tag('100px.jpg?#test') %>
<%= image_tag('100px.jpg#test') %>
diff --git a/middleman-core/fixtures/asset-hash-minified-app/source/images/100px.jpg b/middleman-core/fixtures/asset-hash-minified-app/source/images/100px.jpg
new file mode 100644
index 00000000..557d0278
Binary files /dev/null and b/middleman-core/fixtures/asset-hash-minified-app/source/images/100px.jpg differ
diff --git a/middleman-core/fixtures/asset-hash-minified-app/source/stylesheets/test.css b/middleman-core/fixtures/asset-hash-minified-app/source/stylesheets/test.css
new file mode 100644
index 00000000..059fce9a
--- /dev/null
+++ b/middleman-core/fixtures/asset-hash-minified-app/source/stylesheets/test.css
@@ -0,0 +1,3 @@
+.no-bug{background-image:url(/images/100px.jpg)}
+.bug{content:"";background-image:url(/images/100px.jpg)}
+.no-bug{content:""; background-image:url(/images/100px.jpg)}
diff --git a/middleman-core/fixtures/asset-hash-prefix/config.rb b/middleman-core/fixtures/asset-hash-prefix/config.rb
new file mode 100644
index 00000000..449a2e98
--- /dev/null
+++ b/middleman-core/fixtures/asset-hash-prefix/config.rb
@@ -0,0 +1,7 @@
+
+activate :asset_hash,
+ prefix: "myprefix-"
+
+activate :relative_assets
+
+activate :directory_indexes
diff --git a/middleman-core/fixtures/asset-hash-prefix/lib/middleware.rb b/middleman-core/fixtures/asset-hash-prefix/lib/middleware.rb
new file mode 100644
index 00000000..8ee825fd
--- /dev/null
+++ b/middleman-core/fixtures/asset-hash-prefix/lib/middleware.rb
@@ -0,0 +1,16 @@
+class Middleware
+ def initialize(app)
+ @app = app
+ end
+
+ def call(env)
+ status, headers, response = @app.call(env)
+ body = ''
+ response.each {|part| body += part }
+ if (env["PATH_INFO"] =~ /css$/)
+ body += "\n/* Added by Rack filter */"
+ status, headers, response = Rack::Response.new(body, status, headers).finish
+ end
+ [status, headers, response]
+ end
+end
diff --git a/middleman-core/fixtures/asset-hash-prefix/source/index.html.erb b/middleman-core/fixtures/asset-hash-prefix/source/index.html.erb
new file mode 100644
index 00000000..a88cc13f
--- /dev/null
+++ b/middleman-core/fixtures/asset-hash-prefix/source/index.html.erb
@@ -0,0 +1,6 @@
+<% content_for :head do %>
+
The Middleman!
+<% end %>
+
+
Testing the sitemap hashing
+
diff --git a/middleman-core/fixtures/asset-hash-prefix/source/javascripts/application.js b/middleman-core/fixtures/asset-hash-prefix/source/javascripts/application.js
new file mode 100644
index 00000000..c72f8572
--- /dev/null
+++ b/middleman-core/fixtures/asset-hash-prefix/source/javascripts/application.js
@@ -0,0 +1,2 @@
+function foo(){var message="HEY THERE FRIEND!";var para=document.createElement("p");para.innerHTML=message;var body=document.getElementsByTagName("body")[0];body.insertBefore(para,body.firstChild)}window.onload=foo;
+//# sourceMappingURL=application.js.map
diff --git a/middleman-core/fixtures/asset-hash-prefix/source/javascripts/application.js.map b/middleman-core/fixtures/asset-hash-prefix/source/javascripts/application.js.map
new file mode 100644
index 00000000..6a5acd5c
--- /dev/null
+++ b/middleman-core/fixtures/asset-hash-prefix/source/javascripts/application.js.map
@@ -0,0 +1 @@
+{"version":3,"sources":["source/javascripts/application.js"],"names":["foo","message","para","document","createElement","innerHTML","body","getElementsByTagName","insertBefore","firstChild","window","onload"],"mappings":"AAAA,QAASA,OACP,GAAIC,SAAU,mBACd,IAAIC,MAAOC,SAASC,cAAc,IAClCF,MAAKG,UAAYJ,OACb,IAAIK,MAAOH,SAASI,qBAAqB,QAAQ,EAC/CD,MAAKE,aAAaN,KAAMI,KAAKG,YAGpCC,OAAOC,OAASX"}
\ No newline at end of file
diff --git a/middleman-core/fixtures/asset-hash-prefix/source/layout.erb b/middleman-core/fixtures/asset-hash-prefix/source/layout.erb
new file mode 100644
index 00000000..c8aacecf
--- /dev/null
+++ b/middleman-core/fixtures/asset-hash-prefix/source/layout.erb
@@ -0,0 +1,17 @@
+
+
+
+
+
+ <%= javascript_include_tag "application" %>
+ <%= yield_content :head %>
+
+
+
+
+
+ <%= yield %>
+
+
+
+
diff --git a/middleman-core/fixtures/asset-hash-source-map/config.rb b/middleman-core/fixtures/asset-hash-source-map/config.rb
new file mode 100644
index 00000000..1327d8f0
--- /dev/null
+++ b/middleman-core/fixtures/asset-hash-source-map/config.rb
@@ -0,0 +1,6 @@
+
+activate :asset_hash
+
+activate :relative_assets
+
+activate :directory_indexes
diff --git a/middleman-core/fixtures/asset-hash-source-map/lib/middleware.rb b/middleman-core/fixtures/asset-hash-source-map/lib/middleware.rb
new file mode 100644
index 00000000..8ee825fd
--- /dev/null
+++ b/middleman-core/fixtures/asset-hash-source-map/lib/middleware.rb
@@ -0,0 +1,16 @@
+class Middleware
+ def initialize(app)
+ @app = app
+ end
+
+ def call(env)
+ status, headers, response = @app.call(env)
+ body = ''
+ response.each {|part| body += part }
+ if (env["PATH_INFO"] =~ /css$/)
+ body += "\n/* Added by Rack filter */"
+ status, headers, response = Rack::Response.new(body, status, headers).finish
+ end
+ [status, headers, response]
+ end
+end
diff --git a/middleman-core/fixtures/asset-hash-source-map/source/index.html.erb b/middleman-core/fixtures/asset-hash-source-map/source/index.html.erb
new file mode 100644
index 00000000..a88cc13f
--- /dev/null
+++ b/middleman-core/fixtures/asset-hash-source-map/source/index.html.erb
@@ -0,0 +1,6 @@
+<% content_for :head do %>
+
The Middleman!
+<% end %>
+
+
Testing the sitemap hashing
+
diff --git a/middleman-core/fixtures/asset-hash-source-map/source/javascripts/application.js b/middleman-core/fixtures/asset-hash-source-map/source/javascripts/application.js
new file mode 100644
index 00000000..c72f8572
--- /dev/null
+++ b/middleman-core/fixtures/asset-hash-source-map/source/javascripts/application.js
@@ -0,0 +1,2 @@
+function foo(){var message="HEY THERE FRIEND!";var para=document.createElement("p");para.innerHTML=message;var body=document.getElementsByTagName("body")[0];body.insertBefore(para,body.firstChild)}window.onload=foo;
+//# sourceMappingURL=application.js.map
diff --git a/middleman-core/fixtures/asset-hash-source-map/source/javascripts/application.js.map b/middleman-core/fixtures/asset-hash-source-map/source/javascripts/application.js.map
new file mode 100644
index 00000000..6a5acd5c
--- /dev/null
+++ b/middleman-core/fixtures/asset-hash-source-map/source/javascripts/application.js.map
@@ -0,0 +1 @@
+{"version":3,"sources":["source/javascripts/application.js"],"names":["foo","message","para","document","createElement","innerHTML","body","getElementsByTagName","insertBefore","firstChild","window","onload"],"mappings":"AAAA,QAASA,OACP,GAAIC,SAAU,mBACd,IAAIC,MAAOC,SAASC,cAAc,IAClCF,MAAKG,UAAYJ,OACb,IAAIK,MAAOH,SAASI,qBAAqB,QAAQ,EAC/CD,MAAKE,aAAaN,KAAMI,KAAKG,YAGpCC,OAAOC,OAASX"}
\ No newline at end of file
diff --git a/middleman-core/fixtures/asset-hash-source-map/source/layout.erb b/middleman-core/fixtures/asset-hash-source-map/source/layout.erb
new file mode 100644
index 00000000..c8aacecf
--- /dev/null
+++ b/middleman-core/fixtures/asset-hash-source-map/source/layout.erb
@@ -0,0 +1,17 @@
+
+
+
+
+
+ <%= javascript_include_tag "application" %>
+ <%= yield_content :head %>
+
+
+
+
+
+ <%= yield %>
+
+
+
+
diff --git a/middleman-core/fixtures/asset-host-app/config.rb b/middleman-core/fixtures/asset-host-app/config.rb
index e69de29b..e470be25 100644
--- a/middleman-core/fixtures/asset-host-app/config.rb
+++ b/middleman-core/fixtures/asset-host-app/config.rb
@@ -0,0 +1 @@
+ activate :asset_host, host: "http://assets1.example.com"
diff --git a/middleman-core/fixtures/asset-host-app/source/asset_host.html.erb b/middleman-core/fixtures/asset-host-app/source/asset_host.html.erb
index 30843176..25f4a9d9 100755
--- a/middleman-core/fixtures/asset-host-app/source/asset_host.html.erb
+++ b/middleman-core/fixtures/asset-host-app/source/asset_host.html.erb
@@ -44,3 +44,12 @@
Angular.js
+
+
diff --git a/middleman-cli/features/.gitkeep b/middleman-core/fixtures/asset-host-app/source/images/blank0.gif
similarity index 100%
rename from middleman-cli/features/.gitkeep
rename to middleman-core/fixtures/asset-host-app/source/images/blank0.gif
diff --git a/middleman-cli/fixtures/.gitkeep b/middleman-core/fixtures/asset-host-app/source/images/blank1.gif
similarity index 100%
rename from middleman-cli/fixtures/.gitkeep
rename to middleman-core/fixtures/asset-host-app/source/images/blank1.gif
diff --git a/middleman-core/fixtures/asset-host-app/source/images/blank10.gif b/middleman-core/fixtures/asset-host-app/source/images/blank10.gif
new file mode 100644
index 00000000..e69de29b
diff --git a/middleman-core/fixtures/asset-host-app/source/images/blank100.gif b/middleman-core/fixtures/asset-host-app/source/images/blank100.gif
new file mode 100644
index 00000000..e69de29b
diff --git a/middleman-core/fixtures/asset-host-app/source/images/blank101.gif b/middleman-core/fixtures/asset-host-app/source/images/blank101.gif
new file mode 100644
index 00000000..e69de29b
diff --git a/middleman-core/fixtures/asset-host-app/source/images/blank1010.gif b/middleman-core/fixtures/asset-host-app/source/images/blank1010.gif
new file mode 100644
index 00000000..e69de29b
diff --git a/middleman-core/fixtures/asset-host-app/source/images/blank102.gif b/middleman-core/fixtures/asset-host-app/source/images/blank102.gif
new file mode 100644
index 00000000..e69de29b
diff --git a/middleman-core/fixtures/asset-host-app/source/images/blank1020.gif b/middleman-core/fixtures/asset-host-app/source/images/blank1020.gif
new file mode 100644
index 00000000..e69de29b
diff --git a/middleman-core/fixtures/asset-host-app/source/images/blank1021.gif b/middleman-core/fixtures/asset-host-app/source/images/blank1021.gif
new file mode 100644
index 00000000..e69de29b
diff --git a/middleman-core/fixtures/asset-host-app/source/images/blank1022.gif b/middleman-core/fixtures/asset-host-app/source/images/blank1022.gif
new file mode 100644
index 00000000..e69de29b
diff --git a/middleman-core/fixtures/asset-host-app/source/images/blank1023.gif b/middleman-core/fixtures/asset-host-app/source/images/blank1023.gif
new file mode 100644
index 00000000..e69de29b
diff --git a/middleman-core/fixtures/asset-host-app/source/images/blank1024.gif b/middleman-core/fixtures/asset-host-app/source/images/blank1024.gif
new file mode 100644
index 00000000..e69de29b
diff --git a/middleman-core/fixtures/asset-host-app/source/images/blank103.gif b/middleman-core/fixtures/asset-host-app/source/images/blank103.gif
new file mode 100644
index 00000000..e69de29b
diff --git a/middleman-core/fixtures/asset-host-app/source/images/blank1030.gif b/middleman-core/fixtures/asset-host-app/source/images/blank1030.gif
new file mode 100644
index 00000000..e69de29b
diff --git a/middleman-core/fixtures/asset-host-app/source/images/blank1031.gif b/middleman-core/fixtures/asset-host-app/source/images/blank1031.gif
new file mode 100644
index 00000000..e69de29b
diff --git a/middleman-core/fixtures/asset-host-app/source/images/blank1032.gif b/middleman-core/fixtures/asset-host-app/source/images/blank1032.gif
new file mode 100644
index 00000000..e69de29b
diff --git a/middleman-core/fixtures/asset-host-app/source/images/blank1033.gif b/middleman-core/fixtures/asset-host-app/source/images/blank1033.gif
new file mode 100644
index 00000000..e69de29b
diff --git a/middleman-core/fixtures/asset-host-app/source/images/blank1034.gif b/middleman-core/fixtures/asset-host-app/source/images/blank1034.gif
new file mode 100644
index 00000000..e69de29b
diff --git a/middleman-core/fixtures/asset-host-app/source/images/blank104.gif b/middleman-core/fixtures/asset-host-app/source/images/blank104.gif
new file mode 100644
index 00000000..e69de29b
diff --git a/middleman-core/fixtures/asset-host-app/source/images/blank1043.gif b/middleman-core/fixtures/asset-host-app/source/images/blank1043.gif
new file mode 100644
index 00000000..e69de29b
diff --git a/middleman-core/fixtures/asset-host-app/source/images/blank1054.gif b/middleman-core/fixtures/asset-host-app/source/images/blank1054.gif
new file mode 100644
index 00000000..e69de29b
diff --git a/middleman-core/fixtures/asset-host-app/source/images/blank2.gif b/middleman-core/fixtures/asset-host-app/source/images/blank2.gif
new file mode 100644
index 00000000..e69de29b
diff --git a/middleman-core/fixtures/asset-host-app/source/images/blank20.gif b/middleman-core/fixtures/asset-host-app/source/images/blank20.gif
new file mode 100644
index 00000000..e69de29b
diff --git a/middleman-core/fixtures/asset-host-app/source/images/blank21.gif b/middleman-core/fixtures/asset-host-app/source/images/blank21.gif
new file mode 100644
index 00000000..e69de29b
diff --git a/middleman-core/fixtures/asset-host-app/source/images/blank22.gif b/middleman-core/fixtures/asset-host-app/source/images/blank22.gif
new file mode 100644
index 00000000..e69de29b
diff --git a/middleman-core/fixtures/asset-host-app/source/images/blank23.gif b/middleman-core/fixtures/asset-host-app/source/images/blank23.gif
new file mode 100644
index 00000000..e69de29b
diff --git a/middleman-core/fixtures/asset-host-app/source/images/blank24.gif b/middleman-core/fixtures/asset-host-app/source/images/blank24.gif
new file mode 100644
index 00000000..e69de29b
diff --git a/middleman-core/fixtures/asset-host-app/source/images/blank3.gif b/middleman-core/fixtures/asset-host-app/source/images/blank3.gif
new file mode 100644
index 00000000..e69de29b
diff --git a/middleman-core/fixtures/asset-host-app/source/images/blank30.gif b/middleman-core/fixtures/asset-host-app/source/images/blank30.gif
new file mode 100644
index 00000000..e69de29b
diff --git a/middleman-core/fixtures/asset-host-app/source/images/blank31.gif b/middleman-core/fixtures/asset-host-app/source/images/blank31.gif
new file mode 100644
index 00000000..e69de29b
diff --git a/middleman-core/fixtures/asset-host-app/source/images/blank32.gif b/middleman-core/fixtures/asset-host-app/source/images/blank32.gif
new file mode 100644
index 00000000..e69de29b
diff --git a/middleman-core/fixtures/asset-host-app/source/images/blank33.gif b/middleman-core/fixtures/asset-host-app/source/images/blank33.gif
new file mode 100644
index 00000000..e69de29b
diff --git a/middleman-core/fixtures/asset-host-app/source/images/blank34.gif b/middleman-core/fixtures/asset-host-app/source/images/blank34.gif
new file mode 100644
index 00000000..e69de29b
diff --git a/middleman-core/fixtures/asset-host-app/source/images/blank4.gif b/middleman-core/fixtures/asset-host-app/source/images/blank4.gif
new file mode 100644
index 00000000..e69de29b
diff --git a/middleman-core/fixtures/asset-host-app/source/images/blank43.gif b/middleman-core/fixtures/asset-host-app/source/images/blank43.gif
new file mode 100644
index 00000000..e69de29b
diff --git a/middleman-core/fixtures/asset-host-app/source/images/blank54.gif b/middleman-core/fixtures/asset-host-app/source/images/blank54.gif
new file mode 100644
index 00000000..e69de29b
diff --git a/middleman-core/fixtures/asset-host-app/source/javascripts/asset_host.js b/middleman-core/fixtures/asset-host-app/source/javascripts/asset_host.js
new file mode 100644
index 00000000..251ce693
--- /dev/null
+++ b/middleman-core/fixtures/asset-host-app/source/javascripts/asset_host.js
@@ -0,0 +1,2 @@
+var a = jQuery.css("h1", "font-size");
+console.log(a);
diff --git a/middleman-core/fixtures/automatic-image-size-app/source/markdown-sizes.html.markdown b/middleman-core/fixtures/automatic-image-size-app/source/markdown-sizes.html.markdown
new file mode 100644
index 00000000..1f9c8dcd
--- /dev/null
+++ b/middleman-core/fixtures/automatic-image-size-app/source/markdown-sizes.html.markdown
@@ -0,0 +1 @@
+
diff --git a/middleman-core/fixtures/dynamic-pages-app/config.rb b/middleman-core/fixtures/dynamic-pages-app/config.rb
index 75fab5e0..b195e0f9 100644
--- a/middleman-core/fixtures/dynamic-pages-app/config.rb
+++ b/middleman-core/fixtures/dynamic-pages-app/config.rb
@@ -28,3 +28,7 @@ page "fake/*", locals: { glob_var: "I am one glob" }
page "fake2/*", locals: { glob_var: "I am two glob" }
page "fake3/*", locals: { glob_var: "I am three glob" }
page "fake4/*", locals: { glob_var: "I am four glob" }
+
+["tom", "dick", "harry"].each do |name|
+ proxy "/about/#{name}.html", "/should_be_ignored9.html", locals: { person_name: name }, ignore: true
+end
diff --git a/middleman-core/fixtures/dynamic-pages-app/source/should_be_ignored9.html b/middleman-core/fixtures/dynamic-pages-app/source/should_be_ignored9.html
new file mode 100644
index 00000000..53571d5e
--- /dev/null
+++ b/middleman-core/fixtures/dynamic-pages-app/source/should_be_ignored9.html
@@ -0,0 +1 @@
+
Ignore me! 9
diff --git a/middleman-core/fixtures/external-pipeline-error/config.rb b/middleman-core/fixtures/external-pipeline-error/config.rb
new file mode 100644
index 00000000..96bb2b94
--- /dev/null
+++ b/middleman-core/fixtures/external-pipeline-error/config.rb
@@ -0,0 +1,5 @@
+activate :external_pipeline,
+ name: :failing,
+ command: "mv does-not-exist tmp/file.js",
+ source: "tmp",
+ latency: 2
diff --git a/middleman-core/fixtures/external-pipeline-error/source/javascripts/file.js b/middleman-core/fixtures/external-pipeline-error/source/javascripts/file.js
new file mode 100644
index 00000000..e69de29b
diff --git a/middleman-core/fixtures/frontmatter-neighbor-app/config.rb b/middleman-core/fixtures/frontmatter-neighbor-app/config.rb
index 6fa6e37c..019d65f7 100644
--- a/middleman-core/fixtures/frontmatter-neighbor-app/config.rb
+++ b/middleman-core/fixtures/frontmatter-neighbor-app/config.rb
@@ -15,7 +15,7 @@ class NeighborFrontmatter < ::Middleman::Extension
next unless file
- fmdata = ::Middleman::Util::Data.parse(file[:full_path], app.config[:frontmatter_delims], :yaml).first
+ fmdata = ::Middleman::Util::Data.parse(file, app.config[:frontmatter_delims], :yaml).first
opts = fmdata.extract!(:layout, :layout_engine, :renderer_options, :directory_index, :content_type)
opts[:renderer_options].symbolize_keys! if opts.key?(:renderer_options)
ignored = fmdata.delete(:ignored)
diff --git a/middleman-core/fixtures/frontmatter-settings-neighbor-app/config.rb b/middleman-core/fixtures/frontmatter-settings-neighbor-app/config.rb
index a7e0fc55..7adbe389 100644
--- a/middleman-core/fixtures/frontmatter-settings-neighbor-app/config.rb
+++ b/middleman-core/fixtures/frontmatter-settings-neighbor-app/config.rb
@@ -26,7 +26,7 @@ class NeighborFrontmatter < ::Middleman::Extension
end
def apply_neighbor_data(resource, file)
- fmdata = ::Middleman::Util::Data.parse(file[:full_path], app.config[:frontmatter_delims], :yaml).first
+ fmdata = ::Middleman::Util::Data.parse(file, app.config[:frontmatter_delims], :yaml).first
opts = fmdata.extract!(:layout, :layout_engine, :renderer_options, :directory_index, :content_type)
opts[:renderer_options].symbolize_keys! if opts.key?(:renderer_options)
ignored = fmdata.delete(:ignored)
diff --git a/middleman-core/fixtures/nested-layout-app/source/layouts/inner.erb b/middleman-core/fixtures/nested-layout-app/source/layouts/inner.erb
index 62dfc12b..3e29f567 100644
--- a/middleman-core/fixtures/nested-layout-app/source/layouts/inner.erb
+++ b/middleman-core/fixtures/nested-layout-app/source/layouts/inner.erb
@@ -1,4 +1,7 @@
+---
+inner: true
+---
<% wrap_layout :outer do %>
- Inner
+ I am Inner
<%= yield %>
-<% end %>
\ No newline at end of file
+<% end %>
diff --git a/middleman-core/fixtures/nested-layout-app/source/layouts/inner_haml.haml b/middleman-core/fixtures/nested-layout-app/source/layouts/inner_haml.haml
index a8c1938d..5f99da1e 100644
--- a/middleman-core/fixtures/nested-layout-app/source/layouts/inner_haml.haml
+++ b/middleman-core/fixtures/nested-layout-app/source/layouts/inner_haml.haml
@@ -1,3 +1,7 @@
+---
+inner_haml: true
+---
+
= wrap_layout :outer_haml do
- Inner
- = yield
\ No newline at end of file
+ I am Inner
+ = yield
diff --git a/middleman-core/fixtures/nested-layout-app/source/layouts/inner_slim.slim b/middleman-core/fixtures/nested-layout-app/source/layouts/inner_slim.slim
index 49f1ab6f..f7d9c924 100644
--- a/middleman-core/fixtures/nested-layout-app/source/layouts/inner_slim.slim
+++ b/middleman-core/fixtures/nested-layout-app/source/layouts/inner_slim.slim
@@ -1,3 +1,7 @@
+---
+inner_slim: true
+---
+
= wrap_layout :outer_slim do
- h3 Inner
- == yield
\ No newline at end of file
+ h3 I am Inner
+ == yield
diff --git a/middleman-core/fixtures/nested-layout-app/source/layouts/master.erb b/middleman-core/fixtures/nested-layout-app/source/layouts/master.erb
index 7bd170af..fc862aa4 100644
--- a/middleman-core/fixtures/nested-layout-app/source/layouts/master.erb
+++ b/middleman-core/fixtures/nested-layout-app/source/layouts/master.erb
@@ -1,3 +1,9 @@
-Master
+---
+master: true
+---
+
+Master Erb
<%= current_page.data.title %>
<%= yield %>
+
+Using Inner: <%= current_page.data.inner ? 'true' : 'false' %>
diff --git a/middleman-core/fixtures/nested-layout-app/source/layouts/master_haml.haml b/middleman-core/fixtures/nested-layout-app/source/layouts/master_haml.haml
index 273f21e7..3c949d5c 100644
--- a/middleman-core/fixtures/nested-layout-app/source/layouts/master_haml.haml
+++ b/middleman-core/fixtures/nested-layout-app/source/layouts/master_haml.haml
@@ -1,3 +1,7 @@
-Master
+---
+master_haml: true
+---
+
+Master Haml
%h1= current_page.data.title
= yield
diff --git a/middleman-core/fixtures/nested-layout-app/source/layouts/master_slim.slim b/middleman-core/fixtures/nested-layout-app/source/layouts/master_slim.slim
index 4ade5748..cd5eac1d 100644
--- a/middleman-core/fixtures/nested-layout-app/source/layouts/master_slim.slim
+++ b/middleman-core/fixtures/nested-layout-app/source/layouts/master_slim.slim
@@ -1,3 +1,7 @@
-h1 Master
+---
+master_slim: true
+---
+
+h1 Master Slim
p== current_page.data.title
div== yield
diff --git a/middleman-core/fixtures/nested-layout-app/source/layouts/outer.erb b/middleman-core/fixtures/nested-layout-app/source/layouts/outer.erb
index 643d8efc..adc4f9e2 100644
--- a/middleman-core/fixtures/nested-layout-app/source/layouts/outer.erb
+++ b/middleman-core/fixtures/nested-layout-app/source/layouts/outer.erb
@@ -1,4 +1,8 @@
+---
+outer: true
+---
+
<% wrap_layout :master do %>
- Outer
+ I am Outer
<%= yield %>
-<% end %>
\ No newline at end of file
+<% end %>
diff --git a/middleman-core/fixtures/nested-layout-app/source/layouts/outer_haml.haml b/middleman-core/fixtures/nested-layout-app/source/layouts/outer_haml.haml
index eaf1671f..d6d4b05b 100644
--- a/middleman-core/fixtures/nested-layout-app/source/layouts/outer_haml.haml
+++ b/middleman-core/fixtures/nested-layout-app/source/layouts/outer_haml.haml
@@ -1,3 +1,7 @@
+---
+outer_haml: true
+---
+
= wrap_layout :master_haml do
- Outer
+ I am Outer
= yield
diff --git a/middleman-core/fixtures/nested-layout-app/source/layouts/outer_slim.slim b/middleman-core/fixtures/nested-layout-app/source/layouts/outer_slim.slim
index 0c17185b..65eb3277 100644
--- a/middleman-core/fixtures/nested-layout-app/source/layouts/outer_slim.slim
+++ b/middleman-core/fixtures/nested-layout-app/source/layouts/outer_slim.slim
@@ -1,3 +1,7 @@
+---
+outer_slim: true
+---
+
= wrap_layout :master_slim do
- h2 Outer
+ h2 I am Outer
== yield
diff --git a/middleman-core/fixtures/page-id-app/config-proc.rb b/middleman-core/fixtures/page-id-app/config-proc.rb
new file mode 100644
index 00000000..03070f0e
--- /dev/null
+++ b/middleman-core/fixtures/page-id-app/config-proc.rb
@@ -0,0 +1,7 @@
+%w(1 2 3).each do |n|
+ proxy "/#{n}.html", "/index.html", id: "page#{n}"
+end
+
+page "/overwrites/*", id: :"something-else"
+
+config[:page_id_generator] = ->(path){ path + "-foo" }
diff --git a/middleman-core/fixtures/page-id-app/config.rb b/middleman-core/fixtures/page-id-app/config.rb
new file mode 100644
index 00000000..d0475e7a
--- /dev/null
+++ b/middleman-core/fixtures/page-id-app/config.rb
@@ -0,0 +1,5 @@
+%w(1 2 3).each do |n|
+ proxy "/#{n}.html", "/index.html", id: "page#{n}"
+end
+
+page "/overwrites/*", id: :"something-else"
diff --git a/middleman-core/fixtures/page-id-app/source/feed.xml.erb b/middleman-core/fixtures/page-id-app/source/feed.xml.erb
new file mode 100644
index 00000000..a27800bd
--- /dev/null
+++ b/middleman-core/fixtures/page-id-app/source/feed.xml.erb
@@ -0,0 +1 @@
+I am: <%= current_resource.page_id %>
diff --git a/middleman-core/fixtures/page-id-app/source/fm.html.erb b/middleman-core/fixtures/page-id-app/source/fm.html.erb
new file mode 100644
index 00000000..4d6d0772
--- /dev/null
+++ b/middleman-core/fixtures/page-id-app/source/fm.html.erb
@@ -0,0 +1,5 @@
+---
+id: frontmatter
+---
+
+I am: <%= current_resource.page_id %>
diff --git a/middleman-core/fixtures/page-id-app/source/folder/foldern.html.erb b/middleman-core/fixtures/page-id-app/source/folder/foldern.html.erb
new file mode 100644
index 00000000..a27800bd
--- /dev/null
+++ b/middleman-core/fixtures/page-id-app/source/folder/foldern.html.erb
@@ -0,0 +1 @@
+I am: <%= current_resource.page_id %>
diff --git a/middleman-core/fixtures/page-id-app/source/implicit.html.erb b/middleman-core/fixtures/page-id-app/source/implicit.html.erb
new file mode 100644
index 00000000..a27800bd
--- /dev/null
+++ b/middleman-core/fixtures/page-id-app/source/implicit.html.erb
@@ -0,0 +1 @@
+I am: <%= current_resource.page_id %>
diff --git a/middleman-core/fixtures/page-id-app/source/index.html.erb b/middleman-core/fixtures/page-id-app/source/index.html.erb
new file mode 100644
index 00000000..6060bc3f
--- /dev/null
+++ b/middleman-core/fixtures/page-id-app/source/index.html.erb
@@ -0,0 +1,14 @@
+I am: <%= current_resource.page_id %>
+
+URL1: <%= url_for "frontmatter" %>
+URL2: <%= url_for "page2" %>
+URL3: <%= link_to "Hi", "page3" %>
+URL4: <%= link_to "Sym", :"something-else" %>
+URL5: <%= link_to "Imp", :implicit %>
+URL6: <%= link_to "Foldern", "folder/foldern" %>
+URL7: <%= link_to "Feed", "feed.xml" %>
+
+<%# If custom proc %>
+URL8: <%= link_to "Imp", "implicit.html-foo" %>
+URL9: <%= link_to "Foldern", "folder/foldern.html-foo" %>
+URL10: <%= link_to "Feed", "feed.xml-foo" %>
diff --git a/middleman-core/fixtures/page-id-app/source/overwrites/from-default.html.erb b/middleman-core/fixtures/page-id-app/source/overwrites/from-default.html.erb
new file mode 100644
index 00000000..a27800bd
--- /dev/null
+++ b/middleman-core/fixtures/page-id-app/source/overwrites/from-default.html.erb
@@ -0,0 +1 @@
+I am: <%= current_resource.page_id %>
diff --git a/middleman-core/fixtures/page-id-app/source/overwrites/from-frontmatter.html.erb b/middleman-core/fixtures/page-id-app/source/overwrites/from-frontmatter.html.erb
new file mode 100644
index 00000000..194cf4c1
--- /dev/null
+++ b/middleman-core/fixtures/page-id-app/source/overwrites/from-frontmatter.html.erb
@@ -0,0 +1,5 @@
+---
+id: from_frontmatter
+---
+
+I am: <%= current_resource.page_id %>
diff --git a/middleman-core/fixtures/partials-app/source/_block.erb b/middleman-core/fixtures/partials-app/source/_block.erb
new file mode 100644
index 00000000..179e0c82
--- /dev/null
+++ b/middleman-core/fixtures/partials-app/source/_block.erb
@@ -0,0 +1,3 @@
+Start
+<%= yield %>
+End
diff --git a/middleman-core/fixtures/partials-app/source/block.html.erb b/middleman-core/fixtures/partials-app/source/block.html.erb
new file mode 100644
index 00000000..613f7ccb
--- /dev/null
+++ b/middleman-core/fixtures/partials-app/source/block.html.erb
@@ -0,0 +1,3 @@
+<% partial "block" do %>
+Contents
+<% end %>
diff --git a/middleman-core/fixtures/partials-app/source/index_missing.html.erb b/middleman-core/fixtures/partials-app/source/index_missing.html.erb
new file mode 100755
index 00000000..bcd70b82
--- /dev/null
+++ b/middleman-core/fixtures/partials-app/source/index_missing.html.erb
@@ -0,0 +1,3 @@
+<%= partial "shared/header" %>
+<%= partial "i_do_not_exist" %>
+<%= partial "shared/footer" %>
diff --git a/middleman-core/fixtures/relative-assets-app/source/images/blank2.gif b/middleman-core/fixtures/relative-assets-app/source/images/blank2.gif
new file mode 100644
index 00000000..e69de29b
diff --git a/middleman-core/fixtures/sass-in-slim-app/config.rb b/middleman-core/fixtures/sass-in-slim-app/config.rb
new file mode 100644
index 00000000..e69de29b
diff --git a/middleman-core/lib/middleman-core.rb b/middleman-core/lib/middleman-core.rb
index 8170fefb..687a53e5 100644
--- a/middleman-core/lib/middleman-core.rb
+++ b/middleman-core/lib/middleman-core.rb
@@ -6,9 +6,6 @@ $LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)
# Top-level Middleman namespace
module Middleman
- # Backwards compatibility namespace
- module Features; end
-
autoload :Application, 'middleman-core/application'
end
diff --git a/middleman-core/lib/middleman-core/application.rb b/middleman-core/lib/middleman-core/application.rb
index be603df4..52b31c92 100644
--- a/middleman-core/lib/middleman-core/application.rb
+++ b/middleman-core/lib/middleman-core/application.rb
@@ -96,6 +96,10 @@ module Middleman
# @return [String]
define_setting :source, 'source', 'Name of the source directory'
+ # If we should not run the sitemap.
+ # @return [Boolean]
+ define_setting :disable_sitemap, false, 'If we should not run the sitemap.'
+
# If we should exit before ready event.
# @return [Boolean]
define_setting :exit_before_ready, false, 'If we should exit before ready event.'
@@ -106,7 +110,7 @@ module Middleman
# Middleman environment. Defaults to :development
# @return [String]
- define_setting :environment, ((ENV['MM_ENV'] && ENV['MM_ENV'].to_sym) || :development), 'Middleman environment. Defaults to :development'
+ define_setting :environment, ((ENV['MM_ENV'] && ENV['MM_ENV'].to_sym) || :development), 'Middleman environment. Defaults to :development', import: proc { |s| s.to_sym }
# Which file should be used for directory indexes
# @return [String]
@@ -149,9 +153,17 @@ module Middleman
define_setting :http_prefix, '/', 'Default prefix for building paths'
# Default layout name
- # @return [String, Symbold]
+ # @return [String]
define_setting :layout, :_auto_layout, 'Default layout name'
+ # Which file extensions have a layout by default.
+ # @return [Array.
]
+ define_setting :extensions_with_layout, %w(.htm .html .xhtml .php), 'Which file extensions have a layout by default.'
+
+ # Which file extensions are "assets."
+ # @return [Array.]
+ define_setting :asset_extensions, %w(.css .png .jpg .jpeg .webp .svg .svgz .js .gif .ttf .otf .woff .woff2 .eot .ico .map), 'Which file extensions are treated as assets.'
+
# Default string encoding for templates and output.
# @return [String]
define_setting :encoding, 'utf-8', 'Default string encoding for templates and output'
@@ -170,7 +182,7 @@ module Middleman
ignored = false
file[:relative_path].ascend do |f|
- if f.basename.to_s.match %r{^_[^_]}
+ if f.basename.to_s =~ %r{^_[^_]}
ignored = true
break
end
@@ -179,23 +191,19 @@ module Middleman
ignored
end,
- layout: proc do |file, _sitemap_app|
- file[:relative_path].to_s.start_with?('layout.') ||
- file[:relative_path].to_s.start_with?('layouts/')
+ layout: proc do |file, app|
+ file[:relative_path].to_s.start_with?('layout.', app.config[:layouts_dir] + '/')
end
}, 'Callbacks that can exclude paths from the sitemap'
- # Set textual delimiters that denote the start and end of frontmatter
- define_setting :frontmatter_delims, {
- json: [%w(;;; ;;;)],
- yaml: [%w(--- ---), %w(--- ...)]
- }, 'Allowed frontmatter delimiters'
+ define_setting :skip_build_clean, proc { |p| [/\.git/].any? { |r| p =~ r } }, 'Whether some paths should not be removed during a clean build.'
- define_setting :skip_build_clean, proc { |p| [/\.git/].any? { |r| r.match(p) } }, 'Whether some paths should not be removed during a clean build.'
+ define_setting :cli_options, {}, 'Options from the Command Line.'
define_setting :watcher_disable, false, 'If the Listen watcher should not run'
define_setting :watcher_force_polling, false, 'If the Listen watcher should run in polling mode'
define_setting :watcher_latency, nil, 'The Listen watcher latency'
+ define_setting :watcher_wait_for_delay, 0.5, 'The Listen watcher delay between calls when changes exist'
# Delegate convenience methods off to their implementations
def_delegator :"::Middleman::Logger", :singleton, :logger
@@ -210,49 +218,57 @@ module Middleman
# Search the root of the project for required files
$LOAD_PATH.unshift(root) unless $LOAD_PATH.include?(root)
- @callbacks = ::Middleman::CallbackManager.new
- @callbacks.install_methods!(self, [
- :initialized,
- :configure,
- :before_extensions,
- :before_sitemap,
- :before_configuration,
- :after_configuration,
- :after_configuration_eval,
- :ready,
- :before_build,
- :after_build,
- :before_shutdown,
- :before, # Before Rack requests
- :before_render,
- :after_render,
- :before_server
- ])
+ ::Middleman::Util.instrument 'application.setup' do
+ @callbacks = ::Middleman::CallbackManager.new
+ @callbacks.install_methods!(self, [
+ :initialized,
+ :configure,
+ :before_extensions,
+ :before_instance_block,
+ :before_sitemap,
+ :before_configuration,
+ :after_configuration,
+ :after_configuration_eval,
+ :ready,
+ :before_build,
+ :after_build,
+ :before_shutdown,
+ :before, # Before Rack requests
+ :before_render,
+ :after_render,
+ :before_server,
+ :reload
+ ])
- @middleware = Set.new
- @mappings = Set.new
+ @middleware = Set.new
+ @mappings = Set.new
- @template_context_class = Class.new(Middleman::TemplateContext)
- @generic_template_context = @template_context_class.new(self)
- @config_context = ConfigContext.new(self, @template_context_class)
+ @template_context_class = Class.new(Middleman::TemplateContext)
+ @generic_template_context = @template_context_class.new(self)
+ @config_context = ConfigContext.new(self, @template_context_class)
- # Setup the default values from calls to set before initialization
- @config = ::Middleman::Configuration::ConfigurationManager.new
- @config.load_settings(self.class.config.all_settings)
+ # Setup the default values from calls to set before initialization
+ @config = ::Middleman::Configuration::ConfigurationManager.new
+ @config.load_settings(self.class.config.all_settings)
- config[:source] = ENV['MM_SOURCE'] if ENV['MM_SOURCE']
+ config[:source] = ENV['MM_SOURCE'] if ENV['MM_SOURCE']
- # TODO, make this less global
- ::Middleman::FileRenderer.cache.clear
- ::Middleman::TemplateRenderer.cache.clear
+ # TODO, make this less global
+ ::Middleman::FileRenderer.cache.clear
+ ::Middleman::TemplateRenderer.cache.clear
+ end
execute_callbacks(:before_extensions)
@extensions = ::Middleman::ExtensionManager.new(self)
+ execute_callbacks(:before_instance_block)
+
# Evaluate a passed block if given
config_context.instance_exec(&block) if block_given?
+ apply_cli_options
+
execute_callbacks(:before_sitemap)
# Initialize the Sitemap
@@ -275,6 +291,8 @@ module Middleman
# Run any `configure` blocks for the current mode.
execute_callbacks([:configure, config[:mode]])
+ apply_cli_options
+
# Post parsing, pre-extension callback
execute_callbacks(:after_configuration_eval)
@@ -291,6 +309,17 @@ module Middleman
execute_callbacks(:ready) unless config[:exit_before_ready]
end
+ def apply_cli_options
+ config[:cli_options].each do |k, v|
+ setting = config.setting(k.to_sym)
+ next unless setting
+
+ v = setting.options[:import].call(v) if setting.options[:import]
+
+ config[k.to_sym] = v
+ end
+ end
+
# Eval config
def evaluate_configuration!
# Check for and evaluate local configuration in `config.rb`
@@ -316,11 +345,11 @@ module Middleman
# Clean up missing Tilt exts
def prune_tilt_templates!
- ::Tilt.mappings.each do |key, _|
+ ::Tilt.default_mapping.lazy_map.each_key do |key|
begin
::Tilt[".#{key}"]
rescue LoadError, NameError
- ::Tilt.mappings.delete(key)
+ ::Tilt.default_mapping.lazy_map.delete(key)
end
end
end
@@ -426,6 +455,6 @@ module Middleman
def to_s
"#"
end
- alias_method :inspect, :to_s # Ruby 2.0 calls inspect for NoMethodError instead of to_s
+ alias inspect to_s # Ruby 2.0 calls inspect for NoMethodError instead of to_s
end
end
diff --git a/middleman-core/lib/middleman-core/builder.rb b/middleman-core/lib/middleman-core/builder.rb
index 02feb15b..1796d4d7 100644
--- a/middleman-core/lib/middleman-core/builder.rb
+++ b/middleman-core/lib/middleman-core/builder.rb
@@ -1,6 +1,7 @@
require 'pathname'
require 'fileutils'
require 'tempfile'
+require 'parallel'
require 'middleman-core/rack'
require 'middleman-core/callback_manager'
require 'middleman-core/contracts'
@@ -20,7 +21,7 @@ module Middleman
def_delegator :@app, :logger
# Sort order, images, fonts, js/css and finally everything else.
- SORT_ORDER = %w(.png .jpeg .jpg .gif .bmp .svg .svgz .webp .ico .woff .woff2 .otf .ttf .eot .js .css)
+ SORT_ORDER = %w(.png .jpeg .jpg .gif .bmp .svg .svgz .webp .ico .woff .woff2 .otf .ttf .eot .js .css).freeze
# Create a new Builder instance.
# @param [Middleman::Application] app The app to build.
@@ -36,6 +37,7 @@ module Middleman
@glob = opts.fetch(:glob)
@cleaning = opts.fetch(:clean)
+ @parallel = opts.fetch(:parallel, true)
rack_app = ::Middleman::Rack.new(@app).to_app
@rack = ::Rack::MockRequest.new(rack_app)
@@ -51,18 +53,33 @@ module Middleman
@has_error = false
@events = {}
- @app.execute_callbacks(:before_build, [self])
+ ::Middleman::Util.instrument 'builder.before' do
+ @app.execute_callbacks(:before_build, [self])
+ end
- queue_current_paths if @cleaning
+ ::Middleman::Util.instrument 'builder.queue' do
+ queue_current_paths if @cleaning
+ end
- prerender_css
- output_files
+ ::Middleman::Util.instrument 'builder.prerender' do
+ prerender_css
+ end
- clean! if @cleaning
+ ::Middleman::Profiling.start
+
+ ::Middleman::Util.instrument 'builder.output' do
+ output_files
+ end
::Middleman::Profiling.report('build')
- @app.execute_callbacks(:after_build, [self])
+ ::Middleman::Util.instrument 'builder.clean' do
+ clean! if @cleaning
+ end
+
+ ::Middleman::Util.instrument 'builder.after' do
+ @app.execute_callbacks(:after_build, [self])
+ end
!@has_error
end
@@ -72,13 +89,20 @@ module Middleman
Contract ResourceList
def prerender_css
logger.debug '== Prerendering CSS'
- css_files = @app.sitemap.resources.select do |resource|
- resource.ext == '.css'
- end.each(&method(:output_resource))
- logger.debug '== Checking for Compass sprites'
- # Double-check for compass sprites
- @app.files.find_new_files!
- @app.sitemap.ensure_resource_list_updated!
+
+ css_files = ::Middleman::Util.instrument 'builder.prerender.output' do
+ resources = @app.sitemap.resources.select { |resource| resource.ext == '.css' }
+ output_resources(resources)
+ end
+
+ ::Middleman::Util.instrument 'builder.prerender.check-files' do
+ # Double-check for compass sprites
+ unless @app.files.find_new_files!.empty?
+ logger.debug '== Checking for Compass sprites'
+ @app.sitemap.ensure_resource_list_updated!
+ end
+ end
+
css_files
end
@@ -88,11 +112,49 @@ module Middleman
def output_files
logger.debug '== Building files'
- @app.sitemap.resources
- .sort_by { |resource| SORT_ORDER.index(resource.ext) || 100 }
- .reject { |resource| resource.ext == '.css' }
- .select { |resource| !@glob || File.fnmatch(@glob, resource.destination_path) }
- .each(&method(:output_resource))
+ resources = @app.sitemap.resources
+ .reject { |resource| resource.ext == '.css' }
+ .sort_by { |resource| SORT_ORDER.index(resource.ext) || 100 }
+
+ if @glob
+ resources = resources.select do |resource|
+ if defined?(::File::FNM_EXTGLOB)
+ File.fnmatch(@glob, resource.destination_path, ::File::FNM_EXTGLOB)
+ else
+ File.fnmatch(@glob, resource.destination_path)
+ end
+ end
+ end
+
+ output_resources(resources)
+ end
+
+ Contract ResourceList => ResourceList
+ def output_resources(resources)
+ results = if @parallel
+ ::Parallel.map(resources, &method(:output_resource))
+ else
+ resources.map(&method(:output_resource))
+ end
+
+ @has_error = true if results.any? { |r| r == false }
+
+ if @cleaning && !@has_error
+ results.each do |p|
+ next unless p.exist?
+
+ # handle UTF-8-MAC filename on MacOS
+ cleaned_name = if RUBY_PLATFORM =~ /darwin/
+ p.to_s.encode('UTF-8', 'UTF-8-MAC')
+ else
+ p
+ end
+
+ @to_clean.delete(Pathname(cleaned_name))
+ end
+ end
+
+ resources
end
# Figure out the correct event mode.
@@ -115,9 +177,9 @@ module Middleman
Contract Pathname, String => Tempfile
def write_tempfile(output_file, contents)
file = Tempfile.new([
- File.basename(output_file),
- File.extname(output_file)
- ])
+ File.basename(output_file),
+ File.extname(output_file)
+ ])
file.binmode
file.write(contents)
file.close
@@ -131,63 +193,57 @@ module Middleman
# @return [void]
Contract Pathname, Or[String, Pathname] => Any
def export_file!(output_file, source)
+ # ::Middleman::Util.instrument "write_file", output_file: output_file do
source = write_tempfile(output_file, source.to_s) if source.is_a? String
method, source_path = if source.is_a? Tempfile
- [FileUtils.method(:mv), source.path]
+ [::FileUtils.method(:mv), source.path]
else
- [FileUtils.method(:cp), source.to_s]
+ [::FileUtils.method(:cp), source.to_s]
end
mode = which_mode(output_file, source_path)
if mode == :created || mode == :updated
- FileUtils.mkdir_p(output_file.dirname)
+ ::FileUtils.mkdir_p(output_file.dirname)
method.call(source_path, output_file.to_s)
end
source.unlink if source.is_a? Tempfile
trigger(mode, output_file)
+ # end
end
# Try to output a resource and capture errors.
# @param [Middleman::Sitemap::Resource] resource The resource.
# @return [void]
- Contract IsA['Middleman::Sitemap::Resource'] => Any
+ Contract IsA['Middleman::Sitemap::Resource'] => Or[Pathname, Bool]
def output_resource(resource)
- output_file = @build_dir + resource.destination_path.gsub('%20', ' ')
+ ::Middleman::Util.instrument 'builder.output.resource', path: File.basename(resource.destination_path) do
+ output_file = @build_dir + resource.destination_path.gsub('%20', ' ')
- begin
- if resource.binary?
- export_file!(output_file, resource.file_descriptor[:full_path])
- else
- response = @rack.get(URI.escape(resource.request_path))
-
- # If we get a response, save it to a tempfile.
- if response.status == 200
- export_file!(output_file, binary_encode(response.body))
+ begin
+ if resource.binary?
+ export_file!(output_file, resource.file_descriptor[:full_path])
else
- @has_error = true
- trigger(:error, output_file, response.body)
+ response = @rack.get(::URI.escape(resource.request_path))
+
+ # If we get a response, save it to a tempfile.
+ if response.status == 200
+ export_file!(output_file, binary_encode(response.body))
+ else
+ trigger(:error, output_file, response.body)
+ return false
+ end
end
+ rescue => e
+ trigger(:error, output_file, "#{e}\n#{e.backtrace.join("\n")}")
+ return false
end
- rescue => e
- @has_error = true
- trigger(:error, output_file, "#{e}\n#{e.backtrace.join("\n")}")
- end
- return unless @cleaning
- return unless output_file.exist?
-
- # handle UTF-8-MAC filename on MacOS
- cleaned_name = if RUBY_PLATFORM =~ /darwin/
- output_file.to_s.encode('UTF-8', 'UTF-8-MAC')
- else
output_file
end
-
- @to_clean.delete(Pathname(cleaned_name))
end
# Get a list of all the paths in the destination folder and save them
diff --git a/middleman-core/lib/middleman-core/callback_manager.rb b/middleman-core/lib/middleman-core/callback_manager.rb
index 4315256a..0edaf4cd 100644
--- a/middleman-core/lib/middleman-core/callback_manager.rb
+++ b/middleman-core/lib/middleman-core/callback_manager.rb
@@ -48,8 +48,15 @@ module Middleman
Contract Or[Symbol, ArrayOf[Symbol]], Maybe[ArrayOf[Any]], Maybe[RespondTo[:instance_exec]] => Any
def execute(keys, args=[], scope=self)
- callbacks_for(keys).each { |b| scope.instance_exec(*args, &b) }
+ callbacks = callbacks_for(keys)
+ callbacks_count = callbacks.length + @subscribers.length
+
+ return if callbacks_count < 1
+
+ # ::Middleman::Util.instrument "callbacks.execute", keys: keys, length: callbacks_count do
+ callbacks.each { |b| scope.instance_exec(*args, &b) }
@subscribers.each { |b| scope.instance_exec(keys, args, &b) }
+ # end
end
Contract Or[Symbol, ArrayOf[Symbol]] => ::Hamster::Vector
diff --git a/middleman-core/lib/middleman-core/config_context.rb b/middleman-core/lib/middleman-core/config_context.rb
index 1f9f32f6..3ee98632 100644
--- a/middleman-core/lib/middleman-core/config_context.rb
+++ b/middleman-core/lib/middleman-core/config_context.rb
@@ -8,7 +8,7 @@ module Middleman
attr_reader :app
# Whitelist methods that can reach out.
- def_delegators :@app, :config, :logger, :use, :map, :mime_type, :files, :root, :build?, :server?, :environment?
+ def_delegators :@app, :config, :logger, :use, :map, :mime_type, :files, :root, :build?, :server?, :environment?, :extensions
def_delegator :"@app.extensions", :activate
def initialize(app, template_context_class)
@@ -22,6 +22,10 @@ module Middleman
app.subscribe_to_callbacks(&method(:execute_callbacks))
end
+ def include(mod)
+ extend(mod)
+ end
+
def helpers(*helper_modules, &block)
helper_modules ||= []
diff --git a/middleman-core/lib/middleman-core/configuration.rb b/middleman-core/lib/middleman-core/configuration.rb
index 3f889c00..c916fc90 100644
--- a/middleman-core/lib/middleman-core/configuration.rb
+++ b/middleman-core/lib/middleman-core/configuration.rb
@@ -40,7 +40,7 @@ module Middleman
# Allow configuration settings to be read and written via methods
def method_missing(method, *args)
- if defines_setting?(method) && args.size == 0
+ if defines_setting?(method) && args.empty?
self[method]
elsif method.to_s =~ /^(\w+)=$/ && args.size == 1
self[$1.to_sym] = args[0]
diff --git a/middleman-core/lib/middleman-core/contracts.rb b/middleman-core/lib/middleman-core/contracts.rb
index e99d0d59..55aa0aad 100644
--- a/middleman-core/lib/middleman-core/contracts.rb
+++ b/middleman-core/lib/middleman-core/contracts.rb
@@ -1,23 +1,112 @@
-require 'contracts'
-require 'hamster'
+if ENV['CONTRACTS'] != 'false'
+ require 'contracts'
+ require 'hamster'
-module Contracts
- class IsA
- def self.[](val)
- @lookup ||= {}
- @lookup[val] ||= new(val)
+ module Contracts
+ class IsA
+ def self.[](val)
+ @lookup ||= {}
+ @lookup[val] ||= new(val)
+ end
+
+ def initialize(val)
+ @val = val
+ end
+
+ def valid?(val)
+ val.is_a? @val.constantize
+ end
end
- def initialize(val)
- @val = val
+ VectorOf = Contracts::CollectionOf::Factory.new(::Hamster::Vector)
+ ResourceList = Contracts::ArrayOf[IsA['Middleman::Sitemap::Resource']]
+ end
+else
+ module Contracts
+ def self.included(base)
+ base.extend self
end
- def valid?(val)
- val.is_a? @val.constantize
+ # rubocop:disable MethodName
+ def Contract(*)
+ end
+
+ class Callable
+ def self.[](*)
+ end
+ end
+
+ class Bool
+ end
+
+ class Num
+ end
+
+ class Pos
+ end
+
+ class Neg
+ end
+
+ class Any
+ end
+
+ class None
+ end
+
+ class Or < Callable
+ end
+
+ class Xor < Callable
+ end
+
+ class And < Callable
+ end
+
+ class Not < Callable
+ end
+
+ class RespondTo < Callable
+ end
+
+ class Send < Callable
+ end
+
+ class Exactly < Callable
+ end
+
+ class ArrayOf < Callable
+ end
+
+ class ResourceList < Callable
+ end
+
+ class Args < Callable
+ end
+
+ class HashOf < Callable
+ end
+
+ class Bool
+ end
+
+ class Maybe < Callable
+ end
+
+ class IsA < Callable
+ end
+
+ class SetOf < Callable
+ end
+
+ class Frozen < Callable
+ end
+
+ class VectorOf < Callable
end
end
+end
- VectorOf = ::Contracts::CollectionOf::Factory.new(::Hamster::Vector)
- ResourceList = ::Contracts::ArrayOf[IsA['Middleman::Sitemap::Resource']]
+module Contracts
PATH_MATCHER = Or[String, RespondTo[:match], RespondTo[:call], RespondTo[:to_s]]
end
diff --git a/middleman-core/lib/middleman-core/core_extensions.rb b/middleman-core/lib/middleman-core/core_extensions.rb
index ea31f89a..e38be937 100644
--- a/middleman-core/lib/middleman-core/core_extensions.rb
+++ b/middleman-core/lib/middleman-core/core_extensions.rb
@@ -19,6 +19,12 @@ Middleman::Extensions.register :data, auto_activate: :before_sitemap do
Middleman::CoreExtensions::Data
end
+# Rewrite embedded URLs via Rack
+Middleman::Extensions.register :inline_url_rewriter, auto_activate: :before_sitemap do
+ require 'middleman-core/core_extensions/inline_url_rewriter'
+ Middleman::CoreExtensions::InlineURLRewriter
+end
+
# Catch and show exceptions at the Rack level
Middleman::Extensions.register :show_exceptions, auto_activate: :before_configuration, modes: [:server] do
require 'middleman-core/core_extensions/show_exceptions'
diff --git a/middleman-core/lib/middleman-core/core_extensions/collections.rb b/middleman-core/lib/middleman-core/core_extensions/collections.rb
index 434455f1..b915fffd 100644
--- a/middleman-core/lib/middleman-core/core_extensions/collections.rb
+++ b/middleman-core/lib/middleman-core/core_extensions/collections.rb
@@ -1,3 +1,4 @@
+require 'monitor'
require 'middleman-core/core_extensions/collections/pagination'
require 'middleman-core/core_extensions/collections/step_context'
require 'middleman-core/core_extensions/collections/lazy_root'
@@ -41,6 +42,8 @@ module Middleman
@values_by_name = {}
@collector_roots = []
+
+ @lock = Monitor.new
end
def before_configuration
@@ -81,27 +84,35 @@ module Middleman
Contract ResourceList => ResourceList
def manipulate_resource_list(resources)
- @collector_roots.each do |pair|
- dataset = pair[:block].call(app, resources)
- pair[:root].realize!(dataset)
- end
+ @lock.synchronize do
+ @collector_roots.each do |pair|
+ dataset = pair[:block].call(app, resources)
+ pair[:root].realize!(dataset)
+ end
- ctx = StepContext.new
- leaves = @leaves.dup
+ ctx = StepContext.new(app)
+ StepContext.current = ctx
- @collectors_by_name.each do |k, v|
- @values_by_name[k] = v.value(ctx)
- leaves.delete v
- end
+ leaves = @leaves.dup
- # Execute code paths
- leaves.each do |v|
- v.value(ctx)
- end
+ @collectors_by_name.each do |k, v|
+ @values_by_name[k] = v.value(ctx)
+ leaves.delete v
+ end
- # Inject descriptors
- ctx.descriptors.reduce(resources) do |sum, d|
- d.execute_descriptor(app, sum)
+ # Execute code paths
+ leaves.each do |v|
+ v.value(ctx)
+ end
+
+ # Inject descriptors
+ results = ctx.descriptors.reduce(resources) do |sum, d|
+ d.execute_descriptor(app, sum)
+ end
+
+ StepContext.current = nil
+
+ results
end
end
end
diff --git a/middleman-core/lib/middleman-core/core_extensions/collections/lazy_step.rb b/middleman-core/lib/middleman-core/core_extensions/collections/lazy_step.rb
index 1286af2c..b316b3e3 100644
--- a/middleman-core/lib/middleman-core/core_extensions/collections/lazy_step.rb
+++ b/middleman-core/lib/middleman-core/core_extensions/collections/lazy_step.rb
@@ -2,7 +2,7 @@ module Middleman
module CoreExtensions
module Collections
class LazyCollectorStep < BasicObject
- DELEGATE = [:hash, :eql?]
+ DELEGATE = [:hash, :eql?].freeze
def initialize(name, args, block, parent=nil)
@name = name
diff --git a/middleman-core/lib/middleman-core/core_extensions/collections/step_context.rb b/middleman-core/lib/middleman-core/core_extensions/collections/step_context.rb
index 2ce90b83..0549b826 100644
--- a/middleman-core/lib/middleman-core/core_extensions/collections/step_context.rb
+++ b/middleman-core/lib/middleman-core/core_extensions/collections/step_context.rb
@@ -2,23 +2,30 @@ module Middleman
module CoreExtensions
module Collections
class StepContext
- def self.add_to_context(name, &func)
- send(:define_method, :"_internal_#{name}", &func)
+ class << self
+ attr_accessor :current
+
+ def add_to_context(name, &func)
+ send(:define_method, :"_internal_#{name}", &func)
+ end
end
attr_reader :descriptors
- def initialize
+ def initialize(app)
+ @app = app
@descriptors = []
end
def method_missing(name, *args, &block)
internal = :"_internal_#{name}"
- return super unless respond_to?(internal)
-
- send(internal, *args, &block).tap do |r|
- @descriptors << r if r.respond_to?(:execute_descriptor)
+ if respond_to?(internal)
+ send(internal, *args, &block).tap do |r|
+ @descriptors << r if r.respond_to?(:execute_descriptor)
+ end
+ else
+ @app.config_context.send(name, *args, &block)
end
end
end
diff --git a/middleman-core/lib/middleman-core/core_extensions/data.rb b/middleman-core/lib/middleman-core/core_extensions/data.rb
index 22d571be..d711ef4f 100644
--- a/middleman-core/lib/middleman-core/core_extensions/data.rb
+++ b/middleman-core/lib/middleman-core/core_extensions/data.rb
@@ -8,7 +8,7 @@ module Middleman
class Data < Extension
attr_reader :data_store
- define_setting :data_dir, 'data', 'The directory data files are stored in'
+ define_setting :data_dir, ENV['MM_DATA_DIR'] || 'data', 'The directory data files are stored in'
# Make the internal `data_store` method available as `app.data`
expose_to_application data: :data_store
@@ -57,6 +57,7 @@ module Middleman
@app = app
@data_file_matcher = data_file_matcher
@local_data = {}
+ @local_data_enhanced = nil
@local_sources = {}
@callback_sources = {}
end
@@ -66,7 +67,7 @@ module Middleman
# @param [Symbol] name Name of the data, used for namespacing
# @param [Hash] content The content for this data
# @return [Hash]
- Contract Symbol, Hash => Hash
+ Contract Symbol, Or[Hash, Array] => Hash
def store(name=nil, content=nil)
@local_sources[name.to_s] = content unless name.nil? || content.nil?
@local_sources
@@ -87,6 +88,8 @@ module Middleman
def update_files(updated_files, removed_files)
updated_files.each(&method(:touch_file))
removed_files.each(&method(:remove_file))
+
+ @app.sitemap.rebuild_resource_list!(:touched_data_file)
end
# Update the internal cache for a given file path
@@ -99,13 +102,13 @@ module Middleman
extension = File.extname(data_path)
basename = File.basename(data_path, extension)
+ return unless %w(.yaml .yml .json).include?(extension)
+
if %w(.yaml .yml).include?(extension)
- data, postscript = ::Middleman::Util::Data.parse(file[:full_path], @app.config[:frontmatter_delims], :yaml)
+ data, postscript = ::Middleman::Util::Data.parse(file, @app.config[:frontmatter_delims], :yaml)
data[:postscript] = postscript if !postscript.nil? && data.is_a?(Hash)
elsif extension == '.json'
- data, _postscript = ::Middleman::Util::Data.parse(file[:full_path], @app.config[:frontmatter_delims], :json)
- else
- return
+ data, _postscript = ::Middleman::Util::Data.parse(file, @app.config[:frontmatter_delims], :json)
end
data_branch = @local_data
@@ -117,6 +120,8 @@ module Middleman
end
data_branch[basename] = data
+
+ @local_data_enhanced = nil
end
# Remove a given file from the internal cache
@@ -137,13 +142,15 @@ module Middleman
end
data_branch.delete(basename) if data_branch.key?(basename)
+
+ @local_data_enhanced = nil
end
# Get a hash from either internal static data or a callback
#
# @param [String, Symbol] path The name of the data namespace
# @return [Hash, nil]
- Contract Or[String, Symbol] => Maybe[Hash]
+ Contract Or[String, Symbol] => Maybe[Or[Array, IsA['Middleman::Util::EnhancedHash']]]
def data_for_path(path)
response = if store.key?(path.to_s)
store[path.to_s]
@@ -151,8 +158,7 @@ module Middleman
callbacks[path.to_s].call
end
- response = ::Middleman::Util.recursively_enhance(response)
- response
+ ::Middleman::Util.recursively_enhance(response)
end
# "Magically" find namespaces of data if they exist
@@ -162,7 +168,8 @@ module Middleman
def method_missing(path)
if @local_data.key?(path.to_s)
# Any way to cache this?
- return ::Middleman::Util.recursively_enhance(@local_data[path.to_s])
+ @local_data_enhanced ||= ::Middleman::Util.recursively_enhance(@local_data)
+ return @local_data_enhanced[path.to_s]
else
result = data_for_path(path)
return result if result
@@ -189,7 +196,7 @@ module Middleman
(@local_data.keys + @local_sources.keys + @callback_sources.keys).include?(key.to_s)
end
- alias_method :has_key?, :key?
+ alias has_key? key?
# Convert all the data into a static hash
#
@@ -198,11 +205,11 @@ module Middleman
def to_h
data = {}
- store.each do |k, _|
+ store.each_key do |k|
data[k] = data_for_path(k)
end
- callbacks.each do |k, _|
+ callbacks.each_key do |k|
data[k] = data_for_path(k)
end
diff --git a/middleman-core/lib/middleman-core/core_extensions/default_helpers.rb b/middleman-core/lib/middleman-core/core_extensions/default_helpers.rb
index be6371c1..774522db 100644
--- a/middleman-core/lib/middleman-core/core_extensions/default_helpers.rb
+++ b/middleman-core/lib/middleman-core/core_extensions/default_helpers.rb
@@ -1,4 +1,5 @@
require 'padrino-helpers'
+require 'middleman-core/contracts'
# Don't fail on invalid locale, that's not what our current
# users expect.
@@ -9,7 +10,7 @@ class Padrino::Helpers::OutputHelpers::ErbHandler
def capture_from_template(*args, &block)
self.output_buffer = ''
buf_was = output_buffer
- raw = block.call(*args)
+ raw = yield(*args)
captured = template.instance_variable_get(:@_out_buf)
self.output_buffer = buf_was
engine_matches?(block) && !captured.empty? ? captured : raw
@@ -55,7 +56,7 @@ class Middleman::CoreExtensions::DefaultHelpers < ::Middleman::Extension
output.safe_concat ::Padrino::Helpers::TagHelpers::NEWLINE
end
else
- output.safe_concat "#{content}"
+ output.safe_concat content.to_s
end
output.safe_concat "#{name}>"
@@ -66,7 +67,7 @@ class Middleman::CoreExtensions::DefaultHelpers < ::Middleman::Extension
result = if handler = auto_find_proper_handler(&block)
handler.capture_from_template(*args, &block)
else
- block.call(*args)
+ yield(*args)
end
::ActiveSupport::SafeBuffer.new.safe_concat(result)
@@ -113,7 +114,7 @@ class Middleman::CoreExtensions::DefaultHelpers < ::Middleman::Extension
path_options = {}
path_options[:relative] = options.delete(:relative) if options.key?(:relative)
- sources.flatten.inject(::ActiveSupport::SafeBuffer.new) do |all, source|
+ sources.flatten.reduce(::ActiveSupport::SafeBuffer.new) do |all, source|
all << tag(:link, {
href: asset_path(:css, source, path_options)
}.update(options))
@@ -127,7 +128,7 @@ class Middleman::CoreExtensions::DefaultHelpers < ::Middleman::Extension
path_options = {}
path_options[:relative] = options.delete(:relative) if options.key?(:relative)
- sources.flatten.inject(::ActiveSupport::SafeBuffer.new) do |all, source|
+ sources.flatten.reduce(::ActiveSupport::SafeBuffer.new) do |all, source|
all << content_tag(:script, nil, {
src: asset_path(:js, source, path_options)
}.update(options))
@@ -152,7 +153,7 @@ class Middleman::CoreExtensions::DefaultHelpers < ::Middleman::Extension
# If the basename of the request as no extension, assume we are serving a
# directory and join index_file to the path.
path = File.join(asset_dir, current_resource.path)
- path = path.sub(/#{Regexp.escape(File.extname(path))}$/, ".#{asset_ext}")
+ path = path[0..-(File.extname(path).length + 1)] + ".#{asset_ext}"
yield path if sitemap.find_resource_by_path(path)
end
@@ -191,7 +192,7 @@ class Middleman::CoreExtensions::DefaultHelpers < ::Middleman::Extension
# @param [Hash] options Data to pass through.
# @return [String]
def asset_path(kind, source, options={})
- options_with_resource = options.merge(current_resource: current_resource)
+ options_with_resource = {}.merge!(options).merge!(current_resource: current_resource)
::Middleman::Util.asset_path(app, kind, source, options_with_resource)
end
@@ -202,7 +203,7 @@ class Middleman::CoreExtensions::DefaultHelpers < ::Middleman::Extension
# @param [Hash] options Additional options.
# @return [String] The fully qualified asset url
def asset_url(path, prefix='', options={})
- options_with_resource = options.merge(current_resource: current_resource)
+ options_with_resource = {}.merge!(options).merge!(current_resource: current_resource)
::Middleman::Util.asset_url(app, path, prefix, options_with_resource)
end
@@ -210,7 +211,7 @@ class Middleman::CoreExtensions::DefaultHelpers < ::Middleman::Extension
# or a Resource, this will produce the nice URL configured for that
# path, respecting :relative_links, directory indexes, etc.
def url_for(path_or_resource, options={})
- options_with_resource = options.merge(current_resource: current_resource)
+ options_with_resource = {}.merge!(options).merge!(current_resource: current_resource)
::Middleman::Util.url_for(app, path_or_resource, options_with_resource)
end
@@ -276,5 +277,14 @@ class Middleman::CoreExtensions::DefaultHelpers < ::Middleman::Extension
super(path, params)
end
+
+ def partial(template, options={}, &block)
+ including_parent_locals = {}
+ including_parent_locals.merge!(@locs || {})
+ including_parent_locals.merge!(options[:locals] || {})
+
+ options[:locals] = including_parent_locals
+ super(template, options, &block)
+ end
end
end
diff --git a/middleman-core/lib/middleman-core/core_extensions/file_watcher.rb b/middleman-core/lib/middleman-core/core_extensions/file_watcher.rb
index 617e0ac7..6fd16ae9 100644
--- a/middleman-core/lib/middleman-core/core_extensions/file_watcher.rb
+++ b/middleman-core/lib/middleman-core/core_extensions/file_watcher.rb
@@ -21,17 +21,14 @@ module Middleman
tilde_files: /~$/,
ds_store: /\.DS_Store$/,
git: /(^|\/)\.git(ignore|modules|\/)/
- }
+ }.freeze
# Setup the extension.
def initialize(app, config={}, &block)
super
# Setup source collection.
- @sources = ::Middleman::Sources.new(app,
- disable_watcher: app.config[:watcher_disable],
- force_polling: app.config[:watcher_force_polling],
- latency: app.config[:watcher_latency])
+ @sources = ::Middleman::Sources.new(app)
# Add default ignores.
IGNORES.each do |key, value|
@@ -47,7 +44,7 @@ module Middleman
# @return [void]
Contract Any
def before_configuration
- @sources.find_new_files!
+ @sources.poll_once!
end
# After we config, find new files since config can change paths.
@@ -55,12 +52,19 @@ module Middleman
# @return [void]
Contract Any
def after_configuration
+ @watcher.update_config(
+ disable_watcher: app.config[:watcher_disable],
+ force_polling: app.config[:watcher_force_polling],
+ latency: app.config[:watcher_latency],
+ wait_for_delay: app.config[:watcher_wait_for_delay]
+ )
+
if @original_source_dir != app.config[:source]
@watcher.update_path(app.config[:source])
end
@sources.start!
- @sources.find_new_files!
+ @sources.poll_once!
end
protected
diff --git a/middleman-core/lib/middleman-core/core_extensions/front_matter.rb b/middleman-core/lib/middleman-core/core_extensions/front_matter.rb
index 739b49fd..0f69c634 100644
--- a/middleman-core/lib/middleman-core/core_extensions/front_matter.rb
+++ b/middleman-core/lib/middleman-core/core_extensions/front_matter.rb
@@ -14,6 +14,12 @@ module Middleman::CoreExtensions
# Try to run after routing but before directory_indexes
self.resource_list_manipulator_priority = 20
+ # Set textual delimiters that denote the start and end of frontmatter
+ define_setting :frontmatter_delims, {
+ json: [%w(;;; ;;;)],
+ yaml: [%w(--- ---), %w(--- ...)]
+ }, 'Allowed frontmatter delimiters'
+
def initialize(app, options_hash={}, &block)
super
@@ -28,8 +34,9 @@ module Middleman::CoreExtensions
Contract ResourceList => ResourceList
def manipulate_resource_list(resources)
resources.each do |resource|
- next if resource.ignored?
+ next if resource.binary?
next if resource.file_descriptor.nil?
+ next if resource.file_descriptor[:types].include?(:no_frontmatter)
fmdata = data(resource.file_descriptor[:full_path].to_s).first.dup
@@ -66,16 +73,20 @@ module Middleman::CoreExtensions
return [{}, nil] unless file
- @cache[file[:full_path]] ||= ::Middleman::Util::Data.parse(
- file[:full_path],
- app.config[:frontmatter_delims]
- )
+ file_path = file[:full_path].to_s
+
+ @cache[file_path] ||= begin
+ ::Middleman::Util::Data.parse(
+ file,
+ app.config[:frontmatter_delims]
+ )
+ end
end
Contract ArrayOf[IsA['Middleman::SourceFile']], ArrayOf[IsA['Middleman::SourceFile']] => Any
def clear_data(updated_files, removed_files)
(updated_files + removed_files).each do |file|
- @cache.delete(file[:full_path])
+ @cache.delete(file[:full_path].to_s)
end
end
end
diff --git a/middleman-core/lib/middleman-core/core_extensions/i18n.rb b/middleman-core/lib/middleman-core/core_extensions/i18n.rb
index ce16ab48..156f2694 100644
--- a/middleman-core/lib/middleman-core/core_extensions/i18n.rb
+++ b/middleman-core/lib/middleman-core/core_extensions/i18n.rb
@@ -102,7 +102,7 @@ class Middleman::CoreExtensions::Internationalization < ::Middleman::Extension
locale_suffix = ::I18n.locale
extname = File.extname(partial_name)
- maybe_static = extname.length > 0
+ maybe_static = !extname.empty?
suffixed_partial_name = if maybe_static
partial_name.sub(extname, ".#{locale_suffix}#{extname}")
else
@@ -127,7 +127,7 @@ class Middleman::CoreExtensions::Internationalization < ::Middleman::Extension
end
# Backwards API compat
- alias_method :langs, :locales
+ alias langs locales
Contract Symbol
def locale
@@ -135,7 +135,7 @@ class Middleman::CoreExtensions::Internationalization < ::Middleman::Extension
end
# Backwards API compat
- alias_method :lang, :locale
+ alias lang locale
# Update the main sitemap resource list
# @return Array
@@ -190,7 +190,7 @@ class Middleman::CoreExtensions::Internationalization < ::Middleman::Extension
end
end
- Contract String, Symbol => String
+ Contract String, Symbol => Maybe[String]
def localized_path(path, locale)
lookup_path = path.dup
lookup_path << app.config[:index_file] if lookup_path.end_with?('/')
@@ -203,7 +203,7 @@ class Middleman::CoreExtensions::Internationalization < ::Middleman::Extension
if (options[:mount_at_root] == locale) || (options[:mount_at_root].nil? && locales[0] == locale)
'/'
else
- replacement = options[:locale_map].fetch(locale, locale)
+ replacement = options[:locale_map][locale] || locale
options[:path].sub(':locale', replacement.to_s).sub(':lang', replacement.to_s) # Backward compat
end
end
@@ -213,6 +213,8 @@ class Middleman::CoreExtensions::Internationalization < ::Middleman::Extension
def on_file_changed(_updated_files, _removed_files)
::I18n.load_path |= app.files.by_type(:locales).files.map { |p| p[:full_path].to_s }
::I18n.reload!
+
+ @app.sitemap.rebuild_resource_list!(:touched_locale_file)
end
def configure_i18n
@@ -274,7 +276,7 @@ class Middleman::CoreExtensions::Internationalization < ::Middleman::Extension
File.dirname(path).split('/').each do |path_sub|
next if path_sub == ''
- partially_localized_path = "#{partially_localized_path}/#{(::I18n.t("paths.#{path_sub}", default: path_sub).to_s)}"
+ partially_localized_path = "#{partially_localized_path}/#{::I18n.t("paths.#{path_sub}", default: path_sub)}"
end
path = "#{partially_localized_path}/#{File.basename(path)}"
diff --git a/middleman-core/lib/middleman-core/core_extensions/inline_url_rewriter.rb b/middleman-core/lib/middleman-core/core_extensions/inline_url_rewriter.rb
new file mode 100644
index 00000000..43354e97
--- /dev/null
+++ b/middleman-core/lib/middleman-core/core_extensions/inline_url_rewriter.rb
@@ -0,0 +1,135 @@
+require 'rack'
+require 'rack/response'
+require 'memoist'
+require 'middleman-core/util'
+require 'middleman-core/contracts'
+
+module Middleman
+ module CoreExtensions
+ class InlineURLRewriter < ::Middleman::Extension
+ include Contracts
+
+ expose_to_application rewrite_inline_urls: :add
+
+ REWRITER_DESCRIPTOR = {
+ id: Symbol,
+ proc: Or[Proc, Method],
+ url_extensions: ArrayOf[String],
+ source_extensions: ArrayOf[String],
+ ignore: ArrayOf[::Middleman::Util::IGNORE_DESCRIPTOR],
+ after: Maybe[Symbol]
+ }.freeze
+
+ def initialize(app, options_hash={}, &block)
+ super
+
+ @rewriters = {}
+ end
+
+ Contract REWRITER_DESCRIPTOR => Any
+ def add(options)
+ @rewriters[options] = options
+ end
+
+ def after_configuration
+ return if @rewriters.empty?
+
+ rewriters = @rewriters.values.sort do |a, b|
+ if b[:after] && b[:after] == a[:id]
+ 1
+ else
+ 0
+ end
+ end
+
+ app.use Rack, rewriters: rewriters, middleman_app: @app
+ end
+
+ class Rack
+ extend Memoist
+ include Contracts
+
+ Contract RespondTo[:call], {
+ middleman_app: IsA['Middleman::Application'],
+ rewriters: ArrayOf[REWRITER_DESCRIPTOR]
+ } => Any
+ def initialize(app, options={})
+ @rack_app = app
+ @middleman_app = options.fetch(:middleman_app)
+ @rewriters = options.fetch(:rewriters)
+
+ all_source_exts = @rewriters
+ .reduce([]) { |sum, rewriter| sum + rewriter[:source_extensions] }
+ .flatten
+ .uniq
+ @source_exts_regex_text = Regexp.union(all_source_exts).to_s
+
+ @all_asset_exts = @rewriters
+ .reduce([]) { |sum, rewriter| sum + rewriter[:url_extensions] }
+ .flatten
+ .uniq
+ end
+
+ def call(env)
+ status, headers, response = @rack_app.call(env)
+
+ # Allow configuration or upstream request to skip all rewriting
+ return [status, headers, response] if env['bypass_inline_url_rewriter'] == 'true'
+
+ path = ::Middleman::Util.full_path(env['PATH_INFO'], @middleman_app)
+
+ return [status, headers, response] unless path =~ /(^\/$)|(#{@source_exts_regex_text}$)/
+ return [status, headers, response] unless body = ::Middleman::Util.extract_response_text(response)
+
+ dirpath = ::Pathname.new(File.dirname(path))
+
+ rewritten = ::Middleman::Util.instrument 'inline_url_rewriter', path: path do
+ ::Middleman::Util.rewrite_paths(body, path, @all_asset_exts, @middleman_app) do |asset_path|
+ uri = ::Middleman::Util.parse_uri(asset_path)
+
+ relative_path = uri.host.nil?
+
+ full_asset_path = if relative_path
+ dirpath.join(asset_path).to_s
+ else
+ asset_path
+ end
+
+ @rewriters.each do |rewriter|
+ uid = rewriter.fetch(:id)
+
+ # Allow upstream request to skip this specific rewriting
+ next if env["bypass_inline_url_rewriter_#{uid}"] == 'true'
+
+ exts = rewriter.fetch(:url_extensions)
+ next unless exts.include?(::File.extname(asset_path))
+
+ source_exts = rewriter.fetch(:source_extensions)
+ next unless source_exts.include?(::File.extname(path))
+
+ ignore = rewriter.fetch(:ignore)
+ next if ignore.any? { |r| ::Middleman::Util.should_ignore?(r, full_asset_path) }
+
+ rewrite_ignore = Array(rewriter[:rewrite_ignore] || [])
+ next if rewrite_ignore.any? { |i| ::Middleman::Util.path_match(i, path) }
+
+ proc = rewriter.fetch(:proc)
+
+ result = proc.call(asset_path, dirpath, path)
+ asset_path = result if result
+ end
+
+ asset_path
+ end
+ end
+
+ ::Rack::Response.new(
+ rewritten,
+ status,
+ headers
+ ).finish
+ end
+ end
+ end
+ end
+end
diff --git a/middleman-core/lib/middleman-core/core_extensions/routing.rb b/middleman-core/lib/middleman-core/core_extensions/routing.rb
index c5655dfe..f47ac16b 100644
--- a/middleman-core/lib/middleman-core/core_extensions/routing.rb
+++ b/middleman-core/lib/middleman-core/core_extensions/routing.rb
@@ -4,7 +4,7 @@ module Middleman
class Routing < ConfigExtension
# This should always run late, but not as late as :directory_indexes,
# so it can add metadata to any pages generated by other extensions
- self.resource_list_manipulator_priority = 10
+ self.resource_list_manipulator_priority = [10, 130]
# Expose the `page` method to config.
expose_to_config :page
@@ -24,8 +24,8 @@ module Middleman
normalized_path = '/' + ::Middleman::Util.strip_leading_slash(normalized_path) if normalized_path.is_a?(String)
resources
- .select { |r| ::Middleman::Util.path_match(normalized_path, "/#{r.path}") }
- .each { |r| r.add_metadata(metadata) }
+ .select { |r| ::Middleman::Util.path_match(normalized_path, "/#{r.path}") }
+ .each { |r| r.add_metadata(metadata, true) }
resources
end
@@ -52,10 +52,13 @@ module Middleman
def page(path, opts={})
options = opts.dup
+ page_data = options.delete(:data) || {}
+ page_data[:id] = options.delete(:id) if options.key?(:id)
+
# Default layout
metadata = {
locals: options.delete(:locals) || {},
- page: options.delete(:data) || {},
+ page: page_data,
options: options
}
diff --git a/middleman-core/lib/middleman-core/extension.rb b/middleman-core/lib/middleman-core/extension.rb
index a94a9e12..9805455a 100644
--- a/middleman-core/lib/middleman-core/extension.rb
+++ b/middleman-core/lib/middleman-core/extension.rb
@@ -1,4 +1,5 @@
require 'forwardable'
+require 'memoist'
require 'active_support/core_ext/class/attribute'
require 'middleman-core/configuration'
require 'middleman-core/contracts'
@@ -66,6 +67,8 @@ module Middleman
# @see http://middlemanapp.com/advanced/custom/ Middleman Custom Extensions Documentation
class Extension
extend Forwardable
+ extend Memoist
+
include Contracts
def_delegator :@app, :logger
@@ -402,11 +405,11 @@ module Middleman
ext.after_configuration if ext.respond_to?(:after_configuration)
if ext.respond_to?(:manipulate_resource_list)
- ext.app.sitemap.register_resource_list_manipulator(ext.class.ext_name, ext, ext.class.resource_list_manipulator_priority)
+ ext.app.sitemap.register_resource_list_manipulators(ext.class.ext_name, ext, ext.class.resource_list_manipulator_priority)
end
if ext.class.resources_generators && !ext.class.resources_generators.empty?
- ext.app.sitemap.register_resource_list_manipulator(
+ ext.app.sitemap.register_resource_list_manipulators(
:"#{ext.class.ext_name}_generator",
ext,
ext.class.resource_list_manipulator_priority,
@@ -432,7 +435,7 @@ module Middleman
{}
end
- sum.merge(resource_definitions)
+ sum.merge!(resource_definitions)
end
resources + generator_defs.map do |path, g|
@@ -490,15 +493,14 @@ module Middleman
class ConfigExtension < Extension
def initialize(app, config={}, &block)
@descriptors = {}
- @wrapped = {}
+ @ready = false
self.class.exposed_to_config.each do |k, v|
@descriptors[k] = []
define_singleton_method(:"__original_#{v}", &method(v))
- define_singleton_method(v) do |*args, &block|
- @descriptors[k] << method(:"__original_#{v}").call(*args, &block)
- @app.sitemap.rebuild_resource_list!(:"first_run_change_#{v}")
+ define_singleton_method(v) do |*args, &b|
+ proxy_method_call(k, v, args, &b)
end
end
@@ -506,11 +508,24 @@ module Middleman
end
def after_configuration
+ context = self
+
self.class.exposed_to_config.each do |k, v|
- ::Middleman::CoreExtensions::Collections::StepContext.add_to_context(k, &method(:"__original_#{v}"))
+ ::Middleman::CoreExtensions::Collections::StepContext.add_to_context(k) do |*args, &b|
+ r = context.method(:"__original_#{v}").call(*args, &b)
+ descriptors << r if r.respond_to?(:execute_descriptor)
+ end
end
end
+ def ready
+ @ready = true
+
+ # @descriptors.each do |k, v|
+ # @descriptors[k] = []
+ # end
+ end
+
# Update the main sitemap resource list
# @return Array
Contract ResourceList => ResourceList
@@ -519,5 +534,25 @@ module Middleman
c.execute_descriptor(app, sum)
end
end
+
+ Contract Symbol, Symbol, ArrayOf[Any], Maybe[Proc] => Any
+ def proxy_method_call(k, v, args, &b)
+ if @ready
+ ctx = ::Middleman::CoreExtensions::Collections::StepContext.current
+ r = method(:"__original_#{v}").call(*args, &b)
+
+ if r.respond_to?(:execute_descriptor)
+ if ctx
+ ctx.descriptors << r
+ else
+ @descriptors[k] << r
+ @app.sitemap.rebuild_resource_list!(:"first_run_change_#{v}")
+ end
+ end
+ else
+ @descriptors[k] << method(:"__original_#{v}").call(*args, &b)
+ @app.sitemap.rebuild_resource_list!(:"first_run_change_#{v}")
+ end
+ end
end
end
diff --git a/middleman-core/lib/middleman-core/extensions.rb b/middleman-core/lib/middleman-core/extensions.rb
index c295efe3..001b4344 100644
--- a/middleman-core/lib/middleman-core/extensions.rb
+++ b/middleman-core/lib/middleman-core/extensions.rb
@@ -52,7 +52,7 @@ module Middleman
def register(name, extension_class=nil, options={}, &block)
raise 'Extension name must be a symbol' unless name.is_a?(Symbol)
# If we've already got an extension registered under this name, bail out
- raise "There is already an extension registered with the name '#{name}'" if registered.key?(name)
+ # raise "There is a already an extension registered with the name '#{name}'" if registered.key?(name)
# If the extension is defined with a block, grab options out of the "extension_class" parameter.
if extension_class && block_given? && options.empty? && extension_class.is_a?(Hash)
diff --git a/middleman-core/lib/middleman-core/extensions/asset_hash.rb b/middleman-core/lib/middleman-core/extensions/asset_hash.rb
index da84a23d..3e2a02aa 100644
--- a/middleman-core/lib/middleman-core/extensions/asset_hash.rb
+++ b/middleman-core/lib/middleman-core/extensions/asset_hash.rb
@@ -1,38 +1,40 @@
-require 'addressable/uri'
require 'middleman-core/util'
require 'middleman-core/rack'
class Middleman::Extensions::AssetHash < ::Middleman::Extension
- option :exts, %w(.jpg .jpeg .png .gif .webp .js .css .otf .woff .woff2 .eot .ttf .svg .svgz), 'List of extensions that get asset hashes appended to them.'
+ option :sources, %w(.css .htm .html .js .php .xhtml), 'List of extensions that are searched for hashable assets.'
+ option :exts, nil, 'List of extensions that get asset hashes appended to them.'
option :ignore, [], 'Regexes of filenames to skip adding asset hashes to'
option :rewrite_ignore, [], 'Regexes of filenames to skip processing for path rewrites'
+ option :prefix, '', 'Prefix for hash'
def initialize(app, options_hash={}, &block)
super
+ require 'addressable/uri'
require 'digest/sha1'
require 'rack/mock'
- require 'middleman-core/middleware/inline_url_rewriter'
- end
- def after_configuration
# Allow specifying regexes to ignore, plus always ignore apple touch icons
@ignore = Array(options.ignore) + [/^apple-touch-icon/]
- app.use ::Middleman::Middleware::InlineURLRewriter,
- id: :asset_hash,
- url_extensions: options.exts.sort.reverse,
- source_extensions: %w(.htm .html .php .css .js),
- ignore: @ignore,
- rewrite_ignore: options.rewrite_ignore,
- middleman_app: app,
- proc: method(:rewrite_url)
+ # Exclude .ico from the default list because browsers expect it
+ # to be named "favicon.ico"
+ @exts = options.exts || (app.config[:asset_extensions] - %w(.ico))
+
+ app.rewrite_inline_urls id: :asset_hash,
+ url_extensions: @exts.sort.reverse,
+ source_extensions: options.sources,
+ ignore: @ignore,
+ rewrite_ignore: options.rewrite_ignore,
+ proc: method(:rewrite_url),
+ after: :asset_host
end
Contract String, Or[String, Pathname], Any => Maybe[String]
def rewrite_url(asset_path, dirpath, _request_path)
- uri = ::Addressable::URI.parse(asset_path)
- relative_path = uri.path[0..0] != '/'
+ uri = ::Middleman::Util.parse_uri(asset_path)
+ relative_path = !uri.path.start_with?('/')
full_asset_path = if relative_path
dirpath.join(asset_path).to_s
@@ -73,21 +75,25 @@ class Middleman::Extensions::AssetHash < ::Middleman::Extension
Contract IsA['Middleman::Sitemap::Resource'] => Maybe[IsA['Middleman::Sitemap::Resource']]
def manipulate_single_resource(resource)
- return unless options.exts.include?(resource.ext)
+ return unless @exts.include?(resource.ext)
return if ignored_resource?(resource)
return if resource.ignored?
- # Render through the Rack interface so middleware and mounted apps get a shot
- response = @rack_client.get(
- URI.escape(resource.destination_path),
- 'bypass_inline_url_rewriter_asset_hash' => 'true'
- )
+ digest = if resource.binary?
+ ::Digest::SHA1.file(resource.source_file).hexdigest[0..7]
+ else
+ # Render through the Rack interface so middleware and mounted apps get a shot
+ response = @rack_client.get(
+ ::URI.escape(resource.destination_path),
+ 'bypass_inline_url_rewriter_asset_hash' => 'true'
+ )
- raise "#{resource.path} should be in the sitemap!" unless response.status == 200
+ raise "#{resource.path} should be in the sitemap!" unless response.status == 200
- digest = Digest::SHA1.hexdigest(response.body)[0..7]
+ ::Digest::SHA1.hexdigest(response.body)[0..7]
+ end
- resource.destination_path = resource.destination_path.sub(/\.(\w+)$/) { |ext| "-#{digest}#{ext}" }
+ resource.destination_path = resource.destination_path.sub(/\.(\w+)$/) { |ext| "-#{options.prefix}#{digest}#{ext}" }
resource
end
diff --git a/middleman-core/lib/middleman-core/extensions/asset_host.rb b/middleman-core/lib/middleman-core/extensions/asset_host.rb
index 05b4509f..7cb83d07 100644
--- a/middleman-core/lib/middleman-core/extensions/asset_host.rb
+++ b/middleman-core/lib/middleman-core/extensions/asset_host.rb
@@ -1,27 +1,26 @@
require 'addressable/uri'
-require 'middleman-core/middleware/inline_url_rewriter'
class Middleman::Extensions::AssetHost < ::Middleman::Extension
option :host, nil, 'The asset host to use or a Proc to determine asset host', required: true
- option :exts, %w(.css .png .jpg .jpeg .webp .svg .svgz .js .gif), 'List of extensions that get cache busters strings appended to them.'
- option :sources, %w(.htm .html .php .css .js), 'List of extensions that are searched for bustable assets.'
+ option :exts, nil, 'List of extensions that get cache busters strings appended to them.'
+ option :sources, %w(.css .htm .html .js .php .xhtml), 'List of extensions that are searched for bustable assets.'
option :ignore, [], 'Regexes of filenames to skip adding query strings to'
option :rewrite_ignore, [], 'Regexes of filenames to skip processing for host rewrites'
- def ready
- app.use ::Middleman::Middleware::InlineURLRewriter,
- id: :asset_host,
- url_extensions: options.exts,
- source_extensions: options.sources,
- ignore: options.ignore,
- rewrite_ignore: options.rewrite_ignore,
- middleman_app: app,
- proc: method(:rewrite_url)
+ def initialize(app, options_hash={}, &block)
+ super
+
+ app.rewrite_inline_urls id: :asset_host,
+ url_extensions: options.exts || app.config[:asset_extensions],
+ source_extensions: options.sources,
+ ignore: options.ignore,
+ rewrite_ignore: options.rewrite_ignore,
+ proc: method(:rewrite_url)
end
Contract String, Or[String, Pathname], Any => String
def rewrite_url(asset_path, dirpath, _request_path)
- uri = ::Addressable::URI.parse(asset_path)
+ uri = ::Middleman::Util.parse_uri(asset_path)
relative_path = uri.path[0..0] != '/'
full_asset_path = if relative_path
@@ -38,4 +37,5 @@ class Middleman::Extensions::AssetHost < ::Middleman::Extension
File.join(asset_prefix, full_asset_path)
end
+ memoize :rewrite_url
end
diff --git a/middleman-core/lib/middleman-core/extensions/automatic_image_sizes.rb b/middleman-core/lib/middleman-core/extensions/automatic_image_sizes.rb
index b88721de..5d12501f 100644
--- a/middleman-core/lib/middleman-core/extensions/automatic_image_sizes.rb
+++ b/middleman-core/lib/middleman-core/extensions/automatic_image_sizes.rb
@@ -19,7 +19,7 @@ class Middleman::Extensions::AutomaticImageSizes < ::Middleman::Extension
real_path = path.dup
real_path = File.join(config[:images_dir], real_path) unless real_path.start_with?('/')
- file = app.files.find(:source, real_path)
+ file = app.files.find(:source, real_path) || app.files.find(:source, real_path.sub(/^\//, ''))
if file && file[:full_path].exist?
begin
diff --git a/middleman-core/lib/middleman-core/extensions/cache_buster.rb b/middleman-core/lib/middleman-core/extensions/cache_buster.rb
index 8f1ceb64..2de11632 100644
--- a/middleman-core/lib/middleman-core/extensions/cache_buster.rb
+++ b/middleman-core/lib/middleman-core/extensions/cache_buster.rb
@@ -1,25 +1,19 @@
# The Cache Buster extension
class Middleman::Extensions::CacheBuster < ::Middleman::Extension
- option :exts, %w(.css .png .jpg .jpeg .webp .svg .svgz .js .gif), 'List of extensions that get cache busters strings appended to them.'
- option :sources, %w(.htm .html .php .css .js), 'List of extensions that are searched for bustable assets.'
+ option :exts, nil, 'List of extensions that get cache busters strings appended to them.'
+ option :sources, %w(.css .htm .html .js .php .xhtml), 'List of extensions that are searched for bustable assets.'
option :ignore, [], 'Regexes of filenames to skip adding query strings to'
option :rewrite_ignore, [], 'Regexes of filenames to skip processing for path rewrites'
def initialize(app, options_hash={}, &block)
super
- require 'middleman-core/middleware/inline_url_rewriter'
- end
-
- def after_configuration
- app.use ::Middleman::Middleware::InlineURLRewriter,
- id: :cache_buster,
- url_extensions: options.exts,
- source_extensions: options.sources,
- ignore: options.ignore,
- rewrite_ignore: options.rewrite_ignore,
- middleman_app: app,
- proc: method(:rewrite_url)
+ app.rewrite_inline_urls id: :cache_buster,
+ url_extensions: options.exts || app.config[:asset_extensions],
+ source_extensions: options.sources,
+ ignore: options.ignore,
+ rewrite_ignore: options.rewrite_ignore,
+ proc: method(:rewrite_url)
end
Contract String, Or[String, Pathname], Any => String
diff --git a/middleman-core/lib/middleman-core/extensions/directory_indexes.rb b/middleman-core/lib/middleman-core/extensions/directory_indexes.rb
index 171b1493..cef88868 100644
--- a/middleman-core/lib/middleman-core/extensions/directory_indexes.rb
+++ b/middleman-core/lib/middleman-core/extensions/directory_indexes.rb
@@ -11,16 +11,22 @@ class Middleman::Extensions::DirectoryIndexes < ::Middleman::Extension
index_file = app.config[:index_file]
new_index_path = "/#{index_file}"
+ extensions = %w(.htm .html .php .xhtml)
+
resources.each do |resource|
# Check if it would be pointless to reroute
next if resource.destination_path == index_file ||
resource.destination_path.end_with?(new_index_path) ||
- File.extname(index_file) != resource.ext
+ !extensions.include?(resource.ext)
# Check if file metadata (options set by "page" in config.rb or frontmatter) turns directory_index off
next if resource.options[:directory_index] == false
- resource.destination_path = resource.destination_path.chomp(File.extname(index_file)) + new_index_path
+ extensions.each do |ext|
+ resource.destination_path = resource.destination_path.chomp(ext)
+ end
+
+ resource.destination_path += new_index_path
end
end
end
diff --git a/middleman-core/lib/middleman-core/extensions/external_pipeline.rb b/middleman-core/lib/middleman-core/extensions/external_pipeline.rb
index 44a4e6b3..ae5e4c3c 100644
--- a/middleman-core/lib/middleman-core/extensions/external_pipeline.rb
+++ b/middleman-core/lib/middleman-core/extensions/external_pipeline.rb
@@ -10,32 +10,79 @@ class Middleman::Extensions::ExternalPipeline < ::Middleman::Extension
def initialize(app, config={}, &block)
super
+ return if app.mode?(:config)
+
+ require 'servolux'
require 'thread'
+ require 'fileutils'
+
+ source_path = File.expand_path(options[:source], app.root)
+
+ # Make sure it exists, or `listen` will explode.
+ ::FileUtils.mkdir_p(source_path)
@watcher = app.files.watch :source,
- path: File.expand_path(options[:source], app.root),
- latency: options[:latency]
+ path: source_path,
+ latency: options[:latency],
+ frontmatter: false
+
+ @current_thread = nil
+ app.reload(&method(:reload!))
logger.info "== Executing: `#{options[:command]}`"
if app.build? || options[:disable_background_execution]
- watch_command!
+ watch_command!(false)
+
+ @watcher.poll_once!
else
- ::Thread.new { watch_command! }
+ watch_command!(true)
end
end
- def watch_command!
- ::IO.popen(options[:command], 'r') do |pipe|
- while buf = pipe.gets
+ def reload!
+ if @current_thread
+ logger.info "== Stopping: `#{options[:command]}`"
+
+ @current_thread.stop
+ @current_thread = nil
+ end
+ end
+
+ def watch_command!(async)
+ @current_thread = ::Servolux::Child.new(
+ command: options[:command],
+ suspend: 2
+ )
+
+ @current_thread.start
+
+ watch_thread = Thread.new do
+ while buf = @current_thread.io.gets
without_newline = buf.sub(/\n$/, '')
- logger.info "== External: #{without_newline}" if without_newline.length > 0
+ logger.info "== External: #{without_newline}" unless without_newline.empty?
+ end
+
+ @current_thread.wait
+
+ if !@current_thread.exitstatus.nil? && @current_thread.exitstatus != 0
+ logger.error '== External: Command failed with non-zero exit status'
+ exit(1)
end
end
- @watcher.poll_once!
+ watch_thread.join unless async
rescue ::Errno::ENOENT => e
logger.error "== External: Command failed with message: #{e.message}"
exit(1)
end
+
+ private
+
+ def print_command(stdout)
+ while buf = stdout.gets
+ without_newline = buf.sub(/\n$/, '')
+ logger.info "== External: #{without_newline}" unless without_newline.empty?
+ end
+ end
end
diff --git a/middleman-core/lib/middleman-core/extensions/gzip.rb b/middleman-core/lib/middleman-core/extensions/gzip.rb
index cc1a7fea..0483fe32 100644
--- a/middleman-core/lib/middleman-core/extensions/gzip.rb
+++ b/middleman-core/lib/middleman-core/extensions/gzip.rb
@@ -7,10 +7,10 @@
# to serve your Gzipped files whenever the normal (non-.gz) filename is requested.
#
# Pass the :exts options to customize which file extensions get zipped (defaults
-# to .html, .htm, .js and .css.
+# to .css, .htm, .html, .js, and .xhtml
#
class Middleman::Extensions::Gzip < ::Middleman::Extension
- option :exts, %w(.js .css .html .htm .svg), 'File extensions to Gzip when building.'
+ option :exts, %w(.css .htm .html .js .svg .xhtml), 'File extensions to Gzip when building.'
option :ignore, [], 'Patterns to avoid gzipping'
option :overwrite, false, 'Overwrite original files instead of adding .gz extension.'
@@ -64,10 +64,10 @@ class Middleman::Extensions::Gzip < ::Middleman::Extension
total_savings += (old_size - new_size)
size_change_word = (old_size - new_size) > 0 ? 'smaller' : 'larger'
- builder.trigger :gzip, "#{output_filename} (#{NumberHelpers.new.number_to_human_size((old_size - new_size).abs)} #{size_change_word})"
+ builder.trigger :created, "#{output_filename} (#{NumberHelpers.new.number_to_human_size((old_size - new_size).abs)} #{size_change_word})"
end
- builder.trigger :gzip, "Total gzip savings: #{NumberHelpers.new.number_to_human_size(total_savings)}"
+ builder.trigger :gzip, '', "Total gzip savings: #{NumberHelpers.new.number_to_human_size(total_savings)}"
I18n.locale = old_locale
end
diff --git a/middleman-core/lib/middleman-core/extensions/lorem.rb b/middleman-core/lib/middleman-core/extensions/lorem.rb
index 91a0612d..6cad9abf 100644
--- a/middleman-core/lib/middleman-core/extensions/lorem.rb
+++ b/middleman-core/lib/middleman-core/extensions/lorem.rb
@@ -44,7 +44,7 @@ class Middleman::Extensions::Lorem < ::Middleman::Extension
module LoremObject
class << self
# Words for use in lorem text
- WORDS = %w(alias consequatur aut perferendis sit voluptatem accusantium doloremque aperiam eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo aspernatur aut odit aut fugit sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt neque dolorem ipsum quia dolor sit amet consectetur adipisci velit sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem ut enim ad minima veniam quis nostrum exercitationem ullam corporis nemo enim ipsam voluptatem quia voluptas sit suscipit laboriosam nisi ut aliquid ex ea commodi consequatur quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae et iusto odio dignissimos ducimus qui blanditiis praesentium laudantium totam rem voluptatum deleniti atque corrupti quos dolores et quas molestias excepturi sint occaecati cupiditate non provident sed ut perspiciatis unde omnis iste natus error similique sunt in culpa qui officia deserunt mollitia animi id est laborum et dolorum fuga et harum quidem rerum facilis est et expedita distinctio nam libero tempore cum soluta nobis est eligendi optio cumque nihil impedit quo porro quisquam est qui minus id quod maxime placeat facere possimus omnis voluptas assumenda est omnis dolor repellendus temporibus autem quibusdam et aut consequatur vel illum qui dolorem eum fugiat quo voluptas nulla pariatur at vero eos et accusamus officiis debitis aut rerum necessitatibus saepe eveniet ut et voluptates repudiandae sint et molestiae non recusandae itaque earum rerum hic tenetur a sapiente delectus ut aut reiciendis voluptatibus maiores doloribus asperiores repellat)
+ WORDS = %w(alias consequatur aut perferendis sit voluptatem accusantium doloremque aperiam eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo aspernatur aut odit aut fugit sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt neque dolorem ipsum quia dolor sit amet consectetur adipisci velit sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem ut enim ad minima veniam quis nostrum exercitationem ullam corporis nemo enim ipsam voluptatem quia voluptas sit suscipit laboriosam nisi ut aliquid ex ea commodi consequatur quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae et iusto odio dignissimos ducimus qui blanditiis praesentium laudantium totam rem voluptatum deleniti atque corrupti quos dolores et quas molestias excepturi sint occaecati cupiditate non provident sed ut perspiciatis unde omnis iste natus error similique sunt in culpa qui officia deserunt mollitia animi id est laborum et dolorum fuga et harum quidem rerum facilis est et expedita distinctio nam libero tempore cum soluta nobis est eligendi optio cumque nihil impedit quo porro quisquam est qui minus id quod maxime placeat facere possimus omnis voluptas assumenda est omnis dolor repellendus temporibus autem quibusdam et aut consequatur vel illum qui dolorem eum fugiat quo voluptas nulla pariatur at vero eos et accusamus officiis debitis aut rerum necessitatibus saepe eveniet ut et voluptates repudiandae sint et molestiae non recusandae itaque earum rerum hic tenetur a sapiente delectus ut aut reiciendis voluptatibus maiores doloribus asperiores repellat).freeze
# Get one placeholder word
# @return [String]
diff --git a/middleman-core/lib/middleman-core/extensions/minify_css.rb b/middleman-core/lib/middleman-core/extensions/minify_css.rb
index e58b7852..aca9c60b 100644
--- a/middleman-core/lib/middleman-core/extensions/minify_css.rb
+++ b/middleman-core/lib/middleman-core/extensions/minify_css.rb
@@ -1,3 +1,5 @@
+require 'active_support/core_ext/object/try'
+require 'memoist'
require 'middleman-core/contracts'
# Minify CSS Extension
@@ -23,24 +25,25 @@ class Middleman::Extensions::MinifyCss < ::Middleman::Extension
class SassCompressor
def self.compress(style, options={})
root_node = ::Sass::SCSS::CssParser.new(style, 'middleman-css-input', 1).parse
- root_node.options = options.merge(style: :compressed)
+ root_node.options = {}.merge!(options).merge!(style: :compressed)
root_node.render.strip
end
end
# Rack middleware to look for CSS and compress it
class Rack
+ extend Memoist
include Contracts
INLINE_CSS_REGEX = /(