diff --git a/CONTRIBUTING.md b/.github/CONTRIBUTING.md similarity index 100% rename from CONTRIBUTING.md rename to .github/CONTRIBUTING.md diff --git a/.gitignore b/.gitignore index 591c32b0..aabe272b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +.byebug_history +npm-debug.log +manifest.yaml /.bundle .DS_Store coverage diff --git a/.rubocop.yml b/.rubocop.yml index 8346729e..c92b709f 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -10,11 +10,13 @@ AllCops: - '**/tmp/**/*' - '**/bin/**/*' - 'middleman-core/lib/middleman-core/step_definitions/**/*' - - 'middleman-core/lib/vendored-middleman-deps/**/*' - - 'middleman-cli/lib/middleman-templates/**/*' - 'middleman-core/fixtures/**/*' - 'middleman-core/features/**/*' - 'middleman-core/spec/**/*' + - 'middleman-cli/lib/middleman-cli/templates/**/*' + - 'middleman-cli/fixtures/**/*' + - 'middleman-cli/features/**/*' + - 'middleman-cli/spec/**/*' DoubleNegation: Enabled: false LineLength: @@ -37,8 +39,6 @@ AssignmentInCondition: Enabled: false CyclomaticComplexity: Enabled: false -AbcSize: - Enabled: false HandleExceptions: Enabled: false EndAlignment: @@ -52,7 +52,9 @@ FormatString: CaseIndentation: IndentWhenRelativeTo: end TrivialAccessors: - ExactNameMatch: true + Enabled: false +SingleLineBlockParams: + Enabled: false Metrics/AbcSize: Enabled: false Metrics/PerceivedComplexity: @@ -63,3 +65,7 @@ Style/BlockDelimiters: Enabled: false Style/MultilineBlockChain: Enabled: false +Style/SpecialGlobalVars: + Enabled: false +Style/FrozenStringLiteralComment: + Enabled: false diff --git a/.travis.yml b/.travis.yml index f3150364..ac5a7f06 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,9 +5,8 @@ before_script: - bundle update rvm: - ruby-head - - 2.2.2 - - 2.1 - - 2.0 + - 2.3.1 + - 2.2.4 os: - linux # - osx diff --git a/CHANGELOG.md b/CHANGELOG.md index 486acada..148aa2e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,103 @@ master === +# 4.1.13 + +* Change how config options are passed to Thor. Removes new Thor warnings from #2017 + +# 4.1.12 + +* Fix broken `ignore { |p| true }` form. + +# 4.1.11 + +* Upgrade to Rack 2. + +# 4.1.10 + +* Fix unicode issues in URL deeplinks. +* Add prefix option to asset_hash (#1949) + +# 4.1.9 + +* Fix `--watcher-*` CLI flags. +* Allow spaces in paths to work with `link_to`. Fixes #1914 +* Add support for dotenv +* Fix asset_url with asset_hash (#1919) +* Allow partial lookups without a current_resource (#1912) + +# 4.1.8 + +* Expose `development?` and `production?` helpers to template context. +* require the `try` core extension (#1911) +* Fix contract for Sitemap::Store.register_resource_list_manipulator (#1907) +* Loosen contract on Resource#source_file to Maybe[String] (#1906) +* Let collection loops access ConfigContext for helpers. #1879 +* Use https:// to clone templates (#1901) +* Allow numbers to be unique page_ids (#1886) +* Prevent infinite loop when encountering files where base filename is a possible templating engine + +# 4.1.7 + +* Upgrade fastimage to 2.0 +* Fix shutdown of external_pipeline commands when config.rb is changed. #1877 +* Allow calls to `app.` to work as collections after initial config parse. #1876 + + +# 4.1.5-4.1.6 + +* Fix file recursion when looking for possible asset dependencies. Major preview server performance improvement. + +# 4.1.4 + +* Unify default extensions for all URL processing extensions. #1855 +* Fix URL regex for `content: ` context of CSS. #1853 +* Make sure CLI config over-rides `config.rb` order. +* Fix relative assets in some contexts. #1842 + +# 4.1.3 + +* Expose all top-level config options to CLI (flags now match config. latency -> watcher_latency, etc). +* Fix directory indexes with `.htm` and `.xhtml` files. #1821 + +# 4.1.2 + +* Add `page_id` concept. Using the `id` key in frontmatter, proxy or page will set an ID on a resource which can be referenced by `url_for` and `link_to`. +* Allow looking for `Gemfile` when setting up a project to fail gracefully. +* Send correct exit code when external_pipeline fails during build. +* Fix error when customizing `layouts_dir`. #1028 +* Fix collections (commands in loops) not being processed by `page` command. #1226 +* Correctly asset_hash sourcemap references. + +# 4.1.1 + +* Fix bad code that made `/__middleman/` break. + +# 4.1.0 + +* Add rewrite_ignore option to asset_hash, asset_host, cache_buster & relative_assets. This proc let's you opt-out of the extension behavior on a per-path basis. +* gzip extension now compresses svgs by default +* Fix the `encoding` option. +* Fix relative paths on `image_tag` helper. +* Correctly exit with error code on failed `init` +* Fixed `asset_hash` when path has query string or #hashes +* Fix new extension template +* Don't parse frontmatter on ignored files. +* Fix displaying frontmatter on `/__middleman/sitemap` +* Add `skip_build_clean` config which when set to a block, will avoid removing non-generated paths from build, like .git #1716 +* Minor performance improvements +* DRY-up config.rb-specific commands like `ignore` or `path`. +* Fix automatic images with absolute (or images dir missing) paths in markdown. Fixes #1755 +* Fix asset_host in combination with Google Analytics snippet. #1751 +* Show an error message when git CLI is not available. #1765 +* Correctly show file names of GZIP'ed assets. #1364 +* Build file output is now parallel-ized! Use `middleman build --no-parallel` to disable. +* Make template file extensions that get layouts by default configurable via `config[:extensions_with_layout]` +* Remove `=` from inline url matcher. This means paths in HTML attributes MUST be quoted. Fixes #1780 + +# 4.0.0 + +* Add `:locales` and `:data` source types to the list of files which trigger a live-reload. * Rename i18n `lang` and `langs` to `locale` and `locales`. * Avoid matching URLs across new lines. #1689 * Load Middleman Directory when doing `init` over SSL @@ -32,7 +129,7 @@ master * Add `resources` class method to extensions to allow simple string-based resource generation. * rename `app.add_to_instance` to `Extension.expose_to_application` for adding extension-local methods to the shared app instance. * rename `app.add_to_config_context` to `Extension.expose_to_config` for adding extension-local methods to the sandboxed scope of `config.rb` -* Add `Extension.expose_to_templates`, which auto binds copies of extension-local methods into a Template context. +* Add `Extension.expose_to_template`, which auto binds copies of extension-local methods into a Template context. * Remove side-loading of CLI tasks from `tasks/` * Add the option of naming `config.rb` as `middleman.rb`. * Builder extracted from Thor. `after_build` hook now passes an instance of a Builder instead of the Thor CLI. diff --git a/Gemfile b/Gemfile index 18832cec..db4043a0 100644 --- a/Gemfile +++ b/Gemfile @@ -5,12 +5,16 @@ gem 'rake', '~> 10.3', require: false gem 'yard', '~> 0.8', require: false # Test tools -gem 'pry', '~> 0.10', group: :development, require: false -gem 'pry-byebug' -gem 'pry-stack_explorer' +gem 'byebug' gem 'aruba', '~> 0.7.4', require: false gem 'rspec', '~> 3.0', require: false gem 'cucumber', '~> 2.0', require: false +gem 'addressable', '~> 2.4.0', require: false + +# Pry tools +gem 'pry' +gem 'pry-stack_explorer' +gem 'pry-rescue' # Optional middleman dependencies, included for tests gem 'haml', '>= 4.0.5', require: false @@ -20,7 +24,7 @@ gem 'kramdown', '~> 1.2', require: false gem 'slim', '>= 2.0', require: false gem 'liquid', '>= 2.6', require: false gem 'stylus', '>= 1.0', require: false -gem 'sinatra', '>= 1.4', require: false +gem 'sinatra', '>= 2.0.0.beta2', require: false gem 'redcarpet', '>= 3.1', require: false # Dns server to test preview server @@ -28,6 +32,7 @@ gem 'rubydns', '~> 1.0.1', require: false # To test javascript gem 'poltergeist', '~> 1.8', require: false +gem 'phantomjs', '~> 2.1.1.0', require: false # For less, note there is no compatible JS runtime for windows gem 'therubyrhino', '>= 2.0', platforms: :jruby diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md new file mode 100644 index 00000000..6d2dc0e0 --- /dev/null +++ b/ISSUE_TEMPLATE.md @@ -0,0 +1,9 @@ +## Expected behavior and actual behavior + +## Steps to reproduce the problem (from a clean middleman installation) + +## Additional information + +- Ruby version: +- Middleman version: +- OS version: diff --git a/README.md b/README.md index b7203403..df4497f7 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ The last few years have seen an explosion in the amount and variety of tools dev * [Sass](http://sass-lang.com/) for DRY stylesheets * [CoffeeScript](http://coffeescript.org/) for safer and less verbose javascript -* Multiple asset management solutions, including [Sprockets](https://github.com/sstephenson/sprockets) +* Multiple asset management solutions, including [Sprockets](https://github.com/rails/sprockets) * [ERb](http://ruby-doc.org/stdlib-2.0.0/libdoc/erb/rdoc/ERB.html) & [Haml](http://haml.info/) for dynamic pages and simplified HTML syntax **Middleman** gives the stand-alone developer access to all these tools and many, many more. Why would you use a stand-alone framework instead of Ruby on Rails? @@ -91,7 +91,7 @@ The best way to get quick responses to your issues and swift fixes to your bugs ## Donate -[Click here to lend your support to Middleman](https://spacebox.io/s/4dXbHBorC3) +[Click here to lend your support to Middleman](https://plasso.co/s/4dXbHBorC3) ## Versioning @@ -121,6 +121,6 @@ Copyright (c) 2010-2015 Thomas Reynolds. MIT Licensed, see [LICENSE] for details [codeclimate]: https://codeclimate.com/github/middleman/middleman [gittip]: https://www.gittip.com/middleman/ [rubyinstaller]: http://rubyinstaller.org/ -[RubyInstaller-Devkit]: http:rubyinstaller.org/add-ons/devkit/ +[RubyInstaller-Devkit]: http://rubyinstaller.org/add-ons/devkit/ [rubydoc]: http://rubydoc.info/github/middleman/middleman [LICENSE]: https://github.com/middleman/middleman/blob/master/LICENSE.md diff --git a/Rakefile b/Rakefile index 236a2d83..508c3a1d 100644 --- a/Rakefile +++ b/Rakefile @@ -3,7 +3,7 @@ require 'rake' require File.expand_path('../middleman-core/lib/middleman-core/version.rb', __FILE__) ROOT = File.expand_path(File.dirname(__FILE__)) -GEM_NAME = 'middleman' +GEM_NAME = 'middleman'.freeze middleman_gems = %w(middleman-core middleman-cli middleman) GEM_PATHS = middleman_gems.freeze @@ -36,7 +36,7 @@ end desc 'Generate documentation for all middleman gems' task :doc do GEM_PATHS.each do |g| - Dir.chdir("#{File.join(ROOT, g)}") { sh "#{Gem.ruby} -S rake yard" } + Dir.chdir(File.join(ROOT, g).to_s) { sh "#{Gem.ruby} -S rake yard" } end end @@ -45,14 +45,14 @@ task :test do Rake::Task['rubocop'].invoke GEM_PATHS.each do |g| - Dir.chdir("#{File.join(ROOT, g)}") { sh "#{Gem.ruby} -S rake test" } + Dir.chdir(File.join(ROOT, g).to_s) { sh "#{Gem.ruby} -S rake test" } end end desc 'Run specs for all middleman gems' task :spec do GEM_PATHS.each do |g| - Dir.chdir("#{File.join(ROOT, g)}") { sh "#{Gem.ruby} -S rake spec" } + Dir.chdir(File.join(ROOT, g).to_s) { sh "#{Gem.ruby} -S rake spec" } end end diff --git a/middleman-cli/Rakefile b/middleman-cli/Rakefile index a7cd6621..db0d1cb8 100644 --- a/middleman-cli/Rakefile +++ b/middleman-cli/Rakefile @@ -1,4 +1,4 @@ # coding:utf-8 -RAKE_ROOT = __FILE__ -GEM_NAME = 'middleman-cli' +RAKE_ROOT = __FILE__.freeze +GEM_NAME = 'middleman-cli'.freeze require File.expand_path(File.dirname(__FILE__) + '/../gem_rake_helper') diff --git a/middleman-cli/bin/middleman b/middleman-cli/bin/middleman index 165b7bc9..8b381a08 100755 --- a/middleman-cli/bin/middleman +++ b/middleman-cli/bin/middleman @@ -4,11 +4,37 @@ require 'middleman-core/profiling' if ARGV.include? '--profile' Middleman::Profiling.profiler = Middleman::Profiling::RubyProfProfiler.new end -Middleman::Profiling.start +# Middleman::Profiling.start require "middleman-core/load_paths" Middleman.setup_load_paths +require 'dotenv' +::Dotenv.load + +require 'middleman-core' +require 'middleman-core/logger' + +module Middleman::Cli + class << self + attr_accessor :config + end + + def self.import_config(base) + ::Middleman::Cli.config.all_settings.each do |setting| + if setting.default.is_a?(String) || setting.default.is_a?(NilClass) + base.class_option setting.key, + type: :string, + desc: setting.description + elsif setting.default.is_a?(TrueClass) || setting.default.is_a?(FalseClass) + base.class_option setting.key, + type: :boolean, + desc: setting.description + end + end + end +end + require "middleman-cli" # Change directory to the root @@ -19,5 +45,26 @@ if ARGV[0] != 'help' && (ARGV.length < 1 || ARGV.first.include?('-')) ARGV.unshift('server') end +::Middleman::Logger.singleton(3) +::Middleman::Cli.config = ::Middleman::Application.new do + # + config[:environment] = (ENV['MM_ENV'] || ENV['RACK_ENV'] || 'development').to_sym + config[:mode] = :config + config[:exit_before_ready] = true + config[:watcher_disable] = true + config[:disable_sitemap] = true +end.config + +# Require the Middleman version +require 'middleman-core/version' + +# Include the core CLI items +require 'middleman-cli/init' +require 'middleman-cli/extension' +require 'middleman-cli/server' +require 'middleman-cli/build' +require 'middleman-cli/console' +require 'middleman-cli/config' + # Start the CLI Middleman::Cli::Base.start(ARGV) diff --git a/middleman-core/features/cli_extension.feature b/middleman-cli/features/cli_extension.feature similarity index 91% rename from middleman-core/features/cli_extension.feature rename to middleman-cli/features/cli_extension.feature index b762664c..5385a622 100644 --- a/middleman-core/features/cli_extension.feature +++ b/middleman-cli/features/cli_extension.feature @@ -9,6 +9,6 @@ Feature: Middleman New Extension CLI | Rakefile | | my-extension-library.gemspec | | features/support/env.rb | - | lib/middleman_extension.rb | + | lib/my-extension-library/extension.rb | | lib/my-extension-library.rb | | .gitignore | diff --git a/middleman-core/features/cli_init.feature b/middleman-cli/features/cli_init.feature similarity index 94% rename from middleman-core/features/cli_init.feature rename to middleman-cli/features/cli_init.feature index 52aa8f7b..96595422 100644 --- a/middleman-core/features/cli_init.feature +++ b/middleman-cli/features/cli_init.feature @@ -63,6 +63,10 @@ Feature: Middleman CLI And the file "Gemfile" should contain "middleman-blog" And the file ".gitignore" should exist + Scenario: Create an invalid project using Middleman directory + When I run `middleman init MY_PROJECT -T does-not-exist-for-reals` + Then the exit status should be 1 + Scenario: Create a new project using github(user/repository) When I run `middleman init MY_PROJECT -T middleman/middleman-templates-default` interactively And I type "y" diff --git a/middleman-core/features/cli/preview_server-hook.feature b/middleman-cli/features/preview_server-hook.feature similarity index 100% rename from middleman-core/features/cli/preview_server-hook.feature rename to middleman-cli/features/preview_server-hook.feature diff --git a/middleman-core/features/cli/preview_server.feature b/middleman-cli/features/preview_server.feature similarity index 99% rename from middleman-core/features/cli/preview_server.feature rename to middleman-cli/features/preview_server.feature index ab65683e..94ecc5b2 100644 --- a/middleman-core/features/cli/preview_server.feature +++ b/middleman-cli/features/preview_server.feature @@ -42,7 +42,7 @@ Feature: Run the preview server Inspect your site configuration at "http:// """ - @ruby-2.1 + @wip Scenario: Start the server with defaults in verbose mode, when a local mdns server resolves the local hostname Given I start a mdns server for the local hostname When I run `middleman server --verbose` interactively @@ -115,6 +115,7 @@ Feature: Run the preview server Inspect your site configuration at "http://127.0.0.1:4567/__middleman" """ + @wip Scenario: Start the server with bind address 127.0.0.5 This will have no hostname attached because the hosts file, the DNS server @@ -359,7 +360,6 @@ Feature: Run the preview server The Middleman preview server is bound to ":::65432", "0.0.0.0:65432" """ - @ruby-2.1 @wip Scenario: Start the server when port is blocked by other middleman instance Given `middleman server` is running in background @@ -469,7 +469,8 @@ Feature: Run the preview server Inspect your site configuration at "http://www.example.com:4567/__middleman", "http://127.0.0.1:4567/__middleman" """ - @ruby-2.1 + @ruby-2.1 + @wip Scenario: Start the server with server name "host.local" and the link local name server is used to resolve the server name To make the mdns resolver resolve a name, it needs to end with ".local". @@ -501,7 +502,8 @@ Feature: Run the preview server Inspect your site configuration at "http://host.local:4567/__middleman", "http://127.0.0.1:4567/__middleman" """ - @ruby-2.1 + @ruby-2.1 + @wip Scenario: Start the server with server name "host" and the link local name server is used to resolve the server name To make the mdns resolver resolve a name, it needs to end with ".local". If diff --git a/middleman-cli/features/support/env.rb b/middleman-cli/features/support/env.rb new file mode 100644 index 00000000..8044deab --- /dev/null +++ b/middleman-cli/features/support/env.rb @@ -0,0 +1,19 @@ +ENV["TEST"] = "true" + +require 'sassc' + +require 'simplecov' +SimpleCov.root(File.expand_path(File.dirname(__FILE__) + '/../..')) + +require 'phantomjs/poltergeist' +Capybara.javascript_driver = :poltergeist + +require 'coveralls' +Coveralls.wear! + +require 'codeclimate-test-reporter' +CodeClimate::TestReporter.start + +PROJECT_ROOT_PATH = File.dirname(File.dirname(File.dirname(__FILE__))) +require File.join(PROJECT_ROOT_PATH, 'lib', 'middleman-cli') +require File.join(File.dirname(PROJECT_ROOT_PATH), 'middleman-core', 'lib', 'middleman-core', 'step_definitions') diff --git a/middleman-core/fixtures/preview-server-app/bin/dns_server.rb b/middleman-cli/fixtures/preview-server-app/bin/dns_server.rb similarity index 100% rename from middleman-core/fixtures/preview-server-app/bin/dns_server.rb rename to middleman-cli/fixtures/preview-server-app/bin/dns_server.rb diff --git a/middleman-core/fixtures/preview-server-app/config.rb b/middleman-cli/fixtures/preview-server-app/config.rb similarity index 100% rename from middleman-core/fixtures/preview-server-app/config.rb rename to middleman-cli/fixtures/preview-server-app/config.rb diff --git a/middleman-core/fixtures/preview-server-app/source/index.html.erb b/middleman-cli/fixtures/preview-server-app/source/index.html.erb similarity index 100% rename from middleman-core/fixtures/preview-server-app/source/index.html.erb rename to middleman-cli/fixtures/preview-server-app/source/index.html.erb diff --git a/middleman-core/fixtures/preview-server-app/source/layout.erb b/middleman-cli/fixtures/preview-server-app/source/layout.erb similarity index 100% rename from middleman-core/fixtures/preview-server-app/source/layout.erb rename to middleman-cli/fixtures/preview-server-app/source/layout.erb diff --git a/middleman-core/fixtures/preview-server-app/source/layouts/custom.erb b/middleman-cli/fixtures/preview-server-app/source/layouts/custom.erb similarity index 100% rename from middleman-core/fixtures/preview-server-app/source/layouts/custom.erb rename to middleman-cli/fixtures/preview-server-app/source/layouts/custom.erb diff --git a/middleman-core/fixtures/preview-server-app/source/real.html b/middleman-cli/fixtures/preview-server-app/source/real.html similarity index 100% rename from middleman-core/fixtures/preview-server-app/source/real.html rename to middleman-cli/fixtures/preview-server-app/source/real.html diff --git a/middleman-core/fixtures/preview-server-app/source/real/index.html.erb b/middleman-cli/fixtures/preview-server-app/source/real/index.html.erb similarity index 100% rename from middleman-core/fixtures/preview-server-app/source/real/index.html.erb rename to middleman-cli/fixtures/preview-server-app/source/real/index.html.erb diff --git a/middleman-core/fixtures/preview-server-app/source/should_be_ignored.html b/middleman-cli/fixtures/preview-server-app/source/should_be_ignored.html similarity index 100% rename from middleman-core/fixtures/preview-server-app/source/should_be_ignored.html rename to middleman-cli/fixtures/preview-server-app/source/should_be_ignored.html diff --git a/middleman-core/fixtures/preview-server-app/source/should_be_ignored2.html b/middleman-cli/fixtures/preview-server-app/source/should_be_ignored2.html similarity index 100% rename from middleman-core/fixtures/preview-server-app/source/should_be_ignored2.html rename to middleman-cli/fixtures/preview-server-app/source/should_be_ignored2.html diff --git a/middleman-core/fixtures/preview-server-app/source/should_be_ignored3.html b/middleman-cli/fixtures/preview-server-app/source/should_be_ignored3.html similarity index 100% rename from middleman-core/fixtures/preview-server-app/source/should_be_ignored3.html rename to middleman-cli/fixtures/preview-server-app/source/should_be_ignored3.html diff --git a/middleman-core/fixtures/preview-server-app/source/static.html b/middleman-cli/fixtures/preview-server-app/source/static.html similarity index 100% rename from middleman-core/fixtures/preview-server-app/source/static.html rename to middleman-cli/fixtures/preview-server-app/source/static.html diff --git a/middleman-core/fixtures/preview-server-hook-app/config.rb b/middleman-cli/fixtures/preview-server-hook-app/config.rb similarity index 100% rename from middleman-core/fixtures/preview-server-hook-app/config.rb rename to middleman-cli/fixtures/preview-server-hook-app/config.rb diff --git a/middleman-core/fixtures/preview-server-hook-app/source/index.html.erb b/middleman-cli/fixtures/preview-server-hook-app/source/index.html.erb similarity index 100% rename from middleman-core/fixtures/preview-server-hook-app/source/index.html.erb rename to middleman-cli/fixtures/preview-server-hook-app/source/index.html.erb diff --git a/middleman-cli/lib/middleman-cli.rb b/middleman-cli/lib/middleman-cli.rb index 83b95d30..e17137e7 100644 --- a/middleman-cli/lib/middleman-cli.rb +++ b/middleman-cli/lib/middleman-cli.rb @@ -21,14 +21,3 @@ module Middleman::Cli end end end - -# Require the Middleman version -require 'middleman-core/version' - -# Include the core CLI items -require 'middleman-cli/init' -require 'middleman-cli/extension' -require 'middleman-cli/server' -require 'middleman-cli/build' -require 'middleman-cli/console' -require 'middleman-cli/config' diff --git a/middleman-cli/lib/middleman-cli/build.rb b/middleman-cli/lib/middleman-cli/build.rb index cf2d58a8..9e521dde 100644 --- a/middleman-cli/lib/middleman-cli/build.rb +++ b/middleman-cli/lib/middleman-cli/build.rb @@ -1,3 +1,5 @@ +require 'middleman-core/application' + # CLI Module module Middleman::Cli # The CLI Build class @@ -8,12 +10,15 @@ module Middleman::Cli class_option :environment, aliases: '-e', - default: ENV['MM_ENV'] || ENV['RACK_ENV'] || 'production', - desc: 'The environment Middleman will run under' + default: ENV['MM_ENV'] || ENV['RACK_ENV'] || :production class_option :clean, type: :boolean, default: true, desc: 'Remove orphaned files from build (--no-clean to disable)' + class_option :parallel, + type: :boolean, + default: true, + desc: 'Output files in parallel (--no-parallel to disable)' class_option :glob, type: :string, aliases: '-g', @@ -24,7 +29,7 @@ module Middleman::Cli default: false, desc: 'Print debug messages' class_option :instrument, - type: :string, + type: :boolean, default: false, desc: 'Print instrument messages' class_option :profile, @@ -32,6 +37,8 @@ module Middleman::Cli default: false, desc: 'Generate profiling report for the build' + Middleman::Cli.import_config(self) + # Core build Thor command # @return [void] def build @@ -44,35 +51,44 @@ module Middleman::Cli require 'middleman-core/builder' require 'fileutils' - env = options['environment'].to_sym verbose = options['verbose'] ? 0 : 1 instrument = options['instrument'] - @app = ::Middleman::Application.new do - config[:mode] = :build - config[:environment] = env - config[:show_exceptions] = false - ::Middleman::Logger.singleton(verbose, instrument) + builder = nil + cli_options = options + + ::Middleman::Logger.singleton(verbose, instrument) + + ::Middleman::Util.instrument 'builder.setup' do + @app = ::Middleman::Application.new do + config[:mode] = :build + config[:show_exceptions] = false + config[:cli_options] = cli_options.each_with_object({}) do |(k, v), sum| + sum[k] = v + end + end + + builder = Middleman::Builder.new(@app, + glob: options['glob'], + clean: options['clean'], + parallel: options['parallel']) + builder.thor = self + builder.on_build_event(&method(:on_event)) end - builder = Middleman::Builder.new(@app, - glob: options['glob'], - clean: options['clean'], - parallel: options['parallel']) - builder.thor = self - builder.on_build_event(&method(:on_event)) + ::Middleman::Util.instrument 'builder.run' do + if builder.run! + clean_directories! if options['clean'] + shell.say 'Project built successfully.' + else + msg = 'There were errors during this build' + unless options['verbose'] + msg << ', re-run with `middleman build --verbose` to see the full exception.' + end + shell.say msg, :red - if builder.run! - clean_directories! if options['clean'] - shell.say "Project built successfully." - else - msg = 'There were errors during this build' - unless options['verbose'] - msg << ', re-run with `middleman build --verbose` to see the full exception.' + exit(1) end - shell.say msg, :red - - exit(1) end end diff --git a/middleman-cli/lib/middleman-cli/config.rb b/middleman-cli/lib/middleman-cli/config.rb index 902ea216..add8730a 100644 --- a/middleman-cli/lib/middleman-cli/config.rb +++ b/middleman-cli/lib/middleman-cli/config.rb @@ -23,6 +23,9 @@ module Middleman::Cli ::Middleman::Logger.singleton(2, false) app = ::Middleman::Application.new do + config[:mode] = :config + config[:disable_sitemap] = true + config[:watcher_disable] = true config[:exit_before_ready] = true config[:environment] = opts[:environment].to_sym if opts[:environment] end diff --git a/middleman-cli/lib/middleman-cli/extension.rb b/middleman-cli/lib/middleman-cli/extension.rb index 1b079f4d..69e073fb 100644 --- a/middleman-cli/lib/middleman-cli/extension.rb +++ b/middleman-cli/lib/middleman-cli/extension.rb @@ -30,8 +30,8 @@ module Middleman::Cli template 'extension/Rakefile', File.join(name, 'Rakefile') template 'extension/gemspec', File.join(name, "#{name}.gemspec") template 'extension/Gemfile', File.join(name, 'Gemfile') - template 'extension/lib/middleman_extension.rb', File.join(name, 'lib', 'middleman_extension.rb') template 'extension/lib/lib.rb', File.join(name, 'lib', "#{name}.rb") + template 'extension/lib/lib/extension.rb', File.join(name, 'lib', name, 'extension.rb') template 'extension/features/support/env.rb', File.join(name, 'features', 'support', 'env.rb') empty_directory File.join(name, 'fixtures') end diff --git a/middleman-cli/lib/middleman-cli/init.rb b/middleman-cli/lib/middleman-cli/init.rb index 5bdfb6e3..e8e5770c 100644 --- a/middleman-cli/lib/middleman-cli/init.rb +++ b/middleman-cli/lib/middleman-cli/init.rb @@ -4,6 +4,8 @@ module Middleman::Cli class Init < Thor::Group include Thor::Actions + GIT_CMD = 'git'.freeze + check_unknown_options! argument :target, type: :string, default: '.' @@ -25,6 +27,13 @@ module Middleman::Cli require 'fileutils' require 'tmpdir' + unless git_present? + msg = 'You need to install the git command line tool to initialize a new project. ' + msg << "For help installing git, please refer to GitHub's tutorial at https://help.github.com/articles/set-up-git" + say msg, :red + exit 1 + end + repo_path, repo_branch = if shortname?(options[:template]) require 'open-uri' require 'json' @@ -39,7 +48,7 @@ module Middleman::Cli rescue ::OpenURI::HTTPError say "Template `#{options[:template]}` not found in Middleman Directory." say 'Did you mean to use a full `user/repo` path?' - exit + exit 1 end else repo_name, repo_branch = options[:template].split('#') @@ -51,11 +60,12 @@ module Middleman::Cli begin branch_cmd = repo_branch ? "-b #{repo_branch} " : '' - run("git clone --depth 1 #{branch_cmd}#{repo_path} #{dir}") + git_path = "#{branch_cmd}#{repo_path}" + run("#{GIT_CMD} clone --depth 1 #{branch_cmd}#{repo_path} #{dir}") - unless File.directory?(dir) - say 'Git clone failed, maybe the url is invalid or you don\'t have the permissions?', :red - exit + unless $?.success? + say "Git clone command failed. Make sure git repository exists: #{git_path}", :red + exit 1 end inside(target) do @@ -79,12 +89,31 @@ module Middleman::Cli protected + # Copied from Bundler + def git_present? + return @git_present if defined?(@git_present) + @git_present = which(GIT_CMD) || which('git.exe') + end + + # Copied from Bundler + def which(executable) + if File.file?(executable) && File.executable?(executable) + executable + elsif ENV['PATH'] + path = ENV['PATH'].split(File::PATH_SEPARATOR).find do |p| + abs_path = File.join(p, executable) + File.file?(abs_path) && File.executable?(abs_path) + end + path && File.expand_path(executable, path) + end + end + def shortname?(repo) repo.split('/').length == 1 end def repository_path(repo) - repo.include?('://') || repo.include?('git@') ? repo : "git://github.com/#{repo}.git" + repo.include?('://') || repo.include?('git@') ? repo : "https://github.com/#{repo}.git" end # Add to CLI diff --git a/middleman-cli/lib/middleman-cli/server.rb b/middleman-cli/lib/middleman-cli/server.rb index ab387ee6..85e75a9f 100644 --- a/middleman-cli/lib/middleman-cli/server.rb +++ b/middleman-cli/lib/middleman-cli/server.rb @@ -5,56 +5,33 @@ module Middleman::Cli check_unknown_options! class_option :environment, - aliases: '-e', - default: ENV['MM_ENV'] || ENV['RACK_ENV'] || 'development', - desc: 'The environment Middleman will run under' + aliases: '-e' class_option :port, - aliases: '-p', - desc: 'The port Middleman will listen on' + aliases: '-p' class_option :server_name, - aliases: '-s', - desc: 'The server name Middleman will use' + aliases: '-s' class_option :bind_address, - aliases: '-b', - desc: 'The bind address Middleman will listen on' - class_option :https, - type: :boolean, - desc: 'Serve the preview server over SSL/TLS' - class_option :ssl_certificate, - desc: 'Path to an X.509 certificate to use for the preview server' - class_option :ssl_private_key, - desc: "Path to an RSA private key for the preview server's certificate" + aliases: '-b' class_option :verbose, type: :boolean, default: false, desc: 'Print debug messages' class_option :instrument, - type: :string, - default: false, - desc: 'Print instrument messages' - class_option :disable_watcher, type: :boolean, default: false, - desc: 'Disable the file change and delete watcher process' + desc: 'Print instrument messages' class_option :profile, type: :boolean, default: false, desc: 'Generate profiling report for server startup' - class_option :force_polling, - type: :boolean, - default: false, - desc: 'Force file watcher into polling mode' - class_option :latency, - type: :numeric, - aliases: '-l', - default: 0.5, - desc: 'Set file watcher latency, in seconds' class_option :daemon, type: :boolean, aliases: '-d', default: false, desc: 'Daemonize preview server' + Middleman::Cli.import_config(self) + # Start the server def server require 'middleman-core' @@ -66,24 +43,14 @@ module Middleman::Cli end params = { - port: options['port'], - bind_address: options['bind_address'], - https: options['https'], - server_name: options['server_name'], - ssl_certificate: options['ssl_certificate'], - ssl_private_key: options['ssl_private_key'], - environment: options['environment'], debug: options['verbose'], instrumenting: options['instrument'], - disable_watcher: options['disable_watcher'], reload_paths: options['reload_paths'], - force_polling: options['force_polling'], - latency: options['latency'], daemon: options['daemon'] } puts '== The Middleman is loading' - ::Middleman::PreviewServer.start(params) + ::Middleman::PreviewServer.start(params, options) end # Add to CLI diff --git a/middleman-cli/lib/middleman-cli/templates/extension/lib/lib.rb b/middleman-cli/lib/middleman-cli/templates/extension/lib/lib.rb index 8a3c5dc6..48038a71 100644 --- a/middleman-cli/lib/middleman-cli/templates/extension/lib/lib.rb +++ b/middleman-cli/lib/middleman-cli/templates/extension/lib/lib.rb @@ -1,38 +1,6 @@ -# Require core library -require 'middleman-core' +require "middleman-core" -# Extension namespace -class MyExtension < ::Middleman::Extension - option :my_option, 'default', 'An example option' - - def initialize(app, options_hash={}, &block) - # Call super to build options from the options_hash - super - - # Require libraries only when activated - # require 'necessary/library' - - # set up your extension - # puts options.my_option - end - - def after_configuration - # Do something - end - - # A Sitemap Manipulator - # def manipulate_resource_list(resources) - # end - - # helpers do - # def a_helper - # end - # end +Middleman::Extensions.register :<%= name %> do + require "my-extension/extension" + MyExtension end - -# Register extensions which can be activated -# Make sure we have the version of Middleman we expect -# Name param may be omited, it will default to underscored -# version of class name - -# MyExtension.register(:my_extension) diff --git a/middleman-cli/lib/middleman-cli/templates/extension/lib/lib/extension.rb b/middleman-cli/lib/middleman-cli/templates/extension/lib/lib/extension.rb new file mode 100644 index 00000000..f831386f --- /dev/null +++ b/middleman-cli/lib/middleman-cli/templates/extension/lib/lib/extension.rb @@ -0,0 +1,31 @@ +# Require core library +require 'middleman-core' + +# Extension namespace +class MyExtension < ::Middleman::Extension + option :my_option, 'default', 'An example option' + + def initialize(app, options_hash={}, &block) + # Call super to build options from the options_hash + super + + # Require libraries only when activated + # require 'necessary/library' + + # set up your extension + # puts options.my_option + end + + def after_configuration + # Do something + end + + # A Sitemap Manipulator + # def manipulate_resource_list(resources) + # end + + # helpers do + # def a_helper + # end + # end +end diff --git a/middleman-cli/lib/middleman-cli/templates/extension/lib/middleman_extension.rb b/middleman-cli/lib/middleman-cli/templates/extension/lib/middleman_extension.rb deleted file mode 100644 index 6fba3c06..00000000 --- a/middleman-cli/lib/middleman-cli/templates/extension/lib/middleman_extension.rb +++ /dev/null @@ -1 +0,0 @@ -require '<%= name %>' diff --git a/middleman-cli/middleman-cli.gemspec b/middleman-cli/middleman-cli.gemspec index 05f5c456..e9447a9c 100644 --- a/middleman-cli/middleman-cli.gemspec +++ b/middleman-cli/middleman-cli.gemspec @@ -17,7 +17,7 @@ Gem::Specification.new do |s| s.test_files = `git ls-files -z -- {fixtures,features}/*`.split("\0") s.executable = 'middleman' s.require_path = 'lib' - s.required_ruby_version = '>= 1.9.3' + s.required_ruby_version = '>= 2.2.0' # CLI s.add_dependency('thor', ['>= 0.17.0', '< 2.0']) diff --git a/middleman-core/Rakefile b/middleman-core/Rakefile index 3efd63ea..198f09ee 100644 --- a/middleman-core/Rakefile +++ b/middleman-core/Rakefile @@ -1,4 +1,4 @@ # coding:utf-8 -RAKE_ROOT = __FILE__ +RAKE_ROOT = __FILE__.freeze GEM_NAME = ENV['NAME'] || 'middleman-core' require File.expand_path(File.dirname(__FILE__) + '/../gem_rake_helper') diff --git a/middleman-core/features/asset_hash.feature b/middleman-core/features/asset_hash.feature index 4e9b293d..7c173800 100644 --- a/middleman-core/features/asset_hash.feature +++ b/middleman-core/features/asset_hash.feature @@ -63,22 +63,29 @@ Feature: Assets get file hashes appended to them and references to them are upda Given the Server is running at "asset-hash-app" When I go to "/" Then I should see 'href="apple-touch-icon.png"' - And I should see 'href="stylesheets/site-d2959d87.css"' + And I should see 'href="stylesheets/site-d1a750ca.css"' + And I should see 'href="stylesheets/fragment-99b76247.css"' And I should see 'src="javascripts/application-1d8d5276.js"' And I should see 'src="images/100px-5fd6fb90.jpg"' And I should see 'srcset="images/100px-5fd6fb90.jpg 1x, images/200px-c11eb203.jpg 2x, images/300px-59adce76.jpg 3x"' + And I should see 'src="images/100px-5fd6fb90.jpg?test"' + And I should see 'src="images/100px-5fd6fb90.jpg?#test"' + And I should see 'src="images/100px-5fd6fb90.jpg#test"' When I go to "/subdir/" - Then I should see 'href="../stylesheets/site-d2959d87.css"' + Then I should see 'href="../stylesheets/site-d1a750ca.css"' And I should see 'src="../javascripts/application-1d8d5276.js"' And I should see 'src="../images/100px-5fd6fb90.jpg"' When I go to "/other/" - Then I should see 'href="../stylesheets/site-d2959d87.css"' + Then I should see 'href="../stylesheets/site-d1a750ca.css"' And I should see 'src="../javascripts/application-1d8d5276.js"' And I should see 'src="../images/100px-5fd6fb90.jpg"' + And I should see 'src="../images/100px-5fd6fb90.jpg?test"' + And I should see 'src="../images/100px-5fd6fb90.jpg?#test"' + And I should see 'src="../images/100px-5fd6fb90.jpg#test"' When I go to "/javascripts/application-1d8d5276.js" Then I should see "img.src = '/images/100px-5fd6fb90.jpg'" - When I go to "/stylesheets/site-d2959d87.css" - Then I should see 'background-image: url("../images/100px-5fd6fb90.jpg")' + When I go to "/stylesheets/site-d1a750ca.css" + Then I should see 'background-image: url("../images/100px-5fd6fb90.jpg");' When I go to "/api.json" Then I should see 'images/100px-5fd6fb90.gif' And I should see 'images/100px-5fd6fb90.jpg' @@ -87,6 +94,11 @@ Feature: Assets get file hashes appended to them and references to them are upda Then I should see 'images/100px-5fd6fb90.gif' And I should see 'images/100px-5fd6fb90.jpg' And I should see 'images/100px-1242c368.png' + When I go to "/stylesheets/fragment-99b76247.css" + And I should see 'url("../images/100px-5fd6fb90.jpg");' + And I should see 'url("../images/100px-5fd6fb90.jpg?test");' + And I should see 'url("../images/100px-5fd6fb90.jpg?#test");' + And I should see 'url("../images/100px-5fd6fb90.jpg#test");' Scenario: Hashed assets work with Slim Given the Server is running at "asset-hash-app" @@ -95,17 +107,69 @@ Feature: Assets get file hashes appended to them and references to them are upda And I should see 'src="images/100px-5fd6fb90.jpg"' And I should see 'srcset="images/100px-5fd6fb90.jpg 1x, images/200px-c11eb203.jpg 2x, images/300px-59adce76.jpg 3x"' - Scenario: Enabling an asset host still produces hashed files and references + Scenario: Enabling an asset host still produces hashed files and references (hash first) + Given a fixture app "asset-hash-host-app" + And a file named "config.rb" with: + """ + set :sass_source_maps, false + activate :asset_hash + activate :directory_indexes + activate :asset_host, host: 'http://middlemanapp.com' + """ Given the Server is running at "asset-hash-host-app" When I go to "/" - Then I should see 'href="http://middlemanapp.com/stylesheets/site-e587b659.css"' + Then I should see 'href="http://middlemanapp.com/stylesheets/site-7474cadd.css"' + Then I should see 'href="http://middlemanapp.com/stylesheets/fragment-2902933e.css"' And I should see 'src="http://middlemanapp.com/images/100px-5fd6fb90.jpg"' + And I should see 'src="http://middlemanapp.com/images/100px-5fd6fb90.jpg?test"' + And I should see 'src="http://middlemanapp.com/images/100px-5fd6fb90.jpg?#test"' + And I should see 'src="http://middlemanapp.com/images/100px-5fd6fb90.jpg#test"' When I go to "/subdir/" - Then I should see 'href="http://middlemanapp.com/stylesheets/site-e587b659.css"' + Then I should see 'href="http://middlemanapp.com/stylesheets/site-7474cadd.css"' And I should see 'src="http://middlemanapp.com/images/100px-5fd6fb90.jpg"' When I go to "/other/" - Then I should see 'href="http://middlemanapp.com/stylesheets/site-e587b659.css"' + Then I should see 'href="http://middlemanapp.com/stylesheets/site-7474cadd.css"' And I should see 'src="http://middlemanapp.com/images/100px-5fd6fb90.jpg"' + And I should see 'src="http://middlemanapp.com/images/100px-5fd6fb90.jpg?test"' + And I should see 'src="http://middlemanapp.com/images/100px-5fd6fb90.jpg?#test"' + And I should see 'src="http://middlemanapp.com/images/100px-5fd6fb90.jpg#test"' + When I go to "/stylesheets/fragment-2902933e.css" + And I should see 'url("http://middlemanapp.com/images/100px-5fd6fb90.jpg");' + And I should see 'url("http://middlemanapp.com/images/100px-5fd6fb90.jpg?test");' + And I should see 'url("http://middlemanapp.com/images/100px-5fd6fb90.jpg?#test");' + And I should see 'url("http://middlemanapp.com/images/100px-5fd6fb90.jpg#test");' + + Scenario: Enabling an asset host still produces hashed files and references (host first) + Given a fixture app "asset-hash-host-app" + And a file named "config.rb" with: + """ + set :sass_source_maps, false + activate :asset_host, host: 'http://middlemanapp.com' + activate :directory_indexes + activate :asset_hash + """ + Given the Server is running at "asset-hash-host-app" + When I go to "/" + Then I should see 'href="http://middlemanapp.com/stylesheets/site-7474cadd.css"' + Then I should see 'href="http://middlemanapp.com/stylesheets/fragment-2902933e.css"' + And I should see 'src="http://middlemanapp.com/images/100px-5fd6fb90.jpg"' + And I should see 'src="http://middlemanapp.com/images/100px-5fd6fb90.jpg?test"' + And I should see 'src="http://middlemanapp.com/images/100px-5fd6fb90.jpg?#test"' + And I should see 'src="http://middlemanapp.com/images/100px-5fd6fb90.jpg#test"' + When I go to "/subdir/" + Then I should see 'href="http://middlemanapp.com/stylesheets/site-7474cadd.css"' + And I should see 'src="http://middlemanapp.com/images/100px-5fd6fb90.jpg"' + When I go to "/other/" + Then I should see 'href="http://middlemanapp.com/stylesheets/site-7474cadd.css"' + And I should see 'src="http://middlemanapp.com/images/100px-5fd6fb90.jpg"' + And I should see 'src="http://middlemanapp.com/images/100px-5fd6fb90.jpg?test"' + And I should see 'src="http://middlemanapp.com/images/100px-5fd6fb90.jpg?#test"' + And I should see 'src="http://middlemanapp.com/images/100px-5fd6fb90.jpg#test"' + When I go to "/stylesheets/fragment-2902933e.css" + And I should see 'url("http://middlemanapp.com/images/100px-5fd6fb90.jpg")' + And I should see 'url("http://middlemanapp.com/images/100px-5fd6fb90.jpg?test")' + And I should see 'url("http://middlemanapp.com/images/100px-5fd6fb90.jpg?#test")' + And I should see 'url("http://middlemanapp.com/images/100px-5fd6fb90.jpg#test")' Scenario: The asset hash should change when a SASS partial changes Given the Server is running at "asset-hash-app" @@ -115,14 +179,14 @@ Feature: Assets get file hashes appended to them and references to them are upda font-size: 14px """ When I go to "/partials/" - Then I should see 'href="../stylesheets/uses_partials-44fb2764.css' + Then I should see 'href="../stylesheets/uses_partials-4d4e34e6.css' And the file "source/stylesheets/_partial.sass" has the contents """ body font-size: 18px !important """ When I go to "/partials/" - Then I should see 'href="../stylesheets/uses_partials-10d8ae33.css' + Then I should see 'href="../stylesheets/uses_partials-ec347271.css' Scenario: The asset hash should change when a Rack-based filter changes Given a fixture app "asset-hash-app" @@ -136,12 +200,10 @@ Feature: Assets get file hashes appended to them and references to them are upda """ Given the Server is running at "asset-hash-app" When I go to "/" - Then I should see 'href="stylesheets/site-30784643.css' - When I go to "stylesheets/site-30784643.css" - Then I should see 'background-image' + Then I should see 'href="stylesheets/site-5ad7def0.css' + When I go to "stylesheets/site-5ad7def0.css" + Then I should see 'background-image: url("../images/100px-5fd6fb90.jpg")' Then I should see 'Added by Rack filter' - When I go to "stylesheets/site-7474cadd.css" - Then I should see 'Not Found' Scenario: Hashed-asset files are not produced for ignored paths Given a fixture app "asset-hash-app" @@ -179,10 +241,84 @@ Feature: Assets get file hashes appended to them and references to them are upda | javascripts/application-1d8d5276.js | | stylesheets/site-7474cadd.css | + Scenario: Hashed-asset files are not replaced for rewrite ignored paths + Given a fixture app "asset-hash-app" + And a file named "config.rb" with: + """ + is_stylesheet = proc { |path| path.start_with? '/stylesheets' } + activate :asset_hash, rewrite_ignore: [ + %r(javascripts/*), + '/subdir/*', + is_stylesheet + ] + activate :relative_assets + activate :directory_indexes + """ + And a successfully built app at "asset-hash-app" + When I cd to "build" + Then the following files should exist: + | index.html | + | subdir/index.html | + | images/100px-5fd6fb90.jpg | + | javascripts/application-1d8d5276.js | + | stylesheets/site-8bc55985.css | + And the following files should not exist: + | images/100px.jpg | + | javascripts/application.js | + | stylesheets/site.css | + And the file "javascripts/application-1d8d5276.js" should contain "img.src = '/images/100px.jpg'" + And the file "stylesheets/site-8bc55985.css" should contain: + """ + background-image: url("../images/100px.jpg") + """ + And the file "index.html" should contain 'href="stylesheets/site-8bc55985.css"' + And the file "index.html" should contain 'src="javascripts/application-1d8d5276.js"' + And the file "index.html" should contain 'src="images/100px-5fd6fb90.jpg"' + And the file "subdir/index.html" should contain: + """ +

