rake-pipeline-web-filters 0.5.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. data/.gitignore +2 -0
  2. data/Gemfile +1 -0
  3. data/Rakefile +8 -0
  4. data/lib/rake-pipeline-web-filters.rb +17 -3
  5. data/lib/rake-pipeline-web-filters/cache_buster_filter.rb +42 -0
  6. data/lib/rake-pipeline-web-filters/chained_filter.rb +116 -0
  7. data/lib/rake-pipeline-web-filters/coffee_script_filter.rb +41 -0
  8. data/lib/rake-pipeline-web-filters/filter_with_dependencies.rb +27 -0
  9. data/lib/rake-pipeline-web-filters/gzip_filter.rb +59 -0
  10. data/lib/rake-pipeline-web-filters/handlebars_filter.rb +62 -0
  11. data/lib/rake-pipeline-web-filters/helpers.rb +100 -18
  12. data/lib/rake-pipeline-web-filters/iife_filter.rb +38 -0
  13. data/lib/rake-pipeline-web-filters/less_filter.rb +55 -0
  14. data/lib/rake-pipeline-web-filters/markdown_filter.rb +70 -0
  15. data/lib/rake-pipeline-web-filters/minispade_filter.rb +21 -5
  16. data/lib/rake-pipeline-web-filters/neuter_filter.rb +110 -0
  17. data/lib/rake-pipeline-web-filters/sass_filter.rb +87 -0
  18. data/lib/rake-pipeline-web-filters/stylus_filter.rb +59 -0
  19. data/lib/rake-pipeline-web-filters/tilt_filter.rb +16 -3
  20. data/lib/rake-pipeline-web-filters/uglify_filter.rb +66 -0
  21. data/lib/rake-pipeline-web-filters/version.rb +1 -1
  22. data/lib/rake-pipeline-web-filters/yui_css_filter.rb +70 -0
  23. data/lib/rake-pipeline-web-filters/yui_javascript_filter.rb +59 -0
  24. data/rake-pipeline-web-filters.gemspec +10 -1
  25. data/spec/cache_buster_filter_spec.rb +105 -0
  26. data/spec/chained_filter_spec.rb +76 -0
  27. data/spec/coffee_script_filter_spec.rb +110 -0
  28. data/spec/gzip_filter_spec.rb +49 -0
  29. data/spec/handlebars_filter_spec.rb +70 -0
  30. data/spec/helpers_spec.rb +112 -18
  31. data/spec/iife_filter_spec.rb +55 -0
  32. data/spec/less_filter_spec.rb +59 -0
  33. data/spec/markdown_filter_spec.rb +86 -0
  34. data/spec/minispade_filter_spec.rb +47 -15
  35. data/spec/neuter_filter_spec.rb +204 -0
  36. data/spec/sass_filter_spec.rb +147 -0
  37. data/spec/spec_helper.rb +10 -1
  38. data/spec/stylus_filter_spec.rb +69 -0
  39. data/spec/tilt_filter_spec.rb +25 -1
  40. data/spec/uglify_filter_spec.rb +82 -0
  41. data/spec/yui_css_filter_spec.rb +88 -0
  42. data/spec/yui_javascript_filter_spec.rb +68 -0
  43. metadata +225 -19
  44. data/lib/rake-pipeline-web-filters/ordering_concat_filter.rb +0 -38
  45. data/lib/rake-pipeline-web-filters/sass_compiler.rb +0 -53
  46. data/spec/ordering_concat_filter_spec.rb +0 -39
  47. data/spec/sass_compiler_spec.rb +0 -89
