bpm 0.1.0 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (213) hide show
  1. data/.gitmodules +3 -0
  2. data/Gemfile +0 -13
  3. data/TODO.md +1 -0
  4. data/bpm.gemspec +8 -3
  5. data/lib/bpm/cli/base.rb +22 -20
  6. data/lib/bpm/generator.rb +3 -3
  7. data/lib/bpm/libgems_ext/dependency_installer.rb +4 -4
  8. data/lib/bpm/libgems_ext/installer.rb +1 -1
  9. data/lib/bpm/package.rb +13 -9
  10. data/lib/bpm/pipeline/generated_asset.rb +30 -3
  11. data/lib/bpm/pipeline/transport_processor.rb +20 -12
  12. data/lib/bpm/pipeline.rb +17 -9
  13. data/lib/bpm/project.rb +42 -10
  14. data/lib/bpm/server.rb +5 -0
  15. data/lib/bpm/version.rb +1 -1
  16. data/lib/bpm.rb +2 -0
  17. data/lib/vendored_sprockets.rb +7 -0
  18. data/spec/cli/add_spec.rb +40 -37
  19. data/spec/cli/build_spec.rb +5 -5
  20. data/spec/cli/new_spec.rb +49 -23
  21. data/spec/cli/push_spec.rb +5 -5
  22. data/spec/cli/unpack_spec.rb +14 -14
  23. data/spec/fixtures/{badrake-0.8.7.spd → badrake-0.8.7.bpkg} +0 -0
  24. data/spec/fixtures/{builder-3.0.0.spd → builder-3.0.0.bpkg} +0 -0
  25. data/spec/fixtures/{bundler-1.1.pre.spd → bundler-1.1.pre.bpkg} +0 -0
  26. data/spec/fixtures/{coffee-1.0.1.pre.spd → coffee-1.0.1.pre.bpkg} +0 -0
  27. data/spec/fixtures/{core-test-0.4.9.spd → core-test-0.4.9.bpkg} +0 -0
  28. data/spec/fixtures/{custom_generator-1.0.spd → custom_generator-1.0.bpkg} +0 -0
  29. data/spec/fixtures/custom_name/MyProject.json +4 -0
  30. data/spec/fixtures/hello_world/css/dummy.css +3 -0
  31. data/spec/fixtures/{highline-1.6.1.spd → highline-1.6.1.bpkg} +0 -0
  32. data/spec/fixtures/{ivory-0.0.1.spd → ivory-0.0.1.bpkg} +0 -0
  33. data/spec/fixtures/{jquery-1.4.3.spd → jquery-1.4.3.bpkg} +0 -0
  34. data/spec/fixtures/minitest/assets/bpm_packages.js +1 -0
  35. data/spec/fixtures/minitest/assets/bpm_styles.css +0 -0
  36. data/spec/fixtures/minitest/assets/minitest/app_package.js +1 -0
  37. data/spec/fixtures/minitest/lib/main.js +1 -0
  38. data/spec/fixtures/minitest/minitest.json +22 -0
  39. data/spec/fixtures/minitest/packages/uglyduck/lib/main.js +1 -0
  40. data/spec/fixtures/minitest/packages/uglyduck/minifier/main.js +4 -0
  41. data/spec/fixtures/minitest/packages/uglyduck/package.json +21 -0
  42. data/spec/fixtures/{optparse-1.0.1.spd → optparse-1.0.1.bpkg} +0 -0
  43. data/spec/fixtures/{rake-0.8.6.spd → rake-0.8.6.bpkg} +0 -0
  44. data/spec/fixtures/{rake-0.8.7.spd → rake-0.8.7.bpkg} +0 -0
  45. data/spec/fixtures/{spade-0.5.0.spd → spade-0.5.0.bpkg} +0 -0
  46. data/spec/fixtures/transporter/packages/transport/lib/main.js +1 -0
  47. data/spec/fixtures/transporter/packages/transport/package.json +1 -1
  48. data/spec/fixtures/transporter/packages/transport/transports/wrapper.js +5 -0
  49. data/spec/gauntlet_spec.rb +2 -2
  50. data/spec/package_spec.rb +3 -3
  51. data/spec/pipeline_spec.rb +175 -54
  52. data/spec/project_spec.rb +19 -3
  53. data/spec/support/fake_gem_server.rb +4 -4
  54. data/templates/init/project.json +3 -1
  55. data/templates/project/index.html +1 -1
  56. data/vendor/sprockets/.gitignore +7 -0
  57. data/vendor/sprockets/.travis.yml +6 -0
  58. data/vendor/sprockets/Gemfile +8 -0
  59. data/vendor/sprockets/LICENSE +20 -0
  60. data/vendor/sprockets/README.md +22 -0
  61. data/vendor/sprockets/Rakefile +8 -0
  62. data/vendor/sprockets/lib/sprockets/asset.rb +203 -0
  63. data/vendor/sprockets/lib/sprockets/asset_attributes.rb +161 -0
  64. data/vendor/sprockets/lib/sprockets/base.rb +147 -0
  65. data/vendor/sprockets/lib/sprockets/bundled_asset.rb +222 -0
  66. data/vendor/sprockets/lib/sprockets/cache/file_store.rb +41 -0
  67. data/vendor/sprockets/lib/sprockets/caching.rb +121 -0
  68. data/vendor/sprockets/lib/sprockets/charset_normalizer.rb +41 -0
  69. data/vendor/sprockets/lib/sprockets/context.rb +191 -0
  70. data/vendor/sprockets/lib/sprockets/digest.rb +73 -0
  71. data/vendor/sprockets/lib/sprockets/directive_processor.rb +380 -0
  72. data/vendor/sprockets/lib/sprockets/eco_template.rb +39 -0
  73. data/vendor/sprockets/lib/sprockets/ejs_template.rb +38 -0
  74. data/vendor/sprockets/lib/sprockets/engines.rb +92 -0
  75. data/vendor/sprockets/lib/sprockets/environment.rb +93 -0
  76. data/vendor/sprockets/lib/sprockets/errors.rb +17 -0
  77. data/vendor/sprockets/lib/sprockets/index.rb +80 -0
  78. data/vendor/sprockets/lib/sprockets/jst_processor.rb +26 -0
  79. data/vendor/sprockets/lib/sprockets/processing.rb +310 -0
  80. data/vendor/sprockets/lib/sprockets/processor.rb +32 -0
  81. data/vendor/sprockets/lib/sprockets/safety_colons.rb +28 -0
  82. data/vendor/sprockets/lib/sprockets/server.rb +270 -0
  83. data/vendor/sprockets/lib/sprockets/static_asset.rb +87 -0
  84. data/vendor/sprockets/lib/sprockets/static_compilation.rb +82 -0
  85. data/vendor/sprockets/lib/sprockets/trail.rb +122 -0
  86. data/vendor/sprockets/lib/sprockets/utils.rb +67 -0
  87. data/vendor/sprockets/lib/sprockets/version.rb +3 -0
  88. data/vendor/sprockets/lib/sprockets.rb +31 -0
  89. data/vendor/sprockets/sprockets.gemspec +30 -0
  90. data/vendor/sprockets/test/fixtures/asset/POW.png +0 -0
  91. data/vendor/sprockets/test/fixtures/asset/application.js +6 -0
  92. data/vendor/sprockets/test/fixtures/asset/bar-utf8.css +2 -0
  93. data/vendor/sprockets/test/fixtures/asset/charset.css +2 -0
  94. data/vendor/sprockets/test/fixtures/asset/circle/a.js +2 -0
  95. data/vendor/sprockets/test/fixtures/asset/circle/b.js +2 -0
  96. data/vendor/sprockets/test/fixtures/asset/circle/c.js +2 -0
  97. data/vendor/sprockets/test/fixtures/asset/compat.js +4 -0
  98. data/vendor/sprockets/test/fixtures/asset/constants.js +2 -0
  99. data/vendor/sprockets/test/fixtures/asset/constants.yml +1 -0
  100. data/vendor/sprockets/test/fixtures/asset/default_mime_type.js +1 -0
  101. data/vendor/sprockets/test/fixtures/asset/filename.js.erb +1 -0
  102. data/vendor/sprockets/test/fixtures/asset/foo-utf8.css +2 -0
  103. data/vendor/sprockets/test/fixtures/asset/included_header.js +4 -0
  104. data/vendor/sprockets/test/fixtures/asset/jquery.tmpl.min.js +1 -0
  105. data/vendor/sprockets/test/fixtures/asset/mismatch.js +1 -0
  106. data/vendor/sprockets/test/fixtures/asset/multiple.js +2 -0
  107. data/vendor/sprockets/test/fixtures/asset/multipleengine.js +1 -0
  108. data/vendor/sprockets/test/fixtures/asset/noengine.js +1 -0
  109. data/vendor/sprockets/test/fixtures/asset/noformat.coffee +1 -0
  110. data/vendor/sprockets/test/fixtures/asset/one.css +1 -0
  111. data/vendor/sprockets/test/fixtures/asset/oneengine.js +1 -0
  112. data/vendor/sprockets/test/fixtures/asset/project.css +1 -0
  113. data/vendor/sprockets/test/fixtures/asset/project.js.erb +4 -0
  114. data/vendor/sprockets/test/fixtures/asset/relative/include.js +4 -0
  115. data/vendor/sprockets/test/fixtures/asset/relative/require.js +1 -0
  116. data/vendor/sprockets/test/fixtures/asset/relative/require_outside_path.js +1 -0
  117. data/vendor/sprockets/test/fixtures/asset/require_self.css +9 -0
  118. data/vendor/sprockets/test/fixtures/asset/require_self_twice.css +8 -0
  119. data/vendor/sprockets/test/fixtures/asset/semicolons/bar.js +1 -0
  120. data/vendor/sprockets/test/fixtures/asset/semicolons/index.js +5 -0
  121. data/vendor/sprockets/test/fixtures/asset/sprite.css.erb +12 -0
  122. data/vendor/sprockets/test/fixtures/asset/tree/all/b/c/d.js +2 -0
  123. data/vendor/sprockets/test/fixtures/asset/tree/all/b/c/e.js +1 -0
  124. data/vendor/sprockets/test/fixtures/asset/tree/all/b/c.js +1 -0
  125. data/vendor/sprockets/test/fixtures/asset/tree/all/b.css +2 -0
  126. data/vendor/sprockets/test/fixtures/asset/tree/all/b.js.erb +1 -0
  127. data/vendor/sprockets/test/fixtures/asset/tree/all/d/c.js.coffee +1 -0
  128. data/vendor/sprockets/test/fixtures/asset/tree/all/d/e.js +1 -0
  129. data/vendor/sprockets/test/fixtures/asset/tree/all_with_require.js +7 -0
  130. data/vendor/sprockets/test/fixtures/asset/tree/all_with_require_directory.js +1 -0
  131. data/vendor/sprockets/test/fixtures/asset/tree/all_with_require_tree.js +2 -0
  132. data/vendor/sprockets/test/fixtures/asset/tree/directory/application.js +2 -0
  133. data/vendor/sprockets/test/fixtures/asset/tree/directory/bar.js +1 -0
  134. data/vendor/sprockets/test/fixtures/asset/tree/directory/foo.js +1 -0
  135. data/vendor/sprockets/test/fixtures/asset/tree/tree/application.js +2 -0
  136. data/vendor/sprockets/test/fixtures/asset/tree/tree/bar.js +1 -0
  137. data/vendor/sprockets/test/fixtures/asset/tree/tree/foo.js +1 -0
  138. data/vendor/sprockets/test/fixtures/asset/tree/with_logical_path/a/a.js +1 -0
  139. data/vendor/sprockets/test/fixtures/asset/tree/with_logical_path/require_tree_with_logical_path.js +1 -0
  140. data/vendor/sprockets/test/fixtures/asset/tree/without_argument/a.js +1 -0
  141. data/vendor/sprockets/test/fixtures/asset/tree/without_argument/b.js +1 -0
  142. data/vendor/sprockets/test/fixtures/asset/tree/without_argument/require_tree_without_argument.js +1 -0
  143. data/vendor/sprockets/test/fixtures/asset/two.css +1 -0
  144. data/vendor/sprockets/test/fixtures/asset/unicode.js +2 -0
  145. data/vendor/sprockets/test/fixtures/asset/unknownexts.min.js +2 -0
  146. data/vendor/sprockets/test/fixtures/asset/users.js.erb.str +4 -0
  147. data/vendor/sprockets/test/fixtures/context/POW.png +0 -0
  148. data/vendor/sprockets/test/fixtures/context/application.js.yml +3 -0
  149. data/vendor/sprockets/test/fixtures/context/bar.js +1 -0
  150. data/vendor/sprockets/test/fixtures/context/foo.js +2 -0
  151. data/vendor/sprockets/test/fixtures/context/helpers.css.erb +3 -0
  152. data/vendor/sprockets/test/fixtures/context/properties.js.erb +7 -0
  153. data/vendor/sprockets/test/fixtures/context/require_glob.js +1 -0
  154. data/vendor/sprockets/test/fixtures/context/resolve_content_type.js.erb +4 -0
  155. data/vendor/sprockets/test/fixtures/context/sprite.css.embed +3 -0
  156. data/vendor/sprockets/test/fixtures/default/application.js.coffee +4 -0
  157. data/vendor/sprockets/test/fixtures/default/coffee/foo.coffee +1 -0
  158. data/vendor/sprockets/test/fixtures/default/coffee/index.js +3 -0
  159. data/vendor/sprockets/test/fixtures/default/empty +0 -0
  160. data/vendor/sprockets/test/fixtures/default/gallery.css.erb +3 -0
  161. data/vendor/sprockets/test/fixtures/default/gallery.js +1 -0
  162. data/vendor/sprockets/test/fixtures/default/goodbye.jst.eco +1 -0
  163. data/vendor/sprockets/test/fixtures/default/hello.jst.ejs +1 -0
  164. data/vendor/sprockets/test/fixtures/default/hello.txt +1 -0
  165. data/vendor/sprockets/test/fixtures/default/interpolation.js +1 -0
  166. data/vendor/sprockets/test/fixtures/default/missing_require.js +1 -0
  167. data/vendor/sprockets/test/fixtures/default/mobile/a.js +1 -0
  168. data/vendor/sprockets/test/fixtures/default/mobile/b.js +1 -0
  169. data/vendor/sprockets/test/fixtures/default/mobile/c.css +1 -0
  170. data/vendor/sprockets/test/fixtures/default/mobile/d.css +1 -0
  171. data/vendor/sprockets/test/fixtures/default/mobile/index.css +3 -0
  172. data/vendor/sprockets/test/fixtures/default/mobile/index.js +1 -0
  173. data/vendor/sprockets/test/fixtures/default/noreturn.js +1 -0
  174. data/vendor/sprockets/test/fixtures/default/project.js.coffee.erb +2 -0
  175. data/vendor/sprockets/test/fixtures/directives/code_before_comment +3 -0
  176. data/vendor/sprockets/test/fixtures/directives/comment_without_directives +6 -0
  177. data/vendor/sprockets/test/fixtures/directives/directive_word_splitting +6 -0
  178. data/vendor/sprockets/test/fixtures/directives/directives_after_header +16 -0
  179. data/vendor/sprockets/test/fixtures/directives/double_slash +9 -0
  180. data/vendor/sprockets/test/fixtures/directives/hash +8 -0
  181. data/vendor/sprockets/test/fixtures/directives/no_header +2 -0
  182. data/vendor/sprockets/test/fixtures/directives/slash_star +10 -0
  183. data/vendor/sprockets/test/fixtures/directives/slash_star_single +4 -0
  184. data/vendor/sprockets/test/fixtures/directives/space_between_directive_word +2 -0
  185. data/vendor/sprockets/test/fixtures/directives/triple_hash +10 -0
  186. data/vendor/sprockets/test/fixtures/encoding/ascii.js +1 -0
  187. data/vendor/sprockets/test/fixtures/encoding/ascii_utf8.js +2 -0
  188. data/vendor/sprockets/test/fixtures/encoding/utf16.js +0 -0
  189. data/vendor/sprockets/test/fixtures/encoding/utf8.js +1 -0
  190. data/vendor/sprockets/test/fixtures/encoding/utf8_bom.js +1 -0
  191. data/vendor/sprockets/test/fixtures/engines/hello.alert +1 -0
  192. data/vendor/sprockets/test/fixtures/engines/moo.js.str +1 -0
  193. data/vendor/sprockets/test/fixtures/public/compiled-digest-0aa2105d29558f3eb790d411d7d8fb66.js +3 -0
  194. data/vendor/sprockets/test/fixtures/public/compiled-digest-1c41eb0cf934a0c76babe875f982f9d1.js +1 -0
  195. data/vendor/sprockets/test/fixtures/server/app/javascripts/application.js +5 -0
  196. data/vendor/sprockets/test/fixtures/server/app/javascripts/bar.js +1 -0
  197. data/vendor/sprockets/test/fixtures/server/app/javascripts/foo.js +1 -0
  198. data/vendor/sprockets/test/fixtures/server/app/javascripts/hello.txt +2 -0
  199. data/vendor/sprockets/test/fixtures/server/app/javascripts/tree.js +1 -0
  200. data/vendor/sprockets/test/fixtures/server/vendor/javascripts/missing_require.js +1 -0
  201. data/vendor/sprockets/test/fixtures/server/vendor/stylesheets/missing_require.css +1 -0
  202. data/vendor/sprockets/test/sprockets_test.rb +56 -0
  203. data/vendor/sprockets/test/test_asset.rb +593 -0
  204. data/vendor/sprockets/test/test_asset_attributes.rb +86 -0
  205. data/vendor/sprockets/test/test_caching.rb +62 -0
  206. data/vendor/sprockets/test/test_context.rb +115 -0
  207. data/vendor/sprockets/test/test_directive_processor.rb +124 -0
  208. data/vendor/sprockets/test/test_encoding.rb +65 -0
  209. data/vendor/sprockets/test/test_engines.rb +73 -0
  210. data/vendor/sprockets/test/test_environment.rb +610 -0
  211. data/vendor/sprockets/test/test_server.rb +227 -0
  212. metadata +258 -54
  213. data/spec/fixtures/transporter/packages/transport/lib/wrapper.js +0 -5
