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.
- data/.gitmodules +3 -0
- data/Gemfile +0 -13
- data/TODO.md +1 -0
- data/bpm.gemspec +8 -3
- data/lib/bpm/cli/base.rb +22 -20
- data/lib/bpm/generator.rb +3 -3
- data/lib/bpm/libgems_ext/dependency_installer.rb +4 -4
- data/lib/bpm/libgems_ext/installer.rb +1 -1
- data/lib/bpm/package.rb +13 -9
- data/lib/bpm/pipeline/generated_asset.rb +30 -3
- data/lib/bpm/pipeline/transport_processor.rb +20 -12
- data/lib/bpm/pipeline.rb +17 -9
- data/lib/bpm/project.rb +42 -10
- data/lib/bpm/server.rb +5 -0
- data/lib/bpm/version.rb +1 -1
- data/lib/bpm.rb +2 -0
- data/lib/vendored_sprockets.rb +7 -0
- data/spec/cli/add_spec.rb +40 -37
- data/spec/cli/build_spec.rb +5 -5
- data/spec/cli/new_spec.rb +49 -23
- data/spec/cli/push_spec.rb +5 -5
- data/spec/cli/unpack_spec.rb +14 -14
- data/spec/fixtures/{badrake-0.8.7.spd → badrake-0.8.7.bpkg} +0 -0
- data/spec/fixtures/{builder-3.0.0.spd → builder-3.0.0.bpkg} +0 -0
- data/spec/fixtures/{bundler-1.1.pre.spd → bundler-1.1.pre.bpkg} +0 -0
- data/spec/fixtures/{coffee-1.0.1.pre.spd → coffee-1.0.1.pre.bpkg} +0 -0
- data/spec/fixtures/{core-test-0.4.9.spd → core-test-0.4.9.bpkg} +0 -0
- data/spec/fixtures/{custom_generator-1.0.spd → custom_generator-1.0.bpkg} +0 -0
- data/spec/fixtures/custom_name/MyProject.json +4 -0
- data/spec/fixtures/hello_world/css/dummy.css +3 -0
- data/spec/fixtures/{highline-1.6.1.spd → highline-1.6.1.bpkg} +0 -0
- data/spec/fixtures/{ivory-0.0.1.spd → ivory-0.0.1.bpkg} +0 -0
- data/spec/fixtures/{jquery-1.4.3.spd → jquery-1.4.3.bpkg} +0 -0
- data/spec/fixtures/minitest/assets/bpm_packages.js +1 -0
- data/spec/fixtures/minitest/assets/bpm_styles.css +0 -0
- data/spec/fixtures/minitest/assets/minitest/app_package.js +1 -0
- data/spec/fixtures/minitest/lib/main.js +1 -0
- data/spec/fixtures/minitest/minitest.json +22 -0
- data/spec/fixtures/minitest/packages/uglyduck/lib/main.js +1 -0
- data/spec/fixtures/minitest/packages/uglyduck/minifier/main.js +4 -0
- data/spec/fixtures/minitest/packages/uglyduck/package.json +21 -0
- data/spec/fixtures/{optparse-1.0.1.spd → optparse-1.0.1.bpkg} +0 -0
- data/spec/fixtures/{rake-0.8.6.spd → rake-0.8.6.bpkg} +0 -0
- data/spec/fixtures/{rake-0.8.7.spd → rake-0.8.7.bpkg} +0 -0
- data/spec/fixtures/{spade-0.5.0.spd → spade-0.5.0.bpkg} +0 -0
- data/spec/fixtures/transporter/packages/transport/lib/main.js +1 -0
- data/spec/fixtures/transporter/packages/transport/package.json +1 -1
- data/spec/fixtures/transporter/packages/transport/transports/wrapper.js +5 -0
- data/spec/gauntlet_spec.rb +2 -2
- data/spec/package_spec.rb +3 -3
- data/spec/pipeline_spec.rb +175 -54
- data/spec/project_spec.rb +19 -3
- data/spec/support/fake_gem_server.rb +4 -4
- data/templates/init/project.json +3 -1
- data/templates/project/index.html +1 -1
- data/vendor/sprockets/.gitignore +7 -0
- data/vendor/sprockets/.travis.yml +6 -0
- data/vendor/sprockets/Gemfile +8 -0
- data/vendor/sprockets/LICENSE +20 -0
- data/vendor/sprockets/README.md +22 -0
- data/vendor/sprockets/Rakefile +8 -0
- data/vendor/sprockets/lib/sprockets/asset.rb +203 -0
- data/vendor/sprockets/lib/sprockets/asset_attributes.rb +161 -0
- data/vendor/sprockets/lib/sprockets/base.rb +147 -0
- data/vendor/sprockets/lib/sprockets/bundled_asset.rb +222 -0
- data/vendor/sprockets/lib/sprockets/cache/file_store.rb +41 -0
- data/vendor/sprockets/lib/sprockets/caching.rb +121 -0
- data/vendor/sprockets/lib/sprockets/charset_normalizer.rb +41 -0
- data/vendor/sprockets/lib/sprockets/context.rb +191 -0
- data/vendor/sprockets/lib/sprockets/digest.rb +73 -0
- data/vendor/sprockets/lib/sprockets/directive_processor.rb +380 -0
- data/vendor/sprockets/lib/sprockets/eco_template.rb +39 -0
- data/vendor/sprockets/lib/sprockets/ejs_template.rb +38 -0
- data/vendor/sprockets/lib/sprockets/engines.rb +92 -0
- data/vendor/sprockets/lib/sprockets/environment.rb +93 -0
- data/vendor/sprockets/lib/sprockets/errors.rb +17 -0
- data/vendor/sprockets/lib/sprockets/index.rb +80 -0
- data/vendor/sprockets/lib/sprockets/jst_processor.rb +26 -0
- data/vendor/sprockets/lib/sprockets/processing.rb +310 -0
- data/vendor/sprockets/lib/sprockets/processor.rb +32 -0
- data/vendor/sprockets/lib/sprockets/safety_colons.rb +28 -0
- data/vendor/sprockets/lib/sprockets/server.rb +270 -0
- data/vendor/sprockets/lib/sprockets/static_asset.rb +87 -0
- data/vendor/sprockets/lib/sprockets/static_compilation.rb +82 -0
- data/vendor/sprockets/lib/sprockets/trail.rb +122 -0
- data/vendor/sprockets/lib/sprockets/utils.rb +67 -0
- data/vendor/sprockets/lib/sprockets/version.rb +3 -0
- data/vendor/sprockets/lib/sprockets.rb +31 -0
- data/vendor/sprockets/sprockets.gemspec +30 -0
- data/vendor/sprockets/test/fixtures/asset/POW.png +0 -0
- data/vendor/sprockets/test/fixtures/asset/application.js +6 -0
- data/vendor/sprockets/test/fixtures/asset/bar-utf8.css +2 -0
- data/vendor/sprockets/test/fixtures/asset/charset.css +2 -0
- data/vendor/sprockets/test/fixtures/asset/circle/a.js +2 -0
- data/vendor/sprockets/test/fixtures/asset/circle/b.js +2 -0
- data/vendor/sprockets/test/fixtures/asset/circle/c.js +2 -0
- data/vendor/sprockets/test/fixtures/asset/compat.js +4 -0
- data/vendor/sprockets/test/fixtures/asset/constants.js +2 -0
- data/vendor/sprockets/test/fixtures/asset/constants.yml +1 -0
- data/vendor/sprockets/test/fixtures/asset/default_mime_type.js +1 -0
- data/vendor/sprockets/test/fixtures/asset/filename.js.erb +1 -0
- data/vendor/sprockets/test/fixtures/asset/foo-utf8.css +2 -0
- data/vendor/sprockets/test/fixtures/asset/included_header.js +4 -0
- data/vendor/sprockets/test/fixtures/asset/jquery.tmpl.min.js +1 -0
- data/vendor/sprockets/test/fixtures/asset/mismatch.js +1 -0
- data/vendor/sprockets/test/fixtures/asset/multiple.js +2 -0
- data/vendor/sprockets/test/fixtures/asset/multipleengine.js +1 -0
- data/vendor/sprockets/test/fixtures/asset/noengine.js +1 -0
- data/vendor/sprockets/test/fixtures/asset/noformat.coffee +1 -0
- data/vendor/sprockets/test/fixtures/asset/one.css +1 -0
- data/vendor/sprockets/test/fixtures/asset/oneengine.js +1 -0
- data/vendor/sprockets/test/fixtures/asset/project.css +1 -0
- data/vendor/sprockets/test/fixtures/asset/project.js.erb +4 -0
- data/vendor/sprockets/test/fixtures/asset/relative/include.js +4 -0
- data/vendor/sprockets/test/fixtures/asset/relative/require.js +1 -0
- data/vendor/sprockets/test/fixtures/asset/relative/require_outside_path.js +1 -0
- data/vendor/sprockets/test/fixtures/asset/require_self.css +9 -0
- data/vendor/sprockets/test/fixtures/asset/require_self_twice.css +8 -0
- data/vendor/sprockets/test/fixtures/asset/semicolons/bar.js +1 -0
- data/vendor/sprockets/test/fixtures/asset/semicolons/index.js +5 -0
- data/vendor/sprockets/test/fixtures/asset/sprite.css.erb +12 -0
- data/vendor/sprockets/test/fixtures/asset/tree/all/b/c/d.js +2 -0
- data/vendor/sprockets/test/fixtures/asset/tree/all/b/c/e.js +1 -0
- data/vendor/sprockets/test/fixtures/asset/tree/all/b/c.js +1 -0
- data/vendor/sprockets/test/fixtures/asset/tree/all/b.css +2 -0
- data/vendor/sprockets/test/fixtures/asset/tree/all/b.js.erb +1 -0
- data/vendor/sprockets/test/fixtures/asset/tree/all/d/c.js.coffee +1 -0
- data/vendor/sprockets/test/fixtures/asset/tree/all/d/e.js +1 -0
- data/vendor/sprockets/test/fixtures/asset/tree/all_with_require.js +7 -0
- data/vendor/sprockets/test/fixtures/asset/tree/all_with_require_directory.js +1 -0
- data/vendor/sprockets/test/fixtures/asset/tree/all_with_require_tree.js +2 -0
- data/vendor/sprockets/test/fixtures/asset/tree/directory/application.js +2 -0
- data/vendor/sprockets/test/fixtures/asset/tree/directory/bar.js +1 -0
- data/vendor/sprockets/test/fixtures/asset/tree/directory/foo.js +1 -0
- data/vendor/sprockets/test/fixtures/asset/tree/tree/application.js +2 -0
- data/vendor/sprockets/test/fixtures/asset/tree/tree/bar.js +1 -0
- data/vendor/sprockets/test/fixtures/asset/tree/tree/foo.js +1 -0
- data/vendor/sprockets/test/fixtures/asset/tree/with_logical_path/a/a.js +1 -0
- data/vendor/sprockets/test/fixtures/asset/tree/with_logical_path/require_tree_with_logical_path.js +1 -0
- data/vendor/sprockets/test/fixtures/asset/tree/without_argument/a.js +1 -0
- data/vendor/sprockets/test/fixtures/asset/tree/without_argument/b.js +1 -0
- data/vendor/sprockets/test/fixtures/asset/tree/without_argument/require_tree_without_argument.js +1 -0
- data/vendor/sprockets/test/fixtures/asset/two.css +1 -0
- data/vendor/sprockets/test/fixtures/asset/unicode.js +2 -0
- data/vendor/sprockets/test/fixtures/asset/unknownexts.min.js +2 -0
- data/vendor/sprockets/test/fixtures/asset/users.js.erb.str +4 -0
- data/vendor/sprockets/test/fixtures/context/POW.png +0 -0
- data/vendor/sprockets/test/fixtures/context/application.js.yml +3 -0
- data/vendor/sprockets/test/fixtures/context/bar.js +1 -0
- data/vendor/sprockets/test/fixtures/context/foo.js +2 -0
- data/vendor/sprockets/test/fixtures/context/helpers.css.erb +3 -0
- data/vendor/sprockets/test/fixtures/context/properties.js.erb +7 -0
- data/vendor/sprockets/test/fixtures/context/require_glob.js +1 -0
- data/vendor/sprockets/test/fixtures/context/resolve_content_type.js.erb +4 -0
- data/vendor/sprockets/test/fixtures/context/sprite.css.embed +3 -0
- data/vendor/sprockets/test/fixtures/default/application.js.coffee +4 -0
- data/vendor/sprockets/test/fixtures/default/coffee/foo.coffee +1 -0
- data/vendor/sprockets/test/fixtures/default/coffee/index.js +3 -0
- data/vendor/sprockets/test/fixtures/default/empty +0 -0
- data/vendor/sprockets/test/fixtures/default/gallery.css.erb +3 -0
- data/vendor/sprockets/test/fixtures/default/gallery.js +1 -0
- data/vendor/sprockets/test/fixtures/default/goodbye.jst.eco +1 -0
- data/vendor/sprockets/test/fixtures/default/hello.jst.ejs +1 -0
- data/vendor/sprockets/test/fixtures/default/hello.txt +1 -0
- data/vendor/sprockets/test/fixtures/default/interpolation.js +1 -0
- data/vendor/sprockets/test/fixtures/default/missing_require.js +1 -0
- data/vendor/sprockets/test/fixtures/default/mobile/a.js +1 -0
- data/vendor/sprockets/test/fixtures/default/mobile/b.js +1 -0
- data/vendor/sprockets/test/fixtures/default/mobile/c.css +1 -0
- data/vendor/sprockets/test/fixtures/default/mobile/d.css +1 -0
- data/vendor/sprockets/test/fixtures/default/mobile/index.css +3 -0
- data/vendor/sprockets/test/fixtures/default/mobile/index.js +1 -0
- data/vendor/sprockets/test/fixtures/default/noreturn.js +1 -0
- data/vendor/sprockets/test/fixtures/default/project.js.coffee.erb +2 -0
- data/vendor/sprockets/test/fixtures/directives/code_before_comment +3 -0
- data/vendor/sprockets/test/fixtures/directives/comment_without_directives +6 -0
- data/vendor/sprockets/test/fixtures/directives/directive_word_splitting +6 -0
- data/vendor/sprockets/test/fixtures/directives/directives_after_header +16 -0
- data/vendor/sprockets/test/fixtures/directives/double_slash +9 -0
- data/vendor/sprockets/test/fixtures/directives/hash +8 -0
- data/vendor/sprockets/test/fixtures/directives/no_header +2 -0
- data/vendor/sprockets/test/fixtures/directives/slash_star +10 -0
- data/vendor/sprockets/test/fixtures/directives/slash_star_single +4 -0
- data/vendor/sprockets/test/fixtures/directives/space_between_directive_word +2 -0
- data/vendor/sprockets/test/fixtures/directives/triple_hash +10 -0
- data/vendor/sprockets/test/fixtures/encoding/ascii.js +1 -0
- data/vendor/sprockets/test/fixtures/encoding/ascii_utf8.js +2 -0
- data/vendor/sprockets/test/fixtures/encoding/utf16.js +0 -0
- data/vendor/sprockets/test/fixtures/encoding/utf8.js +1 -0
- data/vendor/sprockets/test/fixtures/encoding/utf8_bom.js +1 -0
- data/vendor/sprockets/test/fixtures/engines/hello.alert +1 -0
- data/vendor/sprockets/test/fixtures/engines/moo.js.str +1 -0
- data/vendor/sprockets/test/fixtures/public/compiled-digest-0aa2105d29558f3eb790d411d7d8fb66.js +3 -0
- data/vendor/sprockets/test/fixtures/public/compiled-digest-1c41eb0cf934a0c76babe875f982f9d1.js +1 -0
- data/vendor/sprockets/test/fixtures/server/app/javascripts/application.js +5 -0
- data/vendor/sprockets/test/fixtures/server/app/javascripts/bar.js +1 -0
- data/vendor/sprockets/test/fixtures/server/app/javascripts/foo.js +1 -0
- data/vendor/sprockets/test/fixtures/server/app/javascripts/hello.txt +2 -0
- data/vendor/sprockets/test/fixtures/server/app/javascripts/tree.js +1 -0
- data/vendor/sprockets/test/fixtures/server/vendor/javascripts/missing_require.js +1 -0
- data/vendor/sprockets/test/fixtures/server/vendor/stylesheets/missing_require.css +1 -0
- data/vendor/sprockets/test/sprockets_test.rb +56 -0
- data/vendor/sprockets/test/test_asset.rb +593 -0
- data/vendor/sprockets/test/test_asset_attributes.rb +86 -0
- data/vendor/sprockets/test/test_caching.rb +62 -0
- data/vendor/sprockets/test/test_context.rb +115 -0
- data/vendor/sprockets/test/test_directive_processor.rb +124 -0
- data/vendor/sprockets/test/test_encoding.rb +65 -0
- data/vendor/sprockets/test/test_engines.rb +73 -0
- data/vendor/sprockets/test/test_environment.rb +610 -0
- data/vendor/sprockets/test/test_server.rb +227 -0
- metadata +258 -54
- 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
|