@@ -0,0 +1,38 @@
1
+ module Rake::Pipeline::Web::Filters
2
+ # A filter that wraps input files in an IIFE (immediately invoked functional expression)
3
+ #
4
+ #
5
+ # @example
6
+ # !!!ruby
7
+ # Rake::Pipeline.build do
8
+ # input "app/assets", "**/*.js"
9
+ # output "public"
10
+ #
11
+ # # Wrap each file in: (function() { ... })();"
12
+ # filter Rake::Pipeline::Web::Filters::IifeFilter
13
+ # end
14
+ class IifeFilter < Rake::Pipeline::Filter
15
+ # @param [Proc] block a block to use as the Filter's
16
+ # {#output_name_generator}.
17
+ def initialize(&block)
18
+ block ||= proc { |input| input }
19
+ super(&block)
20
+ end
21
+
22
+ # Implement the {#generate_output} method required by
23
+ # the {Filter} API. Wraps each input in an IIFE.
24
+ #
25
+ # @param [Array<FileWrapper>] inputs an Array of
26
+ # {FileWrapper} objects representing the inputs to
27
+ # this filter.
28
+ # @param [FileWrapper] output a single {FileWrapper}
29
+ # object representing the output.
30
+ def generate_output(inputs, output)
31
+ inputs.each do |input|
32
+ output.write "(function() {\n"
33
+ output.write input.read
34
+ output.write "})();"
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,55 @@
1
+ require 'rake-pipeline-web-filters/filter_with_dependencies'
2
+
3
+ module Rake::Pipeline::Web::Filters
4
+ # A filter that accepts a series of inputs and translates
5
+ # them using the Tilt template interface, which will attempt
6
+ # to guess which template language to use based on the input
7
+ # file extension.
8
+ #
9
+ # Requires {https://rubygems.org/gems/less-js less-js}
10
+ #
11
+ # @example
12
+ # !!!ruby
13
+ # Rake::Pipeline.build do
14
+ # input "app/assets", "**/*.less"
15
+ # output "public"
16
+ #
17
+ # # Compile each less file with Less.js
18
+ # filter Rake::Pipeline::Web::Filters::LessFilter
19
+ # end
20
+ class LessFilter < Rake::Pipeline::Filter
21
+ include Rake::Pipeline::Web::Filters::FilterWithDependencies
22
+
23
+ # @return [Hash] a hash of options to pass to Less
24
+ # when compiling.
25
+ attr_reader :options
26
+
27
+ # @param [Proc] block a block to use as the Filter's
28
+ # {#output_name_generator}.
29
+ def initialize(options={}, context = nil, &block)
30
+ block ||= proc { |input| input.sub(/\.less$/, '.css') }
31
+ super(&block)
32
+ @options = options
33
+ end
34
+
35
+ # Implement the {#generate_output} method required by
36
+ # the {Filter} API. Attempts to compile each input file
37
+ # with LessJs.
38
+ #
39
+ # @param [Array<FileWrapper>] inputs an Array of
40
+ # {FileWrapper} objects representing the inputs to
41
+ # this filter.
42
+ # @param [FileWrapper] output a single {FileWrapper}
43
+ # object representing the output.
44
+ def generate_output(inputs, output)
45
+ parser = Less::Parser.new options
46
+ inputs.each do |input|
47
+ output.write parser.parse(input.read).to_css
48
+ end
49
+ end
50
+
51
+ def external_dependencies
52
+ [ 'less' ]
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,70 @@
1
+ module Rake::Pipeline::Web::Filters
2
+ # A filter that compiles input files written in Markdown
3
+ # to Markdown using the Redcarpet compiler.
4
+ #
5
+ # @example
6
+ # !!!ruby
7
+ # Rake::Pipeline.build do
8
+ # input "app/assets", "**/*.md"
9
+ # output "public"
10
+ #
11
+ # # Compile each .md file under the app/assets
12
+ # # directory.
13
+ # filter Rake::Pipeline::Web::Filters::MarkdownFilter
14
+ # end
15
+ #
16
+ class MarkdownFilter < Rake::Pipeline::Filter
17
+
18
+ # @param [Hash] options options to pass to the markdown
19
+ # compiler
20
+ # @see http://rubydoc.info/gems/redcarpet/2.0.0/frames for more information
21
+ # about options
22
+ # @option options [#call] :compiler If you wish to use a different
23
+ # Markdown compiler, you can do so by passing anything that responds
24
+ # to `:call`, which will be passed the Markdown text and any
25
+ # options (other than `:compiler`).
26
+ # @option options [Redcarpet::Render::Base] :renderer a
27
+ # Redcarpet renderer. Used only if using the default compiler.
28
+ # @param [Proc] block a block to use as the Filter's
29
+ # {#output_name_generator}.
30
+ def initialize(options={}, &block)
31
+ block ||= proc { |input| input.sub(/\.(md|mdown|mkdown|markdown)$/, '.html') }
32
+ super(&block)
33
+ @compiler = options.delete(:compiler)
34
+ @options = options
35
+ end
36
+
37
+ # Implement the {#generate_output} method required by
38
+ # the {Filter} API. Compiles each input file with Sass.
39
+ #
40
+ # @param [Array<FileWrapper>] inputs an Array of
41
+ # {FileWrapper} objects representing the inputs to
42
+ # this filter.
43
+ # @param [FileWrapper] output a single {FileWrapper}
44
+ # object representing the output.
45
+ def generate_output(inputs, output)
46
+ inputs.each do |input|
47
+ output.write compile(input.read)
48
+ end
49
+ end
50
+
51
+ private
52
+
53
+ def compile(markdown)
54
+ if @compiler
55
+ @compiler.call(markdown, @options)
56
+ else
57
+ default_compiler.render(markdown)
58
+ end
59
+ end
60
+
61
+ def default_compiler
62
+ @default_renderer ||= begin
63
+ require 'redcarpet'
64
+ renderer = @options.delete(:renderer) || Redcarpet::Render::HTML.new
65
+ Redcarpet::Markdown.new(renderer, @options)
66
+ end
67
+ end
68
+
69
+ end
70
+ end
@@ -9,7 +9,7 @@ module Rake::Pipeline::Web::Filters
9
9
  # output "public"