Image url3:

+

+ """ + Scenario: Already minified files should still be hashed Given a successfully built app at "asset-hash-minified-app" When I cd to "build" Then the following files should exist: | javascripts/jquery.min-276c87ff.js | + | stylesheets/test-7de2ad06.css | And the following files should not exist: | javascripts/jquery.min.js | + And the file "stylesheets/test-7de2ad06.css" should contain: + """ + .no-bug{background-image:url(/images/100px-5fd6fb90.jpg)} + .bug{content:"";background-image:url(/images/100px-5fd6fb90.jpg)} + .no-bug{content:""; background-image:url(/images/100px-5fd6fb90.jpg)} + """ + + Scenario: Source map paths include the hash + Given a successfully built app at "asset-hash-source-map" + When I cd to "build" + Then the following files should exist: + | index.html | + | javascripts/application-4553338c.js | + | javascripts/application.js-22cc2b5f.map | + | index.html | + And the following files should not exist: + | javascripts/application.js | + | javascripts/application.js.map | + + And the file "javascripts/application-4553338c.js" should contain "//# sourceMappingURL=application.js-22cc2b5f.map" + + Scenario: Hashes can contain a prefix + Given a successfully built app at "asset-hash-prefix" + When I cd to "build" + Then the following files should exist: + | index.html | + | javascripts/application-myprefix-4553338c.js | + | javascripts/application.js-myprefix-22cc2b5f.map | + | index.html | + And the following files should not exist: + | javascripts/application.js | + | javascripts/application.js.map | + + And the file "javascripts/application-myprefix-4553338c.js" should contain "//# sourceMappingURL=application.js-myprefix-22cc2b5f.map" diff --git a/middleman-core/features/asset_host.feature b/middleman-core/features/asset_host.feature index a8778d29..0433b61b 100644 --- a/middleman-core/features/asset_host.feature +++ b/middleman-core/features/asset_host.feature @@ -8,6 +8,7 @@ Feature: Alternate between multiple asset hosts """ And the Server is running When I go to "/asset_host.html" + Then I should see "'.google-analytics.com/ga.js'" Then I should see 'src="https://code.jquery.com/jquery-2.1.3.min.js"' Then I should see content matching %r{http://assets1.example.com/} Then I should not see content matching %r{http://assets1.example.com//} @@ -16,13 +17,16 @@ Feature: Alternate between multiple asset hosts When I go to "/stylesheets/asset_host.css" Then I should see content matching %r{http://assets1.example.com/} Then I should not see content matching %r{http://assets1.example.com//} + When I go to "/javascripts/asset_host.js" + Then I should not see content matching %r{http://assets1.example.com/} Scenario: Set proc host with inline-option Given a fixture app "asset-host-app" And a file named "config.rb" with: """ activate :asset_host, host: Proc.new { |asset| - "http://assets%d.example.com" % (asset.hash % 4) + hash = Digest::MD5.digest(asset).bytes.map!(&:ord).reduce(&:+) + "http://assets%d.example.com" % (hash % 4) } """ And the Server is running @@ -35,3 +39,17 @@ Feature: Alternate between multiple asset hosts When I go to "/stylesheets/asset_host.css" Then I should see content matching %r{http://assets1.example.com/} Then I should not see content matching %r{http://assets1.example.com//} + + Scenario: Hosts are not rewritten for rewrite ignored paths + Given a fixture app "asset-host-app" + And a file named "config.rb" with: + """ + activate :asset_host, host: "http://assets1.example.com", rewrite_ignore: [ + '/stylesheets/asset_host.css', + ] + """ + And the Server is running + When I go to "/asset_host.html" + Then I should see content matching %r{http://assets1.example.com/} + When I go to "/stylesheets/asset_host.css" + Then I should not see content matching %r{http://assets1.example.com/} diff --git a/middleman-core/features/automatic_image_sizes.feature b/middleman-core/features/automatic_image_sizes.feature index 89f9d7a5..50a0b02e 100644 --- a/middleman-core/features/automatic_image_sizes.feature +++ b/middleman-core/features/automatic_image_sizes.feature @@ -2,15 +2,28 @@ Feature: Automatically detect and insert image dimensions into tags In order to speed up development and appease YSlow Scenario: Rendering an image with the feature disabled - Given "automatic_image_sizes" feature is "disabled" + Given a fixture app "automatic-image-size-app" + And a file named "config.rb" with: + """ + """ And the Server is running at "automatic-image-size-app" When I go to "/auto-image-sizes.html" Then I should not see "width=" And I should not see "height=" - + When I go to "/markdown-sizes.html" + Then I should not see "width=" + And I should not see "height=" + Scenario: Rendering an image with the feature enabled - Given "automatic_image_sizes" feature is "enabled" + Given a fixture app "automatic-image-size-app" + And a file named "config.rb" with: + """ + activate :automatic_image_sizes + """ And the Server is running at "automatic-image-size-app" When I go to "/auto-image-sizes.html" - Then I should see "width=" - And I should see "height=" \ No newline at end of file + Then I should see 'width="1"' + And I should see 'height="1"' + When I go to "/markdown-sizes.html" + Then I should see 'width="1"' + And I should see 'height="1"' diff --git a/middleman-core/features/builder.feature b/middleman-core/features/builder.feature index b61f5cdc..d99c165c 100644 --- a/middleman-core/features/builder.feature +++ b/middleman-core/features/builder.feature @@ -20,13 +20,13 @@ Feature: Builder | layout | | layouts/custom | | layouts/content_for | - + And the file "index.html" should contain "Comment in layout" And the file "index.html" should contain "

Welcome

" And the file "static.html" should contain "Static, no code!" And the file "services/index.html" should contain "Services" And the file "stylesheets/static.css" should contain "body" - + Scenario: Build glob Given a successfully built app at "glob-app" with flags "--glob '*.css'" When I cd to "build" @@ -34,15 +34,19 @@ Feature: Builder | index.html | Then the following files should exist: | stylesheets/site.css | - + Scenario: Build with errors Given a built app at "build-with-errors-app" Then the exit status should be 1 - + Scenario: Build empty errors Given a built app at "empty-app" Then the exit status should be 1 + Scenario: Build external_pipeline errors + Given a built app at "external-pipeline-error" + Then the exit status should be 1 + Scenario: Build alias (b) Given a fixture app "large-build-app" When I run `middleman b` diff --git a/middleman-core/features/cache_buster.feature b/middleman-core/features/cache_buster.feature index d452234e..bea898e3 100644 --- a/middleman-core/features/cache_buster.feature +++ b/middleman-core/features/cache_buster.feature @@ -39,4 +39,18 @@ Feature: Generate mtime-based query string for busting browser caches And the Server is running at "cache-buster-app" When I go to "/cache-buster.html" Then I should see "site.css?" - Then I should see "blank.gif?" \ No newline at end of file + Then I should see "blank.gif?" + + Scenario: URLs are not rewritten for rewrite ignored paths + Given a fixture app "cache-buster-app" + And a file named "config.rb" with: + """ + activate :cache_buster, rewrite_ignore: [ + '/cache-buster.html', + ] + """ + And the Server is running at "cache-buster-app" + When I go to "/cache-buster.html" + Then I should see 'site.css"' + Then I should see 'empty-with-include.js"' + Then I should see 'blank.gif"' diff --git a/middleman-core/features/clean_build.feature b/middleman-core/features/clean_build.feature index eaa9942e..60c18578 100644 --- a/middleman-core/features/clean_build.feature +++ b/middleman-core/features/clean_build.feature @@ -16,6 +16,22 @@ Feature: Build Clean | build/should_be_ignored3.html | And the file "build/index.html" should contain "Comment in layout" + Scenario: Clean build has a whitelist + Given a fixture app "clean-app" + When a file named "build/.test" with: + """ + Hello + """ + When a file named "config.rb" with: + """ + set :skip_build_clean do |path| + path =~ /\.test/ + end + """ + Given a built app at "clean-app" + Then the following files should exist: + | build/.test | + Scenario: Clean build an app with newly ignored files and a nested output directory Given a fixture app "clean-nested-app" When a file named "config.rb" with: diff --git a/middleman-core/features/collections.feature b/middleman-core/features/collections.feature index e12ca0a5..2d313a3c 100644 --- a/middleman-core/features/collections.feature +++ b/middleman-core/features/collections.feature @@ -144,6 +144,33 @@ Feature: Collections And I should see 'Article: Blog3 Another Article' And I should see 'Article: Blog2 Yet Another Article' + Scenario: Work with local helpers + Given a fixture app "collections-app" + And a file named "config.rb" with: + """ + module TestHelper + def help_me + "ok" + end + end + + include TestHelper + + data.articles.each_with_index do |a, i| + proxy "/#{i}-#{help_me}.html", a + end + """ + And a file named "data/articles.yaml" with: + """ + --- + - "/blog1/2011-01-01-new-article.html" + - "/blog2/2011-01-02-another-article.html" + """ + Given the Server is running at "collections-app" + When I go to "0-ok.html" + Then I should see 'Newer Article Content' + When I go to "1-ok.html" + Then I should see 'Another Article Content' Scenario: Collected data update with file changes Given a fixture app "collections-app" diff --git a/middleman-core/features/custom_layouts.feature b/middleman-core/features/custom_layouts.feature index 27f7df08..dfe52344 100644 --- a/middleman-core/features/custom_layouts.feature +++ b/middleman-core/features/custom_layouts.feature @@ -11,6 +11,22 @@ Feature: Custom layouts When I go to "/custom-layout.html" Then I should see "Custom Layout" + Scenario: Using custom :layout attribute with proxy + Given a fixture app "custom-layout-app2" + And a file named "config.rb" with: + """ + page '/test/*', layout: :custom + proxy "/test/me.html", "/custom-layout.html" + live { %w(you) }.each do |who| + proxy "/test/#{who}.html", "/custom-layout.html" + end + """ + And the Server is running at "custom-layout-app2" + When I go to "/test/me.html" + Then I should see "Custom Layout" + When I go to "/test/you.html" + Then I should see "Custom Layout" + Scenario: Using custom :layout attribute with folders Given a fixture app "custom-layout-app2" And a file named "config.rb" with: @@ -65,4 +81,4 @@ Feature: Custom layouts When I go to "/path/index.html" Then I should see "Alt" And I should see "Monde" - And I should not see "Hello" \ No newline at end of file + And I should not see "Hello" diff --git a/middleman-core/features/directory_index.feature b/middleman-core/features/directory_index.feature index f8c87a21..ddbd703f 100644 --- a/middleman-core/features/directory_index.feature +++ b/middleman-core/features/directory_index.feature @@ -9,6 +9,7 @@ Feature: Directory Index | a_folder/needs_index/index.html | | leave_me_alone.html | | wildcard_leave_me_alone.html | + | regex_leave_me_alone2.html | | evil spaces/index.html | | regular/index.html | | .htaccess | @@ -21,12 +22,13 @@ Feature: Directory Index | a_folder/needs_index.html | | leave_me_alone/index.html | | wildcard_leave_me_alone/index.html | + | regex_leave_me_alone2/index.html | And the file "needs_index/index.html" should contain "Indexable" And the file "a_folder/needs_index/index.html" should contain "Indexable" And the file "leave_me_alone.html" should contain "Stay away" And the file "regular/index.html" should contain "Regular" And the file "evil spaces/index.html" should contain "Filled with Evil Spaces" - + Scenario: Preview normal file Given the Server is running at "indexable-app" When I go to "/needs_index/" diff --git a/middleman-core/features/dynamic_pages.feature b/middleman-core/features/dynamic_pages.feature index 6d1b59ac..fc886ffd 100644 --- a/middleman-core/features/dynamic_pages.feature +++ b/middleman-core/features/dynamic_pages.feature @@ -31,7 +31,8 @@ Feature: Dynamic Pages | should_be_ignored6.html | | should_be_ignored7.html | | should_be_ignored8.html | - + | should_be_ignored9.html | + Scenario: Preview basic proxy Given the Server is running at "dynamic-pages-app" When I go to "/fake.html" @@ -42,46 +43,46 @@ Feature: Dynamic Pages Then I should see "I am real" When I go to "/fake4.html" Then I should see "I am real" - + Scenario: Preview proxy with variable one Given the Server is running at "dynamic-pages-app" When I go to "/fake/one.html" Then I should see "I am real: one" Then I should see "Global: I am one glob" Then I should see "All: I am all glob" - + When I go to "/fake2/one.html" Then I should see "I am real: one" Then I should see "Global: I am two glob" Then I should see "All: I am all glob" - + When I go to "/fake3/one.html" Then I should see "I am real: one" Then I should see "Global: I am three glob" Then I should see "All: I am all glob" - + When I go to "/fake4/one.html" Then I should see "I am real: one" Then I should see "Global: I am four glob" Then I should see "All: I am all glob" - + Scenario: Preview proxy with variable two Given the Server is running at "dynamic-pages-app" When I go to "/fake/two.html" Then I should see "I am real: two" Then I should see "Global: I am one glob" Then I should see "All: I am all glob" - + When I go to "/fake2/two.html" Then I should see "I am real: two" Then I should see "Global: I am two glob" Then I should see "All: I am all glob" - + When I go to "/fake3/two.html" Then I should see "I am real: two" Then I should see "Global: I am three glob" Then I should see "All: I am all glob" - + When I go to "/fake4/two.html" Then I should see "I am real: two" Then I should see "Global: I am four glob" @@ -101,7 +102,7 @@ Feature: Dynamic Pages Then the file "fake3/one.html" should contain "I am real: one" Then the file "fake3/one.html" should contain "Global: I am three glob" Then the file "fake3/one.html" should contain "All: I am all glob" - + Scenario: Target ignore Given the Server is running at "dynamic-pages-app" When I go to "/target_ignore.html" @@ -112,7 +113,7 @@ Feature: Dynamic Pages Then I should see "Ignore me! 7" When I go to "/target_ignore4.html" Then I should see "Ignore me! 8" - + Scenario: Preview ignored paths Given the Server is running at "dynamic-pages-app" When I go to "/should_be_ignored.html" @@ -130,4 +131,6 @@ Feature: Dynamic Pages When I go to "/should_be_ignored7.html" Then I should see "File Not Found" When I go to "/should_be_ignored8.html" - Then I should see "File Not Found" \ No newline at end of file + Then I should see "File Not Found" + When I go to "/should_be_ignored9.html" + Then I should see "File Not Found" diff --git a/middleman-core/features/encoding_option.feature b/middleman-core/features/encoding_option.feature new file mode 100644 index 00000000..6244e420 --- /dev/null +++ b/middleman-core/features/encoding_option.feature @@ -0,0 +1,28 @@ +# encoding: iso-8859-1 +Feature: encoding option + + Scenario: No encoding set + Given a fixture app "clean-app" + Given the Server is running at "clean-app" + + When I go to "/index.html" + Then the "Content-Type" header should contain "text/html" + Then the "Content-Type" header should contain "charset=utf-8" + + @wip + Scenario: Custom encoding set + Given a fixture app "i-8859-1-app" + And a file named "config.rb" with: + """ + set :encoding, "ISO-8859-1" + + ::Rack::Mime::MIME_TYPES['.html'] = 'text/html; charset=iso-8859-1' + ::Rack::Mime::MIME_TYPES['.htm'] = 'text/html; charset=iso-8859-1' + ::Rack::Mime::MIME_TYPES['.map'] = 'application/json; charset=iso-8859-1' + """ + Given the Server is running at "i-8859-1-app" + + When I go to "/index.html" + Then the "Content-Type" header should contain "text/html" + Then the "Content-Type" header should contain "charset=iso-8859-1" + Then I should see "äöü" diff --git a/middleman-core/features/front-matter-neighbor.feature b/middleman-core/features/front-matter-neighbor.feature index e2e5cc69..9260ff41 100644 --- a/middleman-core/features/front-matter-neighbor.feature +++ b/middleman-core/features/front-matter-neighbor.feature @@ -22,7 +22,7 @@ Feature: Neighboring YAML Front Matter Then I should not see "---" When I go to "/raw-front-matter.php.frontmatter" Then I should see "File Not Found" - + Scenario: YAML not on first line, with encoding Given the Server is running at "frontmatter-neighbor-app" When I go to "/front-matter-encoding.html" @@ -35,7 +35,7 @@ Feature: Neighboring YAML Front Matter Given the Server is running at "frontmatter-neighbor-app" And the file "source/front-matter-change.html.erb" has the contents """ - <%= current_page.data.title %> + FileA <%= current_page.data.title %> """ And the file "source/front-matter-change.html.erb.frontmatter" has the contents """ @@ -43,6 +43,8 @@ Feature: Neighboring YAML Front Matter title: Hello World layout: false --- + + FileB """ When I go to "/front-matter-change.html" Then I should see "Hello World" @@ -52,6 +54,8 @@ Feature: Neighboring YAML Front Matter title: Hola Mundo layout: false --- + + FileC """ When I go to "/front-matter-change.html" Then I should see "Hola Mundo" diff --git a/middleman-core/features/gzip.feature b/middleman-core/features/gzip.feature index 2f74cc9a..643972bc 100644 --- a/middleman-core/features/gzip.feature +++ b/middleman-core/features/gzip.feature @@ -22,7 +22,7 @@ Feature: GZIP assets during build Given a fixture app "gzip-app" And a file named "config.rb" with: """ - activate :gzip, exts: %w(.js .html .htm) + activate :gzip, exts: %w(.htm .html .js .xhtml) """ And a successfully built app at "gzip-app" Then the following files should exist: diff --git a/middleman-core/features/helpers_link_to.feature b/middleman-core/features/helpers_link_to.feature index 90b6f08a..fe8e8530 100644 --- a/middleman-core/features/helpers_link_to.feature +++ b/middleman-core/features/helpers_link_to.feature @@ -40,19 +40,29 @@ Feature: link_to helper """ absolute: <%= link_to "Needs Index", "/needs_index.html", relative: true %> relative: <%= link_to "Relative", "needs_index.html", relative: true %> + + absolute spaces: <%= link_to "Spaces Index", "/evil spaces.html", relative: true %> + relative spaces: <%= link_to "Spaces Relative", "evil spaces.html", relative: true %> """ And a file named "source/link_to/sub.html.erb" with: """ absolute: <%= link_to "Needs Index", "/needs_index.html", relative: true %> relative: <%= link_to "Relative", "../needs_index.html", relative: true %> + + absolute spaces: <%= link_to "Spaces Index", "/evil spaces.html", relative: true %> + relative spaces: <%= link_to "Spaces Relative", "../evil spaces.html", relative: true %> """ And the Server is running at "indexable-app" When I go to "/link_to.html" Then I should see 'absolute: Needs Index' Then I should see 'relative: Relative' + Then I should see 'absolute spaces: Spaces Index' + Then I should see 'relative spaces: Spaces Relative' When I go to "/link_to/sub.html" Then I should see 'absolute: Needs Index' Then I should see 'relative: Relative' + Then I should see 'absolute spaces: Spaces Index' + Then I should see 'relative spaces: Spaces Relative' Scenario: link_to relative works with strip_index_file Given a fixture app "indexable-app" @@ -113,7 +123,7 @@ Feature: link_to helper When I go to "/link_to/sub.html" Then I should see 'absolute: Needs Index' Then I should see 'relative: Relative' - + Scenario: link_to knows about directory indexes Given a fixture app "indexable-app" And a file named "source/link_to.html.erb" with: diff --git a/middleman-core/features/i18n_link_to.feature b/middleman-core/features/i18n_link_to.feature index a10787c4..9d3f0682 100644 --- a/middleman-core/features/i18n_link_to.feature +++ b/middleman-core/features/i18n_link_to.feature @@ -4,7 +4,7 @@ Feature: i18n Paths Given a fixture app "empty-app" And a file named "data/pages.yml" with: """ - - hello.html + - hello.html """ And a file named "locales/en.yml" with: """ @@ -50,24 +50,24 @@ Feature: i18n Paths Given the Server is running at "empty-app" When I go to "/hello.html" Then I should see "Page: Hello" - Then I should see 'Current Home' - Then I should see 'Other Home' - Then I should see 'Home: Current Block' - Then I should see 'Home: Other Block' - Then I should see 'Current hello.html' - Then I should see 'Other hello.html' - Then I should see 'Current Block' - Then I should see 'Other Block' + Then I should see 'Current Home' + Then I should see 'Other Home' + Then I should see 'Home: Current Block' + Then I should see 'Home: Other Block' + Then I should see 'Current hello.html' + Then I should see 'Other hello.html' + Then I should see 'Current Block' + Then I should see 'Other Block' When I go to "/es/hola.html" Then I should see "Page: Hola" - Then I should see 'Current Home' - Then I should see 'Other Home' - Then I should see 'Home: Current Block' - Then I should see 'Home: Other Block' - Then I should see 'Current hello.html' - Then I should see 'Other hello.html' - Then I should see 'Current Block' - Then I should see 'Other Block' + Then I should see 'Current Home' + Then I should see 'Other Home' + Then I should see 'Home: Current Block' + Then I should see 'Home: Other Block' + Then I should see 'Current hello.html' + Then I should see 'Other hello.html' + Then I should see 'Current Block' + Then I should see 'Other Block' Scenario: link_to is i18n aware and supports relative_links Given a fixture app "empty-app" @@ -124,30 +124,30 @@ Feature: i18n Paths Then I should see "assets/css/main.css" When I go to "/hello.html" Then I should see "Page: Hello" - Then I should see 'Current Home' - Then I should see 'Other Home' - Then I should see 'Home: Current Block' - Then I should see 'Home: Other Block' - Then I should see 'Current hello.html' - Then I should see 'Other hello.html' - Then I should see 'Current Block' - Then I should see 'Other Block' + Then I should see 'Current Home' + Then I should see 'Other Home' + Then I should see 'Home: Current Block' + Then I should see 'Home: Other Block' + Then I should see 'Current hello.html' + Then I should see 'Other hello.html' + Then I should see 'Current Block' + Then I should see 'Other Block' When I go to "/es/hola.html" Then I should see "Page: Hola" - Then I should see 'Current Home' - Then I should see 'Other Home' - Then I should see 'Home: Current Block' - Then I should see 'Home: Other Block' - Then I should see 'Current hello.html' - Then I should see 'Other hello.html' - Then I should see 'Current Block' - Then I should see 'Other Block' + Then I should see 'Current Home' + Then I should see 'Other Home' + Then I should see 'Home: Current Block' + Then I should see 'Home: Other Block' + Then I should see 'Current hello.html' + Then I should see 'Other hello.html' + Then I should see 'Current Block' + Then I should see 'Other Block' Scenario: url_for is i18n aware Given a fixture app "empty-app" And a file named "data/pages.yml" with: """ - - hello.html + - hello.html - article.html """ And a file named "locales/en.yml" with: diff --git a/middleman-core/features/i18n_mixed_sources.feature b/middleman-core/features/i18n_mixed_sources.feature new file mode 100644 index 00000000..7ea92028 --- /dev/null +++ b/middleman-core/features/i18n_mixed_sources.feature @@ -0,0 +1,39 @@ +Feature: i18n merging path trees + + Scenario: Mixing localized and non-localized sources and merging the path trees (see issue #1709) + Given a fixture app "i18n-test-app" + And a file named "config.rb" with: + """ + activate :i18n, mount_at_root: :en, langs: [:en, :es] + """ + Given the Server is running at "i18n-mixed-sources" + + When I go to "/" + Then I should see "Current locale: en" + Then I should see "path: is-localized Home" + When I go to "/es" + Then I should see "Current locale: es" + Then I should see "path: is-localized Home" + + When I go to "/a/" + Then I should see "Current locale: en" + Then I should see "path: is-localized Home # a/index.html.erb" + When I go to "/es/a/" + Then I should see "Current locale: es" + Then I should see "path: is-localized Home # a/index.html.erb" + + When I go to "/b/" + Then I should see "Current locale: en" + Then I should see "path: is-localized Home # b/index.html.erb" + + When I go to "/a/sub.html" + Then I should see "Current locale: en" + Then I should see "path: is-localized Home # a/index.html.erb # a/sub.html.erb" + + When I go to "/b/sub.html" + Then I should see "Current locale: en" + Then I should see "path: is-localized Home # b/index.html.erb # b/sub.html.erb" + + When I go to "/es/b/sub.html" + Then I should see "Current locale: es" + Then I should see "path: is-localized Home # b/index.html.erb # b/sub.html.erb" diff --git a/middleman-core/features/ignore.feature b/middleman-core/features/ignore.feature index 166002c5..9e3d4688 100644 --- a/middleman-core/features/ignore.feature +++ b/middleman-core/features/ignore.feature @@ -12,7 +12,7 @@ Feature: Ignoring paths And the following files should not exist: | build/plain.html | | build/about.html | - + Scenario: Ignore a single path (server) Given a fixture app "ignore-app" And a file named "config.rb" with: @@ -28,6 +28,35 @@ Feature: Ignoring paths When I go to "/about.html" Then I should see "File Not Found" + Scenario: Ignoring collected values + Given a fixture app "ignore-app" + And a file named "data/ignores.yaml" with: + """ + --- + - "plain" + """ + And a file named "config.rb" with: + """ + data.ignores.each do |name| + ignore "#{name}.html" + end + """ + And the Server is running + When I go to "/plain.html" + Then I should see "File Not Found" + When I go to "/about.html" + Then I should not see "File Not Found" + + When the file "data/ignores.yaml" has the contents + """ + --- + - "about" + """ + When I go to "/plain.html" + Then I should not see "File Not Found" + When I go to "/about.html" + Then I should see "File Not Found" + Scenario: Ignore a globbed path (build) Given a fixture app "ignore-app" And a file named "config.rb" with: @@ -47,7 +76,7 @@ Feature: Ignoring paths | build/reports/index.html | | build/reports/another.html | | build/images/icons/messages.png | - + Scenario: Ignore a globbed path (server) Given a fixture app "ignore-app" And a file named "config.rb" with: @@ -93,7 +122,7 @@ Feature: Ignoring paths | build/reports/index.html | | build/reports/another.html | | build/images/icons/messages.png | - + Scenario: Ignore a regex (server) Given a fixture app "ignore-app" And a file named "config.rb" with: @@ -118,4 +147,4 @@ Feature: Ignoring paths When I go to "/reports/another.html" Then I should see "File Not Found" When I go to "/images/icons/messages.png" - Then I should see "File Not Found" \ No newline at end of file + Then I should see "File Not Found" diff --git a/middleman-core/features/import_files.feature b/middleman-core/features/import_files.feature index b45f1256..faaf2039 100644 --- a/middleman-core/features/import_files.feature +++ b/middleman-core/features/import_files.feature @@ -13,5 +13,3 @@ Feature: Import files Then I should see 'jQuery' When I go to "/bower_components2/jquery/dist/jquery.js" Then I should see 'jQuery' - - diff --git a/middleman-core/features/markdown_redcarpet_in_slim.feature b/middleman-core/features/markdown_redcarpet_in_slim.feature new file mode 100644 index 00000000..250c6857 --- /dev/null +++ b/middleman-core/features/markdown_redcarpet_in_slim.feature @@ -0,0 +1,41 @@ +Feature: Markdown support in Slim + In order to test support of the Slim markdown filter + + Scenario: Markdown filter in Slim works + Given a fixture app "markdown-in-slim-app" + And a file named "config.rb" with: + """ + set :markdown_engine, :redcarpet + activate :directory_indexes + """ + And a file named "source/markdown_filter.html.slim" with: + """ + markdown: + # H1 + + paragraph + """ + Given the Server is running at "markdown-in-slim-app" + When I go to "/markdown_filter/" + Then I should see ">H1" + Then I should see "

paragraph

" + + + Scenario: Markdown filter in Slim uses our link_to and image_tag helpers + Given a fixture app "markdown-in-slim-app" + And a file named "config.rb" with: + """ + set :markdown_engine, :redcarpet + activate :directory_indexes + """ + And a file named "source/link_and_image.html.slim" with: + """ + markdown: + [A link](/link_target.html) + + ![image](blank.gif) + """ + Given the Server is running at "markdown-in-slim-app" + When I go to "/link_and_image/" + Then I should see "/link_target/" + Then I should see 'src="/images/blank.gif"' diff --git a/middleman-core/features/nested_layouts.feature b/middleman-core/features/nested_layouts.feature index d70a7e23..f51831fb 100644 --- a/middleman-core/features/nested_layouts.feature +++ b/middleman-core/features/nested_layouts.feature @@ -5,37 +5,37 @@ Feature: Allow nesting of layouts When I go to "/index.html" Then I should see: """ - Master + Master Erb

Index Title

- Outer - Inner + I am Outer + I am Inner Template - + """ When I go to "/another.html" Then I should see: """ - Master + Master Erb

New Article Title

- Outer - Inner + I am Outer + I am Inner

The Article Content

""" - + Scenario: A page uses an inner layout when uses an outer layout (slim) Given the Server is running at "nested-layout-app" When I go to "/slim-test.html" - Then I should see "

Master

New Article Title

Outer

Inner

The Article Content

" - + Then I should see "

Master Slim

New Article Title

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 0f782761..ba131396 100644 --- a/middleman-core/features/relative_assets.feature +++ b/middleman-core/features/relative_assets.feature @@ -41,6 +41,11 @@ Feature: Relative Assets 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' + When I go to "/stylesheets/fonts2.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: Building css with the feature enabled Given a fixture app "relative-assets-app" @@ -115,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 '' + + 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/fonts.css', + ] + """ + 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/fonts.css" + Then I should see 'url(/fonts/roboto/roboto-regular-webfont.eot' diff --git a/middleman-core/features/relative_assets_helpers_only.feature b/middleman-core/features/relative_assets_helpers_only.feature new file mode 100644 index 00000000..194c8b37 --- /dev/null +++ b/middleman-core/features/relative_assets_helpers_only.feature @@ -0,0 +1,123 @@ +Feature: Relative Assets (Helpers Only) + + Scenario: Rendering css with the feature enabled + 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/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/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/support/env.rb b/middleman-core/features/support/env.rb index 29799086..77cfbb06 100644 --- a/middleman-core/features/support/env.rb +++ b/middleman-core/features/support/env.rb @@ -5,7 +5,7 @@ require 'sassc' require 'simplecov' SimpleCov.root(File.expand_path(File.dirname(__FILE__) + '/../..')) -require 'capybara/poltergeist' +require 'phantomjs/poltergeist' Capybara.javascript_driver = :poltergeist require 'coveralls' 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-app/source/index.html.erb b/middleman-core/fixtures/asset-hash-app/source/index.html.erb index 59a9c54f..de017654 100644 --- a/middleman-core/fixtures/asset-hash-app/source/index.html.erb +++ b/middleman-core/fixtures/asset-hash-app/source/index.html.erb @@ -4,6 +4,9 @@

Image url:

+<%= image_tag('100px.jpg?test') %> +<%= image_tag('100px.jpg?#test') %> +<%= image_tag('100px.jpg#test') %>

Ignored path:

diff --git a/middleman-core/fixtures/asset-hash-app/source/layout.erb b/middleman-core/fixtures/asset-hash-app/source/layout.erb index a21259a3..3a1079a7 100644 --- a/middleman-core/fixtures/asset-hash-app/source/layout.erb +++ b/middleman-core/fixtures/asset-hash-app/source/layout.erb @@ -3,16 +3,16 @@ - <%= stylesheet_link_tag "site" %> + <%= stylesheet_link_tag "site", "fragment" %> <%= javascript_include_tag "application" %> <%= yield_content :head %> - +
<%= yield %>
- + diff --git a/middleman-core/fixtures/asset-hash-app/source/other.html.erb b/middleman-core/fixtures/asset-hash-app/source/other.html.erb index 2e6f276f..cc258936 100644 --- a/middleman-core/fixtures/asset-hash-app/source/other.html.erb +++ b/middleman-core/fixtures/asset-hash-app/source/other.html.erb @@ -1,2 +1,5 @@

Image url:

+ + + diff --git a/middleman-core/fixtures/asset-hash-app/source/stylesheets/fragment.css.scss b/middleman-core/fixtures/asset-hash-app/source/stylesheets/fragment.css.scss new file mode 100644 index 00000000..4e175489 --- /dev/null +++ b/middleman-core/fixtures/asset-hash-app/source/stylesheets/fragment.css.scss @@ -0,0 +1,7 @@ +#main { + padding: 50px; + background-image: image-url('100px.jpg'); + background-image: image-url('100px.jpg?test'); + background-image: image-url('100px.jpg?#test'); + background-image: image-url('100px.jpg#test'); +} 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 3616ba64..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,4 +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-host-app/source/layout.erb b/middleman-core/fixtures/asset-hash-host-app/source/layout.erb index 443b3645..a6aa4a0c 100644 --- a/middleman-core/fixtures/asset-hash-host-app/source/layout.erb +++ b/middleman-core/fixtures/asset-hash-host-app/source/layout.erb @@ -3,15 +3,15 @@ - <%= stylesheet_link_tag "site" %> + <%= stylesheet_link_tag "site", "fragment" %> <%= yield_content :head %> - +
<%= yield %>
- + diff --git a/middleman-core/fixtures/asset-hash-host-app/source/other.html.erb b/middleman-core/fixtures/asset-hash-host-app/source/other.html.erb index 2e6f276f..cc258936 100644 --- a/middleman-core/fixtures/asset-hash-host-app/source/other.html.erb +++ b/middleman-core/fixtures/asset-hash-host-app/source/other.html.erb @@ -1,2 +1,5 @@

Image url:

+ + + diff --git a/middleman-core/fixtures/asset-hash-host-app/source/stylesheets/fragment.css.scss b/middleman-core/fixtures/asset-hash-host-app/source/stylesheets/fragment.css.scss index b169b432..4e175489 100644 --- a/middleman-core/fixtures/asset-hash-host-app/source/stylesheets/fragment.css.scss +++ b/middleman-core/fixtures/asset-hash-host-app/source/stylesheets/fragment.css.scss @@ -1,4 +1,7 @@ #main { padding: 50px; + background-image: image-url('100px.jpg'); + background-image: image-url('100px.jpg?test'); + background-image: image-url('100px.jpg?#test'); background-image: image-url('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 @@ +![Alt text](/images/blank.gif) 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/i-8859-1-app/config.rb b/middleman-core/fixtures/i-8859-1-app/config.rb new file mode 100644 index 00000000..3252ee63 --- /dev/null +++ b/middleman-core/fixtures/i-8859-1-app/config.rb @@ -0,0 +1 @@ +set :encoding, "ISO-8859-1" diff --git a/middleman-core/fixtures/i-8859-1-app/source/index.html.erb b/middleman-core/fixtures/i-8859-1-app/source/index.html.erb new file mode 100644 index 00000000..3c015801 --- /dev/null +++ b/middleman-core/fixtures/i-8859-1-app/source/index.html.erb @@ -0,0 +1 @@ +äöü diff --git a/middleman-core/fixtures/i18n-mixed-sources/config.rb b/middleman-core/fixtures/i18n-mixed-sources/config.rb new file mode 100644 index 00000000..4815436f --- /dev/null +++ b/middleman-core/fixtures/i18n-mixed-sources/config.rb @@ -0,0 +1 @@ +activate :i18n, mount_at_root: :en, langs: [:en, :es] diff --git a/middleman-core/fixtures/i18n-mixed-sources/locales/en.yml b/middleman-core/fixtures/i18n-mixed-sources/locales/en.yml new file mode 100644 index 00000000..91519342 --- /dev/null +++ b/middleman-core/fixtures/i18n-mixed-sources/locales/en.yml @@ -0,0 +1,4 @@ +--- +en: + greetings: "Howdy" + hi: "Hello" \ No newline at end of file diff --git a/middleman-core/fixtures/i18n-mixed-sources/locales/es.yml b/middleman-core/fixtures/i18n-mixed-sources/locales/es.yml new file mode 100644 index 00000000..38c7e31e --- /dev/null +++ b/middleman-core/fixtures/i18n-mixed-sources/locales/es.yml @@ -0,0 +1,4 @@ +--- +es: + greetings: "Como Esta?" + hi: "Hola" \ No newline at end of file diff --git a/middleman-core/fixtures/i18n-mixed-sources/source/a/sub.html.erb b/middleman-core/fixtures/i18n-mixed-sources/source/a/sub.html.erb new file mode 100644 index 00000000..b92d02f1 --- /dev/null +++ b/middleman-core/fixtures/i18n-mixed-sources/source/a/sub.html.erb @@ -0,0 +1,9 @@ +--- +title: a/sub.html.erb +--- +Current locale: <%= I18n.locale %> +path: <%= +hierarchy = [current_page] +hierarchy.unshift hierarchy.first.parent while hierarchy.first.parent +hierarchy.collect {|page| page.data.title }.join(" # ") +%> diff --git a/middleman-core/fixtures/i18n-mixed-sources/source/b/index.html.erb b/middleman-core/fixtures/i18n-mixed-sources/source/b/index.html.erb new file mode 100644 index 00000000..c238d925 --- /dev/null +++ b/middleman-core/fixtures/i18n-mixed-sources/source/b/index.html.erb @@ -0,0 +1,9 @@ +--- +title: b/index.html.erb +--- +Current locale: <%= I18n.locale %> +path: <%= +hierarchy = [current_page] +hierarchy.unshift hierarchy.first.parent while hierarchy.first.parent +hierarchy.collect {|page| page.data.title }.join(" # ") +%> diff --git a/middleman-core/fixtures/i18n-mixed-sources/source/index.html.erb b/middleman-core/fixtures/i18n-mixed-sources/source/index.html.erb new file mode 100644 index 00000000..1d47ed2e --- /dev/null +++ b/middleman-core/fixtures/i18n-mixed-sources/source/index.html.erb @@ -0,0 +1,9 @@ +--- +title: non-localized Home +--- +Current locale: <%= I18n.locale %> +path: <%= +hierarchy = [current_page] +hierarchy.unshift hierarchy.first.parent while hierarchy.first.parent +hierarchy.collect {|page| page.data.title }.join(" # ") +%> diff --git a/middleman-core/fixtures/i18n-mixed-sources/source/localizable/a/index.html.erb b/middleman-core/fixtures/i18n-mixed-sources/source/localizable/a/index.html.erb new file mode 100644 index 00000000..dc8aa51f --- /dev/null +++ b/middleman-core/fixtures/i18n-mixed-sources/source/localizable/a/index.html.erb @@ -0,0 +1,9 @@ +--- +title: a/index.html.erb +--- +Current locale: <%= I18n.locale %> +path: <%= +hierarchy = [current_page] +hierarchy.unshift hierarchy.first.parent while hierarchy.first.parent +hierarchy.collect {|page| page.data.title }.join(" # ") +%> diff --git a/middleman-core/fixtures/i18n-mixed-sources/source/localizable/b/sub.html.erb b/middleman-core/fixtures/i18n-mixed-sources/source/localizable/b/sub.html.erb new file mode 100644 index 00000000..40d65942 --- /dev/null +++ b/middleman-core/fixtures/i18n-mixed-sources/source/localizable/b/sub.html.erb @@ -0,0 +1,9 @@ +--- +title: b/sub.html.erb +--- +Current locale: <%= I18n.locale %> +path: <%= +hierarchy = [current_page] +hierarchy.unshift hierarchy.first.parent while hierarchy.first.parent +hierarchy.collect {|page| page.data.title }.join(" # ") +%> diff --git a/middleman-core/fixtures/i18n-mixed-sources/source/localizable/index.html.erb b/middleman-core/fixtures/i18n-mixed-sources/source/localizable/index.html.erb new file mode 100644 index 00000000..79449a82 --- /dev/null +++ b/middleman-core/fixtures/i18n-mixed-sources/source/localizable/index.html.erb @@ -0,0 +1,9 @@ +--- +title: is-localized Home +--- +Current locale: <%= I18n.locale %> +path: <%= +hierarchy = [current_page] +hierarchy.unshift hierarchy.first.parent while hierarchy.first.parent +hierarchy.collect {|page| page.data.title }.join(" # ") +%> diff --git a/middleman-core/fixtures/import-app/config.rb b/middleman-core/fixtures/import-app/config.rb index f41b6dff..7846f1e5 100644 --- a/middleman-core/fixtures/import-app/config.rb +++ b/middleman-core/fixtures/import-app/config.rb @@ -4,4 +4,4 @@ import_path File.expand_path("bower_components/", root) import_path File.expand_path("bower_components", root) do |target_path, original_path| target_path.sub('bower_components', 'bower_components2') -end \ No newline at end of file +end diff --git a/middleman-core/fixtures/indexable-app/config.rb b/middleman-core/fixtures/indexable-app/config.rb index fdccfcbb..17c25ecf 100644 --- a/middleman-core/fixtures/indexable-app/config.rb +++ b/middleman-core/fixtures/indexable-app/config.rb @@ -2,3 +2,4 @@ activate :directory_indexes page "/leave_me_alone.html", directory_index: false page "/wildcard*", directory_index: false +page /regex_.*/, directory_index: false diff --git a/middleman-core/fixtures/indexable-app/source/regex_leave_me_alone2.html b/middleman-core/fixtures/indexable-app/source/regex_leave_me_alone2.html new file mode 100644 index 00000000..5ff5317a --- /dev/null +++ b/middleman-core/fixtures/indexable-app/source/regex_leave_me_alone2.html @@ -0,0 +1 @@ +Stay away, wildcards! 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/relative-assets-app/source/stylesheets/fonts2.css.scss b/middleman-core/fixtures/relative-assets-app/source/stylesheets/fonts2.css.scss new file mode 100644 index 00000000..ef6c7dfe --- /dev/null +++ b/middleman-core/fixtures/relative-assets-app/source/stylesheets/fonts2.css.scss @@ -0,0 +1,10 @@ +@font-face { + font-family: 'Roboto'; + src: url(/fonts/roboto/roboto-regular-webfont.eot); + src: url(/fonts/roboto/roboto-regular-webfont.eot?#iefix) format('embedded-opentype'), + url(/fonts/roboto/roboto-regular-webfont.woff) format('woff'), + url(/fonts/roboto/roboto-regular-webfont.ttf) format('truetype'), + url(/fonts/roboto/roboto-regular-webfont.svg#robotoregular) format('svg'); + font-weight: normal; + font-style: normal; +} 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 ff2d4e4f..52b31c92 100644 --- a/middleman-core/lib/middleman-core/application.rb +++ b/middleman-core/lib/middleman-core/application.rb @@ -70,32 +70,36 @@ module Middleman # Which port preview should start on. # @return [Fixnum] - config.define_setting :port, 4567, 'The preview server port' + define_setting :port, 4567, 'The preview server port' # Which server name should be used # @return [NilClass, String] - config.define_setting :server_name, nil, 'The server name of preview server' + define_setting :server_name, nil, 'The server name of preview server' # Which bind address the preview server should use # @return [NilClass, String] - config.define_setting :bind_address, nil, 'The bind address of the preview server' + define_setting :bind_address, nil, 'The bind address of the preview server' # Whether to serve the preview server over HTTPS. # @return [Boolean] - config.define_setting :https, false, 'Serve the preview server over SSL/TLS' + define_setting :https, false, 'Serve the preview server over SSL/TLS' # The (optional) path to the SSL cert to use for the preview server. # @return [String] - config.define_setting :ssl_certificate, nil, 'Path to an X.509 certificate to use for the preview server' + define_setting :ssl_certificate, nil, 'Path to an X.509 certificate to use for the preview server' # The (optional) private key for the certificate in :ssl_certificate. # @return [String] - config.define_setting :ssl_private_key, nil, "Path to an RSA private key for the preview server's certificate" + define_setting :ssl_private_key, nil, "Path to an RSA private key for the preview server's certificate" # Name of the source directory # @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,21 +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 :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 @@ -208,65 +218,64 @@ 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_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 @sitemap = ::Middleman::Sitemap::Store.new(self) - if Object.const_defined?(:Encoding) - Encoding.default_internal = config[:encoding] - Encoding.default_external = config[:encoding] - end - ::Middleman::Extension.clear_after_extension_callbacks - after_configuration_eval(&method(:prune_tilt_templates)) - - start_lifecycle - end - - # Boot the app. - def start_lifecycle # Before config is parsed, before extensions get to it. execute_callbacks(:initialized) @@ -282,9 +291,17 @@ 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) + if Object.const_defined?(:Encoding) + Encoding.default_external = config[:encoding] + end + + prune_tilt_templates! + # After extensions have worked after_config execute_callbacks(:after_configuration) @@ -292,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,28 +344,36 @@ module Middleman end # Clean up missing Tilt exts - def prune_tilt_templates - ::Tilt.mappings.each do |key, _| + def prune_tilt_templates! + ::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 + # Whether we're in a specific mode + # @param [Symbol] key + # @return [Boolean] + Contract Symbol => Bool + def mode?(key) + config[:mode] == key + end + # Whether we're in server mode # @return [Boolean] If we're in dev mode Contract Bool def server? - config[:mode] == :server + mode?(:server) end # Whether we're in build mode # @return [Boolean] If we're in dev mode Contract Bool def build? - config[:mode] == :build + mode?(:build) end # Whether we're in a specific environment @@ -419,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 4166dcf5..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 @@ -219,8 +275,12 @@ module Middleman # Remove files which were not built in this cycle Contract ArrayOf[Pathname] - def clean - @to_clean.each do |f| + def clean! + to_remove = @to_clean.reject do |f| + app.config[:skip_build_clean].call(f.to_s) + end + + to_remove.each do |f| FileUtils.rm(f) trigger(:deleted, f) end 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 3afedbfe..55aa0aad 100644 --- a/middleman-core/lib/middleman-core/contracts.rb +++ b/middleman-core/lib/middleman-core/contracts.rb @@ -1,4 +1,4 @@ -if ENV['TEST'] || ENV['CONTRACTS'] == 'true' +if ENV['CONTRACTS'] != 'false' require 'contracts' require 'hamster' 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 3491d894..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,26 +84,36 @@ 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) + @lock.synchronize do + @collector_roots.each do |pair| + dataset = pair[:block].call(app, resources) + pair[:root].realize!(dataset) + end + + ctx = StepContext.new(app) + StepContext.current = ctx + + leaves = @leaves.dup + + @collectors_by_name.each do |k, v| + @values_by_name[k] = v.value(ctx) + leaves.delete v + end + + # 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 - - ctx = StepContext.new - leaves = @leaves.dup - - @collectors_by_name.each do |k, v| - @values_by_name[k] = v.value(ctx) - leaves.delete v - end - - # Execute code paths - leaves.each do |v| - v.value(ctx) - end - - # Inject descriptors - resources + ctx.descriptors.map { |d| d.to_resource(app) } 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 c33c0833..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 @@ -1,25 +1,31 @@ -require 'hamster' - 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 - @descriptors = ::Hamster::Set.empty + def initialize(app) + @app = app + @descriptors = [] end def method_missing(name, *args, &block) internal = :"_internal_#{name}" + if respond_to?(internal) - @descriptors = @descriptors.add(send(internal, *args, &block)) + send(internal, *args, &block).tap do |r| + @descriptors << r if r.respond_to?(:execute_descriptor) + end else - super + @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 f1762e43..d711ef4f 100644 --- a/middleman-core/lib/middleman-core/core_extensions/data.rb +++ b/middleman-core/lib/middleman-core/core_extensions/data.rb @@ -8,6 +8,8 @@ module Middleman class Data < Extension attr_reader :data_store + 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 @@ -21,7 +23,6 @@ module Middleman super @data_store = DataStore.new(app, DATA_FILE_MATCHER) - app.config.define_setting :data_dir, 'data', 'The directory data files are stored in' start_watching(app.config[:data_dir]) end @@ -56,6 +57,7 @@ module Middleman @app = app @data_file_matcher = data_file_matcher @local_data = {} + @local_data_enhanced = nil @local_sources = {} @callback_sources = {} end @@ -65,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 @@ -86,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 @@ -98,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 @@ -116,6 +120,8 @@ module Middleman end data_branch[basename] = data + + @local_data_enhanced = nil end # Remove a given file from the internal cache @@ -136,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] @@ -150,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 @@ -161,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 @@ -188,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 # @@ -197,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 8b10e456..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 @@ -17,6 +18,8 @@ class Padrino::Helpers::OutputHelpers::ErbHandler end class Middleman::CoreExtensions::DefaultHelpers < ::Middleman::Extension + define_setting :relative_links, false, 'Whether to generate relative links instead of absolute ones' + def initialize(app, options_hash={}, &block) super @@ -30,8 +33,6 @@ class Middleman::CoreExtensions::DefaultHelpers < ::Middleman::Extension ::Middleman::TemplateContext.send :include, ::Padrino::Helpers::RenderHelpers ::Middleman::TemplateContext.send :include, ::Padrino::Helpers::NumberHelpers # ::Middleman::TemplateContext.send :include, ::Padrino::Helpers::TranslationHelpers - - app.config.define_setting :relative_links, false, 'Whether to generate relative links instead of absolute ones' end # The helpers @@ -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 "" @@ -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 @@ -201,16 +202,16 @@ class Middleman::CoreExtensions::DefaultHelpers < ::Middleman::Extension # @param [String] prefix The type prefix (such as "images") # @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) - ::Middleman::Util.asset_url(app, prefix, options_with_resource) + def asset_url(path, prefix='', options={}) + options_with_resource = {}.merge!(options).merge!(current_resource: current_resource) + ::Middleman::Util.asset_url(app, path, prefix, options_with_resource) end # Given a source path (referenced either absolutely or relatively) # 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/external_helpers.rb b/middleman-core/lib/middleman-core/core_extensions/external_helpers.rb index 7f662aec..b4be1a68 100644 --- a/middleman-core/lib/middleman-core/core_extensions/external_helpers.rb +++ b/middleman-core/lib/middleman-core/core_extensions/external_helpers.rb @@ -2,17 +2,12 @@ module Middleman module CoreExtensions # Load helpers in `helpers/` class ExternalHelpers < Extension - def initialize(app, options_hash={}, &block) - super - - # Setup a default helpers paths - app.config.define_setting :helpers_dir, 'helpers', 'Directory to autoload helper modules from' - app.config.define_setting :helpers_filename_glob, '**.rb', 'Glob pattern for matching helper ruby files' - app.config.define_setting :helpers_filename_to_module_name_proc, proc { |filename| - basename = File.basename(filename, File.extname(filename)) - basename.camelcase - }, 'Proc implementing the conversion from helper filename to module name' - end + define_setting :helpers_dir, 'helpers', 'Directory to autoload helper modules from' + define_setting :helpers_filename_glob, '**.rb', 'Glob pattern for matching helper ruby files' + define_setting :helpers_filename_to_module_name_proc, proc { |filename| + basename = File.basename(filename, File.extname(filename)) + basename.camelcase + }, 'Proc implementing the conversion from helper filename to module name' def after_configuration helpers_path = File.join(app.root, app.config[:helpers_dir]) 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 b1488c1a..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,7 +34,9 @@ module Middleman::CoreExtensions Contract ResourceList => ResourceList def manipulate_resource_list(resources) resources.each do |resource| + 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 @@ -65,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 ada6dd0c..156f2694 100644 --- a/middleman-core/lib/middleman-core/core_extensions/i18n.rb +++ b/middleman-core/lib/middleman-core/core_extensions/i18n.rb @@ -10,7 +10,7 @@ class Middleman::CoreExtensions::Internationalization < ::Middleman::Extension option :data, 'locales', 'The directory holding your locale configurations' # Exposes `locales` to templates - expose_to_template :locales, :langs + expose_to_template :locales, :langs, :locale, :lang def initialize(*) super @@ -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,15 @@ class Middleman::CoreExtensions::Internationalization < ::Middleman::Extension end # Backwards API compat - alias_method :langs, :locales + alias langs locales + + Contract Symbol + def locale + ::I18n.locale + end + + # Backwards API compat + alias lang locale # Update the main sitemap resource list # @return Array @@ -177,10 +185,12 @@ class Middleman::CoreExtensions::Internationalization < ::Middleman::Extension sum[abs_path][desc.locale] = '/' + desc.path end - resources + new_resources.map { |r| r.to_resource(app) } + new_resources.reduce(resources) do |sum, r| + r.execute_descriptor(app, sum) + 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?('/') @@ -188,11 +198,23 @@ class Middleman::CoreExtensions::Internationalization < ::Middleman::Extension @lookup[lookup_path] && @lookup[lookup_path][locale] end + Contract Symbol => String + def path_root(locale) + if (options[:mount_at_root] == locale) || (options[:mount_at_root].nil? && locales[0] == locale) + '/' + else + replacement = options[:locale_map][locale] || locale + options[:path].sub(':locale', replacement.to_s).sub(':lang', replacement.to_s) # Backward compat + end + end + private 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 @@ -237,10 +259,10 @@ class Middleman::CoreExtensions::Internationalization < ::Middleman::Extension end LocalizedPageDescriptor = Struct.new(:path, :source_path, :locale) do - def to_resource(app) + def execute_descriptor(app, resources) r = ::Middleman::Sitemap::ProxyResource.new(app.sitemap, path, source_path) r.add_metadata options: { locale: locale } - r + resources + [r] end end @@ -254,17 +276,12 @@ 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)}" - prefix = if (options[:mount_at_root] == locale) || (options[:mount_at_root].nil? && locales[0] == locale) - '/' - else - replacement = options[:locale_map].fetch(locale, locale) - options[:path].sub(':locale', replacement.to_s).sub(':lang', replacement.to_s) # Backward compat - end + prefix = path_root(locale) # path needs to be changed if file has a localizable extension. (options[mount_at_root] == locale) path = ::Middleman::Util.normalize_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 db773d29..f47ac16b 100644 --- a/middleman-core/lib/middleman-core/core_extensions/routing.rb +++ b/middleman-core/lib/middleman-core/core_extensions/routing.rb @@ -1,32 +1,36 @@ # Routing extension module Middleman module CoreExtensions - class Routing < Extension + 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 - def initialize(app, options_hash={}, &block) - super + PageDescriptor = Struct.new(:path, :metadata) do + def execute_descriptor(app, resources) + normalized_path = path.dup - @page_configs = Set.new - end - - # @return Array - Contract ResourceList => ResourceList - def manipulate_resource_list(resources) - resources.each do |resource| - @page_configs.each do |p| - resource.add_metadata(p[:metadata]) if Middleman::Util.path_match(p[:path], "/#{resource.path}") + if normalized_path.is_a?(String) && !normalized_path.include?('*') + # Normalize path + normalized_path = ::Middleman::Util.normalize_path(normalized_path) + if normalized_path.end_with?('/') || app.files.by_type(:source).watchers.any? { |w| (w.directory + Pathname(normalized_path)).directory? } + normalized_path = ::File.join(normalized_path, app.config[:index_file]) + end end + + 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, true) } + + resources end end - PageDescriptor = Struct.new(:path, :metadata) - # The page method allows options to be set for a given source path, regex, or glob. # Options that may be set include layout, locals, andx ignore. # @@ -44,28 +48,21 @@ module Middleman # @option opts [Hash] locals Local variables for the template. These will be available when the template renders. # @option opts [Hash] data Extra metadata to add to the page. This is the same as frontmatter, though frontmatter will take precedence over metadata defined here. Available via {Resource#data}. # @return [void] - Contract String, Hash => Any + Contract Or[String, Regexp], Hash => PageDescriptor 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 = { - options: options, locals: options.delete(:locals) || {}, - page: options.delete(:data) || {} + page: page_data, + options: options } - if path.is_a?(String) && !path.include?('*') - # Normalize path - path = Middleman::Util.normalize_path(path) - if path.end_with?('/') || app.files.by_type(:source).watchers.any? { |w| (w.directory + Pathname(path)).directory? } - path = File.join(path, @app.config[:index_file]) - end - end - - path = '/' + Util.strip_leading_slash(path) if path.is_a?(String) - - @page_configs << PageDescriptor.new(path, metadata) + PageDescriptor.new(path, metadata) end end end diff --git a/middleman-core/lib/middleman-core/core_extensions/show_exceptions.rb b/middleman-core/lib/middleman-core/core_extensions/show_exceptions.rb index b6407100..fec4f7bf 100644 --- a/middleman-core/lib/middleman-core/core_extensions/show_exceptions.rb +++ b/middleman-core/lib/middleman-core/core_extensions/show_exceptions.rb @@ -3,13 +3,7 @@ require 'rack/showexceptions' # Support rack/showexceptions during development module Middleman::CoreExtensions class ShowExceptions < ::Middleman::Extension - def initialize(app, options_hash={}, &block) - super - - return if app.config.defines_setting? :show_exceptions - - app.config.define_setting :show_exceptions, ENV['TEST'] ? false : true, 'Whether to catch and display exceptions' - end + define_setting :show_exceptions, ENV['TEST'] ? false : true, 'Whether to catch and display exceptions' def ready app.use ::Rack::ShowExceptions if !app.build? && app.config[:show_exceptions] diff --git a/middleman-core/lib/middleman-core/extension.rb b/middleman-core/lib/middleman-core/extension.rb index a44273a3..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 @@ -135,6 +138,23 @@ module Middleman config.define_setting(key, default, description, options) end + # @api private + # @return [Middleman::Configuration::ConfigurationManager] The defined global options for this extension. + def global_config + @_global_config ||= ::Middleman::Configuration::ConfigurationManager.new + end + + # Add an global option to this extension. + # @see Middleman::Configuration::ConfigurationManager#define_setting + # @example + # option :compress, false, 'Whether to compress the output' + # @param [Symbol] key The name of the option + # @param [Object] default The default value for the option + # @param [String] description A human-readable description of what the option does + def define_setting(key, default=nil, description=nil, options={}) + global_config.define_setting(key, default, description, options) + end + # Short-hand for simple Sitemap manipulation # @example A generator which returns an array of resources # resources :make_resources @@ -385,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, @@ -415,7 +435,7 @@ module Middleman {} end - sum.merge(resource_definitions) + sum.merge!(resource_definitions) end resources + generator_defs.map do |path, g| @@ -469,4 +489,70 @@ module Middleman @app.ready(&method(:ready)) if respond_to?(:ready) end end + + class ConfigExtension < Extension + def initialize(app, config={}, &block) + @descriptors = {} + @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, &b| + proxy_method_call(k, v, args, &b) + end + end + + super + end + + def after_configuration + context = self + + self.class.exposed_to_config.each do |k, 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 + def manipulate_resource_list(resources) + @descriptors.values.flatten.reduce(resources) do |sum, c| + 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/extension_manager.rb b/middleman-core/lib/middleman-core/extension_manager.rb index 4e2040eb..cc297b38 100644 --- a/middleman-core/lib/middleman-core/extension_manager.rb +++ b/middleman-core/lib/middleman-core/extension_manager.rb @@ -9,6 +9,8 @@ module Middleman @app = app @activated = {} + ::Middleman::Extensions.load_settings(@app) + manager = self { diff --git a/middleman-core/lib/middleman-core/extensions.rb b/middleman-core/lib/middleman-core/extensions.rb index 42fbdb2d..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) @@ -120,6 +120,18 @@ module Middleman app.extensions.activate descriptor[:name] end end + + def load_settings(app) + registered.each do |name, _| + begin + ext = load(name) + unless ext.global_config.all_settings.empty? + app.config.load_settings(ext.global_config.all_settings) + end + rescue LoadError + end + end + end end end end diff --git a/middleman-core/lib/middleman-core/extensions/asset_hash.rb b/middleman-core/lib/middleman-core/extensions/asset_hash.rb index 8b9b606b..3e2a02aa 100644 --- a/middleman-core/lib/middleman-core/extensions/asset_hash.rb +++ b/middleman-core/lib/middleman-core/extensions/asset_hash.rb @@ -1,36 +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, - 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 @@ -71,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 d0487591..7cb83d07 100644 --- a/middleman-core/lib/middleman-core/extensions/asset_host.rb +++ b/middleman-core/lib/middleman-core/extensions/asset_host.rb @@ -1,25 +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, - 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 @@ -36,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 7cc41a36..2de11632 100644 --- a/middleman-core/lib/middleman-core/extensions/cache_buster.rb +++ b/middleman-core/lib/middleman-core/extensions/cache_buster.rb @@ -1,23 +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, - 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 63befc21..ae5e4c3c 100644 --- a/middleman-core/lib/middleman-core/extensions/external_pipeline.rb +++ b/middleman-core/lib/middleman-core/extensions/external_pipeline.rb @@ -10,34 +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] - end + path: source_path, + latency: options[:latency], + frontmatter: false + + @current_thread = nil + app.reload(&method(:reload!)) - def ready 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 2620eb6f..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), '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 = /(]*>\s*(?:\/\*\*\/)?\s*<\/style>)/m # Init # @param [Class] app # @param [Hash] options - Contract RespondTo[:call], ({ + Contract RespondTo[:call], { ignore: ArrayOf[PATH_MATCHER], inline: Bool, compressor: Or[Proc, RespondTo[:to_proc], RespondTo[:compress]] - }) => Any + } => Any def initialize(app, options={}) @app = app @ignore = options.fetch(:ignore) @@ -69,7 +72,7 @@ class Middleman::Extensions::MinifyCss < ::Middleman::Extension end if minified - headers['Content-Length'] = ::Rack::Utils.bytesize(minified).to_s + headers['Content-Length'] = minified.bytesize.to_s response = [minified] end @@ -82,8 +85,9 @@ class Middleman::Extensions::MinifyCss < ::Middleman::Extension # @param [String] path # @return [Boolean] def ignore?(path) - @ignore.any? { |ignore| Middleman::Util.path_match(ignore, path) } + @ignore.any? { |ignore| ::Middleman::Util.path_match(ignore, path) } end + memoize :ignore? # Whether this type of content can be minified # @param [String, nil] content_type @@ -91,6 +95,7 @@ class Middleman::Extensions::MinifyCss < ::Middleman::Extension def minifiable?(content_type) @content_types.include?(content_type) end + memoize :minifiable? # Whether this type of content contains inline content that can be minified # @param [String, nil] content_type @@ -98,6 +103,7 @@ class Middleman::Extensions::MinifyCss < ::Middleman::Extension def minifiable_inline?(content_type) @inline_content_types.include?(content_type) end + memoize :minifiable_inline? # Minify the content # @param [String] content @@ -105,6 +111,7 @@ class Middleman::Extensions::MinifyCss < ::Middleman::Extension def minify(content) @compressor.compress(content) end + memoize :minify # Detect and minify inline content # @param [String] content @@ -114,5 +121,6 @@ class Middleman::Extensions::MinifyCss < ::Middleman::Extension $1 + minify($2) + $3 end end + memoize :minify_inline end end diff --git a/middleman-core/lib/middleman-core/extensions/minify_javascript.rb b/middleman-core/lib/middleman-core/extensions/minify_javascript.rb index 73a75cd0..714d0645 100644 --- a/middleman-core/lib/middleman-core/extensions/minify_javascript.rb +++ b/middleman-core/lib/middleman-core/extensions/minify_javascript.rb @@ -1,4 +1,6 @@ +require 'active_support/core_ext/object/try' require 'middleman-core/contracts' +require 'memoist' # Minify Javascript Extension class Middleman::Extensions::MinifyJavascript < ::Middleman::Extension @@ -22,17 +24,18 @@ class Middleman::Extensions::MinifyJavascript < ::Middleman::Extension # Rack middleware to look for JS and compress it class Rack + extend Memoist include Contracts INLINE_JS_REGEX = /(]*>\s*(?:\/\/(?:(?:)|(?:\]\]>)))?\s*<\/script>)/m # Init # @param [Class] app # @param [Hash] options - Contract RespondTo[:call], ({ + Contract RespondTo[:call], { ignore: ArrayOf[PATH_MATCHER], inline: Bool, compressor: Or[Proc, RespondTo[:to_proc], RespondTo[:compress]] - }) => Any + } => Any def initialize(app, options={}) @app = app @ignore = options.fetch(:ignore) @@ -61,7 +64,7 @@ class Middleman::Extensions::MinifyJavascript < ::Middleman::Extension end if minified - headers['Content-Length'] = ::Rack::Utils.bytesize(minified).to_s + headers['Content-Length'] = minified.bytesize.to_s response = [minified] end @@ -76,6 +79,7 @@ class Middleman::Extensions::MinifyJavascript < ::Middleman::Extension def ignore?(path) @ignore.any? { |ignore| Middleman::Util.path_match(ignore, path) } end + memoize :ignore? # Whether this type of content can be minified # @param [String, nil] content_type @@ -83,6 +87,7 @@ class Middleman::Extensions::MinifyJavascript < ::Middleman::Extension def minifiable?(content_type) @content_types.include?(content_type) end + memoize :minifiable? # Whether this type of content contains inline content that can be minified # @param [String, nil] content_type @@ -90,6 +95,7 @@ class Middleman::Extensions::MinifyJavascript < ::Middleman::Extension def minifiable_inline?(content_type) @inline_content_types.include?(content_type) end + memoize :minifiable_inline? # Minify the content # @param [String] content @@ -100,6 +106,7 @@ class Middleman::Extensions::MinifyJavascript < ::Middleman::Extension warn "WARNING: Couldn't compress JavaScript in #{@path}: #{e.message}" content end + memoize :minify # Detect and minify inline content # @param [String] content @@ -112,12 +119,13 @@ class Middleman::Extensions::MinifyJavascript < ::Middleman::Extension # Only compress script tags that contain JavaScript (as opposed to # something like jQuery templates, identified with a "text/html" type). - if first.include?('