@@ -0,0 +1,41 @@
1
+ require 'tilt'
2
+
3
+ module Sprockets
4
+ # Some browsers have issues with stylesheets that contain multiple
5
+ # `@charset` definitions. The issue surfaces while using Sass since
6
+ # it inserts a `@charset` at the top of each file. Then Sprockets
7
+ # concatenates them together.
8
+ #
9
+ # The `CharsetNormalizer` processor strips out multiple `@charset`
10
+ # definitions.
11
+ #
12
+ # The current implementation is naive. It picks the first `@charset`
13
+ # it sees and strips the others. This works for most people because
14
+ # the other definitions are usually `UTF-8`. A more sophisticated
15
+ # approach would be to re-encode stylesheets with mixed encodings.
16
+ #
17
+ # This behavior can be disabled with:
18
+ #
19
+ # environment.unregister_bundle_processor 'text/css', Sprockets::CharsetNormalizer
20
+ #
21
+ class CharsetNormalizer < Tilt::Template
22
+ def prepare
23
+ end
24
+
25
+ def evaluate(context, locals, &block)
26
+ charset = nil
27
+
28
+ # Find and strip out any `@charset` definitions
29
+ filtered_data = data.gsub(/^@charset "([^"]+)";$/) {
30
+ charset ||= $1; ""
31
+ }
32
+
33
+ if charset
34
+ # If there was a charset, move it to the top
35
+ "@charset \"#{charset}\";#{filtered_data}"
36
+ else
37
+ data
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,191 @@
1
+ require 'sprockets/errors'
2
+ require 'sprockets/utils'
3
+ require 'pathname'
4
+ require 'set'
5
+
6
+ module Sprockets
7
+ # `Context` provides helper methods to all `Tilt` processors. They
8
+ # are typically accessed by ERB templates. You can mix in custom
9
+ # helpers by injecting them into `Environment#context_class`. Do not
10
+ # mix them into `Context` directly.
11
+ #
12
+ # environment.instance_eval do
13
+ # include MyHelper
14
+ # def asset_url; end
15
+ # end
16
+ #
17
+ # <%= asset_url "foo.png" %>
18
+ #
19
+ # The `Context` also collects dependencies declared by
20
+ # assets. See `DirectiveProcessor` for an example of this.
21
+ class Context
22
+ attr_reader :environment, :pathname
23
+ attr_reader :_required_paths, :_dependency_paths
24
+ attr_writer :__LINE__
25
+
26
+ def initialize(environment, logical_path, pathname)
27
+ @environment = environment
28
+ @logical_path = logical_path
29
+ @pathname = pathname
30
+ @__LINE__ = nil
31
+
32
+ @_required_paths = []
33
+ @_dependency_paths = Set.new([pathname.to_s])
34
+ end
35
+
36
+ # Returns the environment path that contains the file.
37
+ #
38
+ # If `app/javascripts` and `app/stylesheets` are in your path, and
39
+ # current file is `app/javascripts/foo/bar.js`, `root_path` would
40
+ # return `app/javascripts`.
41
+ def root_path
42
+ environment.paths.detect { |path| pathname.to_s[path] }
43
+ end
44
+
45
+ # Returns logical path without any file extensions.
46
+ #
47
+ # 'app/javascripts/application.js'
48
+ # # => 'application'
49
+ #
50
+ def logical_path
51
+ @logical_path[/^([^.]+)/, 0]
52
+ end
53
+
54
+ # Returns content type of file
55
+ #
56
+ # 'application/javascript'
57
+ # 'text/css'
58
+ #
59
+ def content_type
60
+ environment.content_type_of(pathname)
61
+ end
62
+
63
+ # Given a logical path, `resolve` will find and return the fully
64
+ # expanded path. Relative paths will also be resolved. An optional
65
+ # `:content_type` restriction can be supplied to restrict the
66
+ # search.
67
+ #
68
+ # resolve("foo.js")
69
+ # # => "/path/to/app/javascripts/foo.js"
70
+ #
71
+ # resolve("./bar.js")
72
+ # # => "/path/to/app/javascripts/bar.js"
73
+ #
74
+ def resolve(path, options = {}, &block)
75
+ pathname = Pathname.new(path)
76
+ attributes = environment.attributes_for(pathname)
77
+
78
+ if pathname.absolute?
79
+ pathname
80
+
81
+ elsif content_type = options[:content_type]
82
+ content_type = self.content_type if content_type == :self
83
+
84
+ if attributes.format_extension
85
+ if content_type != attributes.content_type
86
+ raise ContentTypeMismatch, "#{path} is " +
87
+ "'#{attributes.content_type}', not '#{content_type}'"
88
+ end
89
+ end
90
+
91
+ resolve(path) do |candidate|
92
+ if self.content_type == environment.content_type_of(candidate)
93
+ return candidate
94
+ end
95
+ end
96
+
97
+ raise FileNotFound, "couldn't find file '#{path}'"
98
+ else
99
+ environment.resolve(path, :base_path => self.pathname.dirname, &block)
100
+ end
101
+ end
102
+
103
+ # `depend_on` allows you to state a dependency on a file without
104
+ # including it.
105
+ #
106
+ # This is used for caching purposes. Any changes made to
107
+ # the dependency file with invalidate the cache of the
108
+ # source file.
109
+ def depend_on(path)
110
+ @_dependency_paths << resolve(path).to_s
111
+ end
112
+
113
+ # Reads `path` and runs processors on the file.
114
+ #
115
+ # This allows you to capture the result of an asset and include it
116
+ # directly in another.
117
+ #
118
+ # <%= evaluate "bar.js" %>
119
+ #
120
+ def evaluate(path, options = {})
121
+ start_time = Time.now.to_f
122
+ pathname = resolve(path)
123
+ attributes = environment.attributes_for(pathname)
124
+ processors = options[:processors] || attributes.processors
125
+
126
+ if options[:data]
127
+ result = options[:data]
128
+ else
129
+ result = Sprockets::Utils.read_unicode(pathname)
130
+ end
131
+
132
+ processors.each do |processor|
133
+ begin
134
+ template = processor.new(pathname.to_s) { result }
135
+ result = template.render(self, {})
136
+ rescue Exception => e
137
+ annotate_exception! e
138
+ raise
139
+ end
140
+ end
141
+
142
+ elapsed_time = ((Time.now.to_f - start_time) * 1000).to_i
143
+ logger.info "Compiled #{attributes.pretty_path} (#{elapsed_time}ms) (pid #{Process.pid})"
144
+
145
+ result
146
+ end
147
+
148
+ # Tests if target path is able to be safely required into the
149
+ # current concatenation.
150
+ def asset_requirable?(path)
151
+ pathname = resolve(path)
152
+ content_type = environment.content_type_of(pathname)
153
+ pathname.file? && (self.content_type.nil? || self.content_type == content_type)
154
+ end
155
+
156
+ # `require_asset` declares `path` as a dependency of the file. The
157
+ # dependency will be inserted before the file and will only be
158
+ # included once.
159
+ #
160
+ # If ERB processing is enabled, you can use it to dynamically
161
+ # require assets.
162
+ #
163
+ # <%= require_asset "#{framework}.js" %>
164
+ #
165
+ def require_asset(path)
166
+ pathname = resolve(path, :content_type => :self)
167
+
168
+ unless @_required_paths.include?(pathname.to_s)
169
+ @_dependency_paths << pathname.to_s
170
+ @_required_paths << pathname.to_s
171
+ end
172
+
173
+ pathname
174
+ end
175
+
176
+ private
177
+ # Annotates exception backtrace with the original template that
178
+ # the exception was raised in.
179
+ def annotate_exception!(exception)
180
+ location = pathname.to_s
181
+ location << ":#{@__LINE__}" if @__LINE__
182
+
183
+ exception.extend(Sprockets::EngineError)
184
+ exception.sprockets_annotation = " (in #{location})"
185
+ end
186
+
187
+ def logger
188
+ environment.logger
189
+ end
190
+ end
191
+ end
@@ -0,0 +1,73 @@
1
+ module Sprockets
2
+ # `Digest` is an internal mixin whose public methods are exposed on
3
+ # the `Environment` and `Index` classes.
4
+ module Digest
5
+ # Returns a `Digest` implementation class.
6
+ #
7
+ # Defaults to `Digest::MD5`.
8
+ def digest_class
9
+ @digest_class
10
+ end
11
+
12
+ # Assign a `Digest` implementation class. This maybe any Ruby
13
+ # `Digest::` implementation such as `Digest::MD5` or
14
+ # `Digest::SHA1`.
15
+ #
16
+ # environment.digest_class = Digest::SHA1
17
+ #
18
+ def digest_class=(klass)
19
+ expire_index!
20
+ @digest_class = klass
21
+ end
22
+
23
+ # The `Environment#version` is a custom value used for manually
24
+ # expiring all asset caches.
25
+ #
26
+ # Sprockets is able to track most file and directory changes and
27
+ # will take care of expiring the cache for you. However, its
28
+ # impossible to know when any custom helpers change that you mix
29
+ # into the `Context`.
30
+ #
31
+ # It would be wise to increment this value anytime you make a
32
+ # configuration change to the `Environment` object.
33
+ def version
34
+ @version
35
+ end
36
+
37
+ # Assign an environment version.
38
+ #
39
+ # environment.version = '2.0'
40
+ #
41
+ def version=(version)
42
+ expire_index!
43
+ @version = version
44
+ end
45
+
46
+ # Returns a `Digest` instance for the `Environment`.
47
+ #
48
+ # This value serves two purposes. If two `Environment`s have the
49
+ # same digest value they can be treated as equal. This is more
50
+ # useful for comparing environment states between processes rather
51
+ # than in the same. Two equal `Environment`s can share the same
52
+ # cached assets.
53
+ #
54
+ # The value also provides a seed digest for all `Asset`
55
+ # digests. Any change in the environment digest will affect all of
56
+ # its assets.
57
+ def digest
58
+ # Recompute digest for the first time or again after its been cleared
59
+ @digest ||= compute_digest
60
+
61
+ # Returned a dupped copy so the caller can safely mutate it with `.update`
62
+ @digest.dup
63
+ end
64
+
65
+ protected
66
+ def compute_digest
67
+ # Compute the initial digest using the implementation
68
+ # class. The Sprockets release version and custom environment
69
+ # version are mixed in.
70
+ digest_class.new.update(VERSION).update(version.to_s)
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,380 @@
1
+ require 'pathname'
2
+ require 'shellwords'
3
+ require 'tilt'
4
+ require 'yaml'
5
+
6
+ module Sprockets
7
+ # The `DirectiveProcessor` is responsible for parsing and evaluating
8
+ # directive comments in a source file.
9
+ #
10
+ # A directive comment starts with a comment prefix, followed by an "=",
11
+ # then the directive name, then any arguments.
12
+ #
13
+ # // JavaScript
14
+ # //= require "foo"
15
+ #
16
+ # # CoffeeScript
17
+ # #= require "bar"
18
+ #
19
+ # /* CSS
20
+ # *= require "baz"
21
+ # */
22
+ #
23
+ # The Processor is implemented as a `Tilt::Template` and is loosely
24
+ # coupled to Sprockets. This makes it possible to disable or modify
25
+ # the processor to do whatever you'd like. You could add your own
26
+ # custom directives or invent your own directive syntax.
27
+ #
28
+ # `Environment#processors` includes `DirectiveProcessor` by default.
29
+ #
30
+ # To remove the processor entirely:
31
+ #
32
+ # env.unregister_processor('text/css', Sprockets::DirectiveProcessor)
33
+ # env.unregister_processor('application/javascript', Sprockets::DirectiveProcessor)
34
+ #
35
+ # Then inject your own preprocessor:
36
+ #
37
+ # env.register_processor('text/css', MyProcessor)
38
+ #
39
+ class DirectiveProcessor < Tilt::Template
40
+ attr_reader :pathname
41
+
42
+ def prepare
43
+ @pathname = Pathname.new(file)
44
+
45
+ @directive_parser = Parser.new(data)
46
+ @included_pathnames = []
47
+ @compat = false
48
+ end
49
+
50
+ # Implemented for Tilt#render.
51
+ #
52
+ # `context` is a `Context` instance with methods that allow you to
53
+ # access the environment and append to the bundle. See `Context`
54
+ # for the complete API.
55
+ def evaluate(context, locals, &block)
56
+ @context = context
57
+
58
+ @result = ""
59
+ @has_written_body = false
60
+
61
+ process_directives
62
+ process_source
63
+
64
+ @result
65
+ end
66
+
67
+ def processed_header
68
+ @directive_parser.processed_header
69
+ end
70
+
71
+ def processed_body
72
+ @directive_parser.body
73
+ end
74
+
75
+ def processed_source
76
+ @directive_parser.processed_source
77
+ end
78
+
79
+ def directives
80
+ @directive_parser.directives
81
+ end
82
+
83
+ protected
84
+ class Parser
85
+ # Directives will only be picked up if they are in the header
86
+ # of the source file. C style (/* */), JavaScript (//), and
87
+ # Ruby (#) comments are supported.
88
+ #
89
+ # Directives in comments after the first non-whitespace line
90
+ # of code will not be processed.
91
+ #
92
+ HEADER_PATTERN = /
93
+ \A (
94
+ (?m:\s*) (
95
+ (\/\* (?m:.*?) \*\/) |
96
+ (\#\#\# (?m:.*?) \#\#\#) |
97
+ (\/\/ .* \n?)+ |
98
+ (\# .* \n?)+
99
+ )
100
+ )+
101
+ /x
102
+
103
+ # Directives are denoted by a `=` followed by the name, then
104
+ # argument list.
105
+ #
106
+ # A few different styles are allowed:
107
+ #
108
+ # // =require foo
109
+ # //= require foo
110
+ # //= require "foo"
111
+ #
112
+ DIRECTIVE_PATTERN = /
113
+ ^ [\W]* = \s* (\w+.*?) (\*\/)? $
114
+ /x
115
+
116
+ attr_reader :source, :header, :body
117
+
118
+ def initialize(source)
119
+ @source = source
120
+ @header = @source[HEADER_PATTERN, 0] || ""
121
+ @body = $' || @source
122
+
123
+ # Ensure body ends in a new line
124
+ @body += "\n" if @body != "" && @body !~ /\n\Z/m
125
+ end
126
+
127
+ def header_lines
128
+ @header_lines ||= header.split("\n")
129
+ end
130
+
131
+ # Returns the header String with any directives stripped.
132
+ def processed_header
133
+ header_lines.reject do |line|
134
+ extract_directive(line)
135
+ end.join("\n")
136
+ end
137
+
138
+ # Returns the source String with any directives stripped.
139
+ def processed_source
140
+ @processed_source ||= processed_header + body
141
+ end
142
+
143
+ # Returns an Array of directive structures. Each structure
144
+ # is an Array with the line number as the first element, the
145
+ # directive name as the second element, followed by any
146
+ # arguments.
147
+ #
148
+ # [[1, "require", "foo"], [2, "require", "bar"]]
149
+ #
150
+ def directives
151
+ @directives ||= header_lines.each_with_index.map do |line, index|
152
+ if directive = extract_directive(line)
153
+ [index + 1, *Shellwords.shellwords(directive)]
154
+ end
155
+ end.compact
156
+ end
157
+
158
+ def extract_directive(line)
159
+ line[DIRECTIVE_PATTERN, 1]
160
+ end
161
+ end
162
+
163
+ attr_reader :included_pathnames
164
+ attr_reader :context
165
+
166
+ # Gathers comment directives in the source and processes them.
167
+ # Any directive method matching `process_*_directive` will
168
+ # automatically be available. This makes it easy to extend the
169
+ # processor.
170
+ #
171
+ # To implement a custom directive called `require_glob`, subclass
172
+ # `Sprockets::DirectiveProcessor`, then add a method called
173
+ # `process_require_glob_directive`.
174
+ #
175
+ # class DirectiveProcessor < Sprockets::DirectiveProcessor
176
+ # def process_require_glob_directive
177
+ # Dir["#{pathname.dirname}/#{glob}"].sort.each do |filename|
178
+ # require(filename)
179
+ # end
180
+ # end
181
+ # end
182
+ #
183
+ # Replace the current processor on the environment with your own:
184
+ #
185
+ # env.unregister_processor('text/css', Sprockets::DirectiveProcessor)
186
+ # env.register_processor('text/css', DirectiveProcessor)
187
+ #
188
+ def process_directives
189
+ directives.each do |line_number, name, *args|
190
+ context.__LINE__ = line_number
191
+ send("process_#{name}_directive", *args)
192
+ context.__LINE__ = nil
193
+ end
194
+ end
195
+
196
+ def process_source
197
+ unless @has_written_body || processed_header.empty?
198
+ @result << processed_header << "\n"
199
+ end
200
+
201
+ included_pathnames.each do |pathname|
202
+ @result << context.evaluate(pathname)
203
+ end
204
+
205
+ unless @has_written_body
206
+ @result << processed_body
207
+ end
208
+
209
+ if compat? && constants.any?
210
+ @result.gsub!(/<%=(.*?)%>/) { constants[$1.strip] }
211
+ end
212
+ end
213
+
214
+ # The `require` directive functions similar to Ruby's own `require`.
215
+ # It provides a way to declare a dependency on a file in your path
216
+ # and ensures its only loaded once before the source file.
217
+ #
218
+ # `require` works with files in the environment path:
219
+ #
220
+ # //= require "foo.js"
221
+ #
222
+ # Extensions are optional. If your source file is ".js", it
223
+ # assumes you are requiring another ".js".
224
+ #
225
+ # //= require "foo"
226
+ #
227
+ # Relative paths work too. Use a leading `./` to denote a relative
228
+ # path:
229
+ #
230
+ # //= require "./bar"
231
+ #
232
+ def process_require_directive(path)
233
+ if @compat
234
+ if path =~ /<([^>]+)>/
235
+ path = $1
236
+ else
237
+ path = "./#{path}" unless relative?(path)
238
+ end
239
+ end
240
+
241
+ context.require_asset(path)
242
+ end
243
+
244
+ # `require_self` causes the body of the current file to be
245
+ # inserted before any subsequent `require` or `include`
246
+ # directives. Useful in CSS files, where it's common for the
247
+ # index file to contain global styles that need to be defined
248
+ # before other dependencies are loaded.
249
+ #
250
+ # /*= require "reset"
251
+ # *= require_self
252
+ # *= require_tree .
253
+ # */
254
+ #
255
+ def process_require_self_directive
256
+ if @has_written_body
257
+ raise ArgumentError, "require_self can only be called once per source file"
258
+ end
259
+
260
+ context.require_asset(pathname)
261
+ process_source
262
+ included_pathnames.clear
263
+ @has_written_body = true
264
+ end
265
+
266
+ # The `include` directive works similar to `require` but
267
+ # inserts the contents of the dependency even if it already
268
+ # has been required.
269
+ #
270
+ # //= include "header"
271
+ #
272
+ def process_include_directive(path)
273
+ included_pathnames << context.resolve(path)
274
+ end
275
+
276
+ # `require_directory` requires all the files inside a single
277
+ # directory. It's similar to `path/*` since it does not follow
278
+ # nested directories.
279
+ #
280
+ # //= require_directory "./javascripts"
281
+ #
282
+ def process_require_directory_directive(path = ".")
283
+ if relative?(path)
284
+ root = pathname.dirname.join(path).expand_path
285
+ context.depend_on(root)
286
+
287
+ Dir["#{root}/*"].sort.each do |filename|
288
+ if filename == self.file
289
+ next
290
+ elsif context.asset_requirable?(filename)
291
+ context.require_asset(filename)
292
+ end
293
+ end
294
+ else
295
+ # The path must be relative and start with a `./`.
296
+ raise ArgumentError, "require_directory argument must be a relative path"
297
+ end
298
+ end
299
+
300
+ # `require_tree` requires all the nested files in a directory.
301
+ # Its glob equivalent is `path/**/*`.
302
+ #
303
+ # //= require_tree "./public"
304
+ #
305
+ def process_require_tree_directive(path = ".")
306
+ if relative?(path)
307
+ root = pathname.dirname.join(path).expand_path
308
+ context.depend_on(root)
309
+
310
+ Dir["#{root}/**/*"].sort.each do |filename|
311
+ if filename == self.file
312
+ next
313
+ elsif File.directory?(filename)
314
+ context.depend_on(filename)
315
+ elsif context.asset_requirable?(filename)
316
+ context.require_asset(filename)
317
+ end
318
+ end
319
+ else
320
+ # The path must be relative and start with a `./`.
321
+ raise ArgumentError, "require_tree argument must be a relative path"
322
+ end
323
+ end
324
+
325
+ # Allows you to state a dependency on a file without
326
+ # including it.
327
+ #
328
+ # This is used for caching purposes. Any changes made to
329
+ # the dependency file with invalidate the cache of the
330
+ # source file.
331
+ #
332
+ # This is useful if you are using ERB and File.read to pull
333
+ # in contents from another file.
334
+ #
335
+ # //= depend_on "foo.png"
336
+ #
337
+ def process_depend_on_directive(path)
338
+ context.depend_on(context.resolve(path))
339
+ end
340
+
341
+ # Enable Sprockets 1.x compat mode.
342
+ #
343
+ # Makes it possible to use the same JavaScript source
344
+ # file in both Sprockets 1 and 2.
345
+ #
346
+ # //= compat
347
+ #
348
+ def process_compat_directive
349
+ @compat = true
350
+ end
351
+
352
+ # Checks if Sprockets 1.x compat mode enabled
353
+ def compat?
354
+ @compat
355
+ end
356
+
357
+ # Sprockets 1.x allowed for constant interpolation if a
358
+ # constants.yml was present. This is only available if
359
+ # compat mode is on.
360
+ def constants
361
+ if compat?
362
+ path = File.join(context.root_path, "constants.yml")
363
+ File.exist?(path) ? YAML.load_file(path) : {}
364
+ else
365
+ {}
366
+ end
367
+ end
368
+
369
+ # `provide` is stubbed out for Sprockets 1.x compat.
370
+ # Mutating the path when an asset is being built is
371
+ # not permitted.
372
+ def process_provide_directive(path)
373
+ end
374
+
375
+ private
376
+ def relative?(path)
377
+ path =~ /^\.($|\.?\/)/
378
+ end
379
+ end
380
+ end