10
10
  #
11
11
  # # Wrap each JS file in a minispade.register closure.
12
- # filter Rake::Pipeleine::Web::Filters::MinispadeFilter
12
+ # filter Rake::Pipeline::Web::Filters::MinispadeFilter
13
13
  # end
14
14
  class MinispadeFilter < Rake::Pipeline::Filter
15
15
 
@@ -18,11 +18,18 @@ module Rake::Pipeline::Web::Filters
18
18
  # each outputted function; defaults to false.
19
19
  # @option options [Proc] :module_id_generator a proc to use to generate
20
20
  # the minispade module id.
21
+ # @option options [Boolean] :rewrite_requires If true, change calls to
22
+ # +require+ in the source to +minispade.require+.
23
+ # @option options [Boolean] :string If true, compiles the output as
24
+ # a String instead of a closure. This means that @sourceURL can be
25
+ # appended for good stack traces and debugging.
21
26
  def initialize(options = {})
22
27
  super()
23
- @use_strict = !!options[:use_strict]
28
+ @use_strict = options[:use_strict]
24
29
  @module_id_generator = options[:module_id_generator] ||
25
30
  proc { |input| input.fullpath.sub(Dir.pwd, '') }
31
+ @rewrite_requires = options[:rewrite_requires]
32
+ @string_module = options[:string]
26
33
  end
27
34
 
28
35
  # Implement the {#generate_output} method required by
@@ -37,9 +44,18 @@ module Rake::Pipeline::Web::Filters
37
44
  def generate_output(inputs, output)
38
45
  inputs.each do |input|
39
46
  code = input.read
