bpm 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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