40
- code = '"use strict"; ' + code if @use_strict
41
- function = "function() { #{code} }"
42
- ret = "minispade.register('#{@module_id_generator.call(input)}',#{function});"
47
+ code.gsub!(%r{^\s*require\s*\(\s*}, 'minispade.require(') if @rewrite_requires
48
+ code.gsub!(%r{^\s*requireAll\s*\(\s*}, 'minispade.requireAll(') if @rewrite_requires
49
+ code = %["use strict";\n] + code if @use_strict
50
+
51
+ module_id = @module_id_generator.call(input)
52
+
53
+ if @string_module
54
+ contents = %{(function() {#{code}\n})();\n//@ sourceURL=#{module_id}}.to_json
55
+ else
56
+ contents = "function() {#{code}\n}"
57
+ end
58
+ ret = "minispade.register('#{module_id}', #{contents});"
43
59
  output.write ret
44
60
  end
45
61
  end
@@ -0,0 +1,110 @@
1
+ module Rake::Pipeline::Web::Filters
2
+
3
+ class NeuterBatch
4
+ def initialize(config, known_files)
5
+ @config = config || {}
6
+ @known_files = known_files
7
+ @required = []
8
+ end
9
+
10
+ def file_wrapper(klass, *args)
11
+ file = klass.new(*args)
12
+ file.extend NeuterWrapper
13
+ file.batch(self)
14
+ file
15
+ end
16
+
17
+ def required(req)
18
+ unless @known_files.include?(req)
19
+ warn "Included '#{req}', which is not listed in :additional_dependencies. The pipeline may not invalidate properly."
20
+ end
21
+ @required << req
22
+ end
23
+
24
+ def required?(req)
25
+ @required.include?(req)
26
+ end
27
+
28
+ def strip_requires(source)
29
+ requires = []
30
+ regexp = @config[:require_regexp] || %r{^\s*require\(['"]([^'"]*)['"]\);?\s*}
31
+ # FIXME: This $1 may not be reliable with other regexps
32
+ source.gsub!(regexp){ requires << $1; '' }
33
+ requires
34
+ end
35
+
36
+ def transform_path(path, input)
37
+ @config[:path_transform] ? @config[:path_transform].call(path, input) : path
38
+ end
39
+
40
+ def closure_wrap(source)
41
+ @config[:closure_wrap] ? "(function() {\n#{source}\n})();\n\n" : source
42
+ end
43
+
44
+ def filename_comment(input)
45
+ @config[:filename_comment] ? @config[:filename_comment].call(input) + "\n" : ""
46
+ end
47
+ end
48
+
49
+ module NeuterWrapper
50
+ def batch(batch)
51
+ @batch = batch
52
+ @batch.required(fullpath)
53
+ end
54
+
55
+ def read
56
+ source = super
57
+
58
+ required_files = @batch.strip_requires(source).map do |req|
59
+ req_path = @batch.transform_path(req, self)
60
+ if req_path && !@batch.required?(File.expand_path(req_path, root))
61
+ @batch.file_wrapper(self.class, root, req_path, encoding).read
62
+ else
63
+ nil
64
+ end
65
+ end.compact
66
+
67
+ file = @batch.filename_comment(self) + @batch.closure_wrap(source)
68
+
69
+ (required_files << file).join("\n\n")
70
+ end
71
+ end
72
+
73
+ # A filter that takes files with requires and collapses them into a single
74
+ # file without requires.
75
+ #
76
+ # @example
77
+ # !!!ruby
78
+ # Rake::Pipeline.build do
79
+ # input "app/assets", "**/*.js"
80
+ # output "public"
81
+ #
82
+ # filter Rake::Pipeline::Web::Filters::NeuterFilter, "neutered.js"
83
+ # end
84
+ class NeuterFilter < Rake::Pipeline::ConcatFilter
85
+ def initialize(string=nil, config={}, &block)
86
+ if string.is_a?(Hash)
87
+ config = string
88
+ string = nil
89
+ end
90
+
91
+ @config = config
92
+
93
+ super(string, &block)
94
+ end
95
+
96
+ def generate_output(inputs, output)
97
+ inputs.each do |input|
98
+ known_files = [input.fullpath] + additional_dependencies(input)
99
+ batch = NeuterBatch.new(@config, known_files)
100
+ file = batch.file_wrapper(file_wrapper_class, input.root, input.path, input.encoding)
101
+ output.write file.read
102
+ end
103
+ end
104
+
105
+ def additional_dependencies(input)
106
+ method = @config[:additional_dependencies]
107
+ method ? method.call(input).map{|p| File.expand_path(p, input.root) } : []
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,87 @@
1
+ require 'rake-pipeline-web-filters/filter_with_dependencies'
2
+
3
+ module Rake::Pipeline::Web::Filters
4
+ # A filter that compiles input files written in SCSS
5
+ # to CSS using the Sass compiler and the Compass CSS
6
+ # framework.
7
+ #
8
+ # Requires {http://rubygems.org/gems/sass sass} and
9
+ # {http://rubygems.org/gems/compass compass}
10
+ #
11
+ # @example
12
+ # !!!ruby
13
+ # Rake::Pipeline.build do
14
+ # input "app/assets", "**/*.scss"
15
+ # output "public"
16
+ #
17
+ # # Compile each SCSS file under the app/assets
18
+ # # directory.
19
+ # filter Rake::Pipeline::Web::Filters::SassFilter
20
+ # end
21
+ class SassFilter < Rake::Pipeline::Filter
22
+ include Rake::Pipeline::Web::Filters::FilterWithDependencies
23
+
24
+ # @return [Hash] a hash of options to pass to Sass
25
+ # when compiling.
26
+ attr_reader :options, :additional_load_paths
27
+
28
+ # @param [Hash] options options to pass to the Sass
29
+ # compiler
30
+ # @option options [Array] :additional_load_paths a
31
+ # list of paths to append to Sass's :load_path.
32
+ # @param [Proc] block a block to use as the Filter's
33
+ # {#output_name_generator}.
34
+ def initialize(options={}, &block)
35
+ block ||= proc { |input| input.sub(/\.(scss|sass)$/, '.css') }
36
+ super(&block)
37
+
38
+ @options = compass_options
39
+ @additional_load_paths = Array(options.delete(:additional_load_paths))
40
+ @options[:load_paths].concat(@additional_load_paths)
41
+ @options.merge!(options)
42
+ end
43
+
44
+ # Implement the {#generate_output} method required by
45
+ # the {Filter} API. Compiles each input file with Sass.
46
+ #
47
+ # @param [Array<FileWrapper>] inputs an Array of
48
+ # {FileWrapper} objects representing the inputs to
49
+ # this filter.
50
+ # @param [FileWrapper] output a single {FileWrapper}
51
+ # object representing the output.
52
+ def generate_output(inputs, output)
53
+ inputs.each do |input|
54
+ output.write Sass.compile(input.read, sass_options_for_file(input))
55
+ end
56
+ end
57
+
58
+ def additional_dependencies(input)
59
+ engine = Sass::Engine.new(input.read, sass_options_for_file(input))
60
+ engine.dependencies.map { |dep| dep.options[:filename] }
61
+ end
62
+
63
+ private
64
+
65
+ def external_dependencies
66
+ [ 'sass', 'compass' ]
67
+ end
68
+
69
+ # @return the Sass options for the current Compass
70
+ # configuration.
71
+ def compass_options
72
+ Compass.add_project_configuration
73
+ Compass.configuration.to_sass_engine_options
74
+ end
75
+
76
+ # @return the Sass options for the given +file+.
77
+ # Adds a +:syntax+ option if the filter's options
78
+ # don't already include one.
79
+ def sass_options_for_file(file)
80
+ added_opts = {
81
+ :filename => file.fullpath,
82
+ :syntax => file.path.match(/\.sass$/) ? :sass : :scss
83
+ }
84
+ added_opts.merge(@options)
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,59 @@
1
+ require 'rake-pipeline-web-filters/filter_with_dependencies'
2
+
3
+ module Rake::Pipeline::Web::Filters
4
+ # A filter that compiles input files written in Stylus
5
+ # to CSS using the Stylus compiler
6
+ #
7
+ # Requires http://rubygems.org/gems/stylus
8
+ #
9
+ # You will need the `node` command on your runtime
10
+ # for this to work. See https://github.com/lucasmazza/ruby-stylus
11
+ # for more information.
12
+ #
13
+ # @example
14
+ # !!!ruby
15
+ # Rake::Pipeline.build do
16
+ # input "app/assets", "**/*.styl"
17
+ # output "public"
18
+ #
19
+ # # Compile each Stylus file under the app/assets
20
+ # # directory.
21
+ # filter Rake::Pipeline::Web::Filters::StylusFilter
22
+ # end
23
+ class StylusFilter < Rake::Pipeline::Filter
24
+ include Rake::Pipeline::Web::Filters::FilterWithDependencies
25
+
26
+ attr_reader :options
27
+
28
+ # @param [Hash] options options to pass to Stylus
29
+ # @option options [Array] :use Plugins to import from Node
30
+ # @option options [Boolean] :debug Output debug info
31
+ # @param [Proc] block a block to use as the Filter's
32
+ # {#output_name_generator}.
33
+ def initialize(options={}, &block)
34
+ block ||= proc { |input| input.sub(/\.(styl)$/, '.css') }
35
+ super(&block)
36
+ @options = options
37
+ end
38
+
39
+ def generate_output(inputs, output)
40
+ options.each do |key, value|
41
+ if key == :use
42
+ Stylus.send key, *value
43
+ next
44
+ end
45
+ Stylus.send "#{key}=", value
46
+ end
47
+ inputs.each do |input|
48
+ output.write Stylus.compile(input.read)
49
+ end
50
+ end
51
+
52
+ private
53
+
54
+ def external_dependencies
55
+ [ 'stylus' ]
56
+ end
57
+
58
+ end
59
+ end