epuber-stylus 1.1.0
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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +10 -0
- data/LICENSE +22 -0
- data/README.md +154 -0
- data/lib/epuber-stylus.rb +174 -0
- data/lib/epuber-stylus/import_processor.rb +71 -0
- data/lib/epuber-stylus/railtie.rb +32 -0
- data/lib/epuber-stylus/runtime.rb +54 -0
- data/lib/epuber-stylus/runtime/compiler.js +40 -0
- data/lib/epuber-stylus/runtime/runner.js +20 -0
- data/lib/epuber-stylus/sprockets.rb +58 -0
- data/lib/epuber-stylus/tilt.rb +2 -0
- data/lib/epuber-stylus/tilt/rails.rb +51 -0
- data/lib/epuber-stylus/tilt/stylus.rb +52 -0
- data/lib/epuber-stylus/version.rb +3 -0
- data/lib/rails/generators/epuber-stylus/assets/assets_generator.rb +13 -0
- data/lib/rails/generators/epuber-stylus/assets/templates/stylesheet.css.styl +3 -0
- data/lib/rails/generators/epuber-stylus/scaffold/scaffold_generator.rb +11 -0
- data/spec/generators/assets_generator_spec.rb +11 -0
- data/spec/generators/controller_generator_spec.rb +12 -0
- data/spec/generators/scaffold_generator_spec.rb +17 -0
- data/spec/import_processor_spec.rb +67 -0
- data/spec/rails_spec.rb +46 -0
- data/spec/runtime_spec.rb +11 -0
- data/spec/spec_helper.rb +30 -0
- data/spec/sprockets_spec.rb +36 -0
- data/spec/stylus_spec.rb +118 -0
- data/spec/support/generators/test_case.rb +59 -0
- data/spec/support/generators/tmp/app/controllers/posts_controller.rb +58 -0
- data/spec/support/generators/tmp/app/helpers/posts_helper.rb +2 -0
- data/spec/support/generators/tmp/config/routes.rb +0 -0
- data/spec/support/helpers.rb +63 -0
- data/spec/support/matchers.rb +5 -0
- data/spec/tilt/rails_spec.rb +64 -0
- data/spec/tilt/stylus_spec.rb +24 -0
- metadata +137 -0
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'execjs'
|
2
|
+
|
3
|
+
module Stylus
|
4
|
+
# Internal: Module responsible for the ExecJS interaction. Besides handling
|
5
|
+
# the compilation execution, this module provide a runtime validation to ensure
|
6
|
+
# that the Node.JS binary is available to use.
|
7
|
+
module Runtime
|
8
|
+
# Internal: Calls a specific function on the Node.JS context.
|
9
|
+
#
|
10
|
+
# Example
|
11
|
+
# exec('version', 2) # => '2'
|
12
|
+
#
|
13
|
+
# Returns The function returned value.
|
14
|
+
def exec(*arguments)
|
15
|
+
check_availability!
|
16
|
+
context.call(*arguments)
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
# Internal: Queries the runtime for it's availability and raises a 'RuntimeError'
|
21
|
+
# if the runtime isn't available. Otherwise, this is a noop.
|
22
|
+
def check_availability!
|
23
|
+
unless runtime.available?
|
24
|
+
message = 'The Node.JS runtime is not available to Stylus.'
|
25
|
+
message << 'Ensure that the "node" (or "nodejs") executable is present in your $PATH.'
|
26
|
+
raise RuntimeError, message
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# Internal: Compile the Stylus compilation script into a execution context
|
31
|
+
# to execute functions into.
|
32
|
+
#
|
33
|
+
# Returns the compiled context.
|
34
|
+
def context
|
35
|
+
@context ||= runtime.compile(script)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Internal: The custom compilation script body.
|
39
|
+
def script
|
40
|
+
File.read(File.expand_path('../runtime/compiler.js',__FILE__))
|
41
|
+
end
|
42
|
+
|
43
|
+
# Internal: Create the ExecJS external runtime with a old runner script that
|
44
|
+
# maintains the state of 'require', so we can use it to load modules like on
|
45
|
+
# any Node.JS program.
|
46
|
+
def runtime
|
47
|
+
@runtime ||= ExecJS::ExternalRuntime.new(
|
48
|
+
name: 'Node.js (V8)',
|
49
|
+
command: ['nodejs', 'node'],
|
50
|
+
runner_path: File.expand_path('../runtime/runner.js', __FILE__)
|
51
|
+
)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
var stylus = require('stylus');
|
2
|
+
|
3
|
+
function compile(str, options, plugins, imports, definitions) {
|
4
|
+
var style = stylus(str, options);
|
5
|
+
var output = '';
|
6
|
+
|
7
|
+
for(var name in plugins) {
|
8
|
+
var fn = require(name);
|
9
|
+
style.use(fn(plugins[name]));
|
10
|
+
}
|
11
|
+
|
12
|
+
imports.forEach(function(path) {
|
13
|
+
style.import(path);
|
14
|
+
})
|
15
|
+
|
16
|
+
for(var definition in definitions) {
|
17
|
+
obj = definitions[definition];
|
18
|
+
value = obj.value
|
19
|
+
|
20
|
+
if(obj.literal) {
|
21
|
+
value = new stylus.nodes.Literal(value);
|
22
|
+
}
|
23
|
+
|
24
|
+
style.define(definition, value);
|
25
|
+
}
|
26
|
+
|
27
|
+
style.render(function(error, css) {
|
28
|
+
if(error) throw error;
|
29
|
+
output = css;
|
30
|
+
})
|
31
|
+
return output;
|
32
|
+
}
|
33
|
+
|
34
|
+
function convert(str) {
|
35
|
+
return stylus.convertCSS(str);
|
36
|
+
}
|
37
|
+
|
38
|
+
function version() {
|
39
|
+
return stylus.version;
|
40
|
+
}
|
@@ -0,0 +1,20 @@
|
|
1
|
+
(function(program, execJS) { execJS(program) })(function() { #{source}
|
2
|
+
}, function(program) {
|
3
|
+
var output, print = function(string) {
|
4
|
+
process.stdout.write('' + string);
|
5
|
+
};
|
6
|
+
try {
|
7
|
+
result = program();
|
8
|
+
if (typeof result == 'undefined' && result !== null) {
|
9
|
+
print('["ok"]');
|
10
|
+
} else {
|
11
|
+
try {
|
12
|
+
print(JSON.stringify(['ok', result]));
|
13
|
+
} catch (err) {
|
14
|
+
print('["err"]');
|
15
|
+
}
|
16
|
+
}
|
17
|
+
} catch (err) {
|
18
|
+
print(JSON.stringify(['err', '' + err]));
|
19
|
+
}
|
20
|
+
});
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'epuber-stylus'
|
2
|
+
require 'epuber-stylus/tilt/stylus'
|
3
|
+
require 'epuber-stylus/tilt/rails'
|
4
|
+
require 'epuber-stylus/import_processor'
|
5
|
+
# Public: The setup logic to configure both Stylus and Sprockets on any
|
6
|
+
# kind of application - Rails, Sinatra or Rack.
|
7
|
+
#
|
8
|
+
# Example
|
9
|
+
#
|
10
|
+
# # mounting Sprockets as a Rack application with Stylus
|
11
|
+
# assets = Sprockets::Environment.new
|
12
|
+
# assets.append_path 'stylesheets'
|
13
|
+
# Stylus.setup(assets)
|
14
|
+
# run assets.index
|
15
|
+
module Stylus
|
16
|
+
# Public: Configure a Sprockets environment with Stylus Tilt engine
|
17
|
+
# and the ImportProcessor. It also accept a configuration Hash to
|
18
|
+
# setup the load path and flags of the Stylus module.
|
19
|
+
#
|
20
|
+
# environment - A instance of Sprockets::Environment.
|
21
|
+
# options - The configuration Hash (default: {})
|
22
|
+
# :rails - a flag to inform that the current application is a Rails app.
|
23
|
+
# :paths - An Array of paths to use the '@import' directive, defaults
|
24
|
+
# to the `paths` attribute on the environment object.
|
25
|
+
# :debug - The Boolean value for the debug flag.
|
26
|
+
# :compress - The Boolean value for the debug compress.
|
27
|
+
#
|
28
|
+
# Example
|
29
|
+
#
|
30
|
+
# assets = Sprockets::Environment.new
|
31
|
+
# Stylus.setup(assets, compress: settings.production?)
|
32
|
+
#
|
33
|
+
# Returns nothing.
|
34
|
+
def self.setup(environment, options = {})
|
35
|
+
paths = options[:paths] || environment.paths
|
36
|
+
|
37
|
+
Stylus.paths.concat(paths)
|
38
|
+
|
39
|
+
Stylus.debug = options.fetch(:debug, Stylus.debug)
|
40
|
+
Stylus.compress = options.fetch(:compress, Stylus.compress)
|
41
|
+
template = detect_template_hander(options)
|
42
|
+
environment.register_engine('.styl', template)
|
43
|
+
environment.register_preprocessor('text/css', Stylus::ImportProcessor)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Internal: Gets the desired Tilt template handler to the current configuration.
|
47
|
+
# If a 'rails' option is present then the Rails specific template will be
|
48
|
+
# returned instead of the default Stylus Tilt template.
|
49
|
+
#
|
50
|
+
# Returns a Tilt::Template children class.
|
51
|
+
def self.detect_template_hander(options = {})
|
52
|
+
if options[:rails]
|
53
|
+
Stylus::Rails::StylusTemplate
|
54
|
+
else
|
55
|
+
Tilt::StylusTemplate
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'epuber-stylus/tilt/stylus'
|
2
|
+
# Public: A Tilt template to compile Stylus stylesheets with asset helpers.
|
3
|
+
module Stylus
|
4
|
+
module Rails
|
5
|
+
class StylusTemplate < ::Tilt::StylusTemplate
|
6
|
+
|
7
|
+
# Public: The default mime type for stylesheets.
|
8
|
+
self.default_mime_type = 'text/css'
|
9
|
+
|
10
|
+
# Internal: Appends stylus mixin for asset_url and asset_path support
|
11
|
+
def evaluate(scope, locals, &block)
|
12
|
+
@data = build_mixin_body(scope) + data
|
13
|
+
super
|
14
|
+
end
|
15
|
+
|
16
|
+
protected
|
17
|
+
|
18
|
+
# Internal: Builds body of a mixin
|
19
|
+
#
|
20
|
+
# Returns string representation of a mixin with asset helper functions
|
21
|
+
def build_mixin_body(scope)
|
22
|
+
@mixin_body ||= if assets_hash(scope).values.all? {|value| value != '' }
|
23
|
+
<<-STYL
|
24
|
+
asset-url(key)
|
25
|
+
return pair[1] if pair[0] == key for pair in #{assets_hash(scope)[:url]} ()
|
26
|
+
asset-path(key)
|
27
|
+
return pair[1] if pair[0] == key for pair in #{assets_hash(scope)[:path]} ()
|
28
|
+
STYL
|
29
|
+
else
|
30
|
+
''
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Internal: Construct Hash with absolute/relative paths in stylus syntax.
|
35
|
+
#
|
36
|
+
# Returns string representations of hash in Stylus syntax
|
37
|
+
def assets_hash(scope)
|
38
|
+
@assets_hash ||= scope.environment.each_logical_path.each_with_object({ :url => '', :path => '' }) do |logical_path, assets_hash|
|
39
|
+
unless File.extname(logical_path) =~ /^(\.(css|js)|)$/
|
40
|
+
path_to_asset = scope.path_to_asset(logical_path)
|
41
|
+
assets_hash[:url] << "('#{logical_path}' url(\"#{path_to_asset}\")) "
|
42
|
+
assets_hash[:path] << "('#{logical_path}' \"#{path_to_asset}\") "
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
Tilt.register ::Stylus::Rails::StylusTemplate, 'styl'
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'tilt'
|
2
|
+
# Public: A Tilt template to compile Stylus stylesheets.
|
3
|
+
#
|
4
|
+
# Examples
|
5
|
+
#
|
6
|
+
# template = Tilt::StylusTemplate.new { |t| File.read('app.styl') }
|
7
|
+
# template.render # => the compiled CSS from the app.styl file.
|
8
|
+
#
|
9
|
+
# Options should assigned on the template constructor.
|
10
|
+
# template = Tilt::StylusTemplate.new(compress: true) { |t| File.read('app.styl') }
|
11
|
+
# template.render # => the compiled CSS with compression enabled.
|
12
|
+
module Tilt
|
13
|
+
class StylusTemplate < Template
|
14
|
+
|
15
|
+
# Public: The default mime type for stylesheets.
|
16
|
+
self.default_mime_type = 'text/css'
|
17
|
+
|
18
|
+
# Internal: Checks if the Stylus module has been properly defined.
|
19
|
+
#
|
20
|
+
# Returns true if the 'Stylus' module is present.
|
21
|
+
def self.engine_initialized?
|
22
|
+
defined? ::Stylus
|
23
|
+
end
|
24
|
+
|
25
|
+
# Internal: Require the 'stylus' file to load the Stylus module.
|
26
|
+
#
|
27
|
+
# Returns nothing.
|
28
|
+
def initialize_engine
|
29
|
+
require_template_library 'stylus'
|
30
|
+
end
|
31
|
+
|
32
|
+
# Internal: Caches the filename as an option entry if it's present.
|
33
|
+
#
|
34
|
+
# Returns nothing.
|
35
|
+
def prepare
|
36
|
+
if self.file
|
37
|
+
options[:filename] ||= self.file
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Internal: Compile the template Stylus using this instance options.
|
42
|
+
# The current 'scope' and given 'locals' are ignored and the output
|
43
|
+
# is cached.
|
44
|
+
#
|
45
|
+
# Returns a String with the compiled stylesheet with CSS syntax.
|
46
|
+
def evaluate(scope, locals, &block)
|
47
|
+
@output ||= Stylus.compile(data, options)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
Tilt.register Tilt::StylusTemplate, 'styl'
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'rails/generators/named_base'
|
2
|
+
|
3
|
+
module Stylus
|
4
|
+
module Generators
|
5
|
+
class AssetsGenerator < ::Rails::Generators::NamedBase
|
6
|
+
source_root File.expand_path('../templates', __FILE__)
|
7
|
+
|
8
|
+
def copy_stylus
|
9
|
+
template 'stylesheet.css.styl', File.join('app/assets/stylesheets', class_path, "#{file_name}.css.styl")
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'rails/generators/css/scaffold/scaffold_generator'
|
2
|
+
|
3
|
+
module Stylus
|
4
|
+
module Generators
|
5
|
+
# Just inherit from the original Generator from Rails
|
6
|
+
# because `scaffold.css` it's just to help people to start up
|
7
|
+
# their Rails applications.
|
8
|
+
class ScaffoldGenerator < Css::Generators::ScaffoldGenerator
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'rails/generators/epuber-stylus/assets/assets_generator'
|
3
|
+
|
4
|
+
describe Stylus::Generators::AssetsGenerator do
|
5
|
+
include Generators::TestCase
|
6
|
+
arguments %w(posts)
|
7
|
+
|
8
|
+
it 'generates a .styl file' do
|
9
|
+
expect(file('app/assets/stylesheets/posts.css.styl')).to exist
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'rails/generators/rails/controller/controller_generator'
|
3
|
+
require 'rails/generators/epuber-stylus/assets/assets_generator'
|
4
|
+
|
5
|
+
describe Rails::Generators::ControllerGenerator do
|
6
|
+
include Generators::TestCase
|
7
|
+
arguments %w(posts --stylesheet-engine=stylus)
|
8
|
+
|
9
|
+
it 'generates a .styl file' do
|
10
|
+
expect(file('app/assets/stylesheets/posts.css.styl')).to exist
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'rails/generators/rails/scaffold/scaffold_generator'
|
3
|
+
require 'rails/generators/epuber-stylus/assets/assets_generator'
|
4
|
+
require 'rails/generators/epuber-stylus/scaffold/scaffold_generator'
|
5
|
+
|
6
|
+
describe Rails::Generators::ScaffoldGenerator do
|
7
|
+
include Generators::TestCase
|
8
|
+
arguments %w(posts --stylesheet-engine=stylus --orm=false)
|
9
|
+
|
10
|
+
it 'generates the default scaffold stylesheet' do
|
11
|
+
expect(file('app/assets/stylesheets/scaffold.css')).to exist
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'generates a named .styl file' do
|
15
|
+
expect(file('app/assets/stylesheets/posts.css.styl')).to exist
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Stylus::ImportProcessor do
|
4
|
+
let(:env) do
|
5
|
+
Sprockets::Environment.new do |assets|
|
6
|
+
assets.append_path fixture_root
|
7
|
+
Stylus.setup(assets)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'adds an imported stylesheet as a dependency' do
|
12
|
+
asset = env['import']
|
13
|
+
dependencies = dependencies_on(asset)
|
14
|
+
|
15
|
+
expect(dependencies).to include(fixture_path('mixins/vendor'))
|
16
|
+
end
|
17
|
+
|
18
|
+
context 'nested dependencies' do
|
19
|
+
it 'walks the dependency chain of imported files' do
|
20
|
+
asset = env['nested_import']
|
21
|
+
dependencies = dependencies_on(asset)
|
22
|
+
|
23
|
+
expect(dependencies).to include(fixture_path('mixins/nested'))
|
24
|
+
expect(dependencies).to include(fixture_path('mixins/vendor'))
|
25
|
+
end
|
26
|
+
|
27
|
+
it "adds files referenced in a directory's index file" do
|
28
|
+
asset = env['indexed_nested_import']
|
29
|
+
dependencies = dependencies_on(asset)
|
30
|
+
|
31
|
+
expect(dependencies).to include(fixture_path('indexed/index'))
|
32
|
+
expect(dependencies).to include(fixture_path('indexed/first_dep'))
|
33
|
+
expect(dependencies).to include(fixture_path('indexed/second_dep'))
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'walks dependency chains through indexes' do
|
37
|
+
asset = env['indexed_recursive_import']
|
38
|
+
dependencies = dependencies_on(asset)
|
39
|
+
|
40
|
+
expect(dependencies).to include(fixture_path('indexed_nested_import'))
|
41
|
+
expect(dependencies).to include(fixture_path('indexed/index'))
|
42
|
+
expect(dependencies).to include(fixture_path('indexed/first_dep'))
|
43
|
+
expect(dependencies).to include(fixture_path('indexed/second_dep'))
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'does not process non-stylus files' do
|
48
|
+
source = '@import "nib"'
|
49
|
+
template = Stylus::ImportProcessor.new('stylesheet.scss') { source }
|
50
|
+
context = double
|
51
|
+
|
52
|
+
expect(context).to_not receive(:depend_on)
|
53
|
+
template.render(context)
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'swallows errors from files outside the Sprockets paths' do
|
57
|
+
source = '@import "nib"'
|
58
|
+
template = Stylus::ImportProcessor.new { source }
|
59
|
+
sprockets = double
|
60
|
+
expect(sprockets).to receive(:resolve).twice.and_raise(::Sprockets::FileNotFound)
|
61
|
+
expect(template).to receive(:stylus_file?).and_return(true)
|
62
|
+
|
63
|
+
expect {
|
64
|
+
template.render(sprockets)
|
65
|
+
}.to_not raise_error
|
66
|
+
end
|
67
|
+
end
|
data/spec/rails_spec.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'Rails integration' do
|
4
|
+
it "copies all folders from the Sprockets load path" do
|
5
|
+
app = create_app
|
6
|
+
Stylus.paths.should include fixture_root
|
7
|
+
Stylus.paths.should == app.assets.paths
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'process .styl files with the asset pipeline' do
|
11
|
+
result = fixture(:simple).last
|
12
|
+
|
13
|
+
app = create_app
|
14
|
+
app.assets['simple'].to_s.should == result
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'enables @import definitions' do
|
18
|
+
result = fixture(:import).last
|
19
|
+
|
20
|
+
app = create_app
|
21
|
+
app.assets['import'].to_s.should == result
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'skips debug info by default' do
|
25
|
+
app = create_app
|
26
|
+
asset = app.assets['simple']
|
27
|
+
asset.to_s.should_not match(/line 1 : #{asset.pathname}/)
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'provides debug info if required' do
|
31
|
+
app = create_app(:debug => true)
|
32
|
+
asset = app.assets['simple']
|
33
|
+
asset.to_s.should match(/line 1 : #{asset.pathname}/)
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'compress the output if Rails is configured to compress them too' do
|
37
|
+
result = fixture(:compressed).last
|
38
|
+
|
39
|
+
app = create_app(:compress => true)
|
40
|
+
app.assets['compressed'].to_s.should == result.rstrip
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'loads the app normally even when the asset pipeline is disabled' do
|
44
|
+
pending "TODO: supress the sprockets-rails railtie to test this."
|
45
|
+
end
|
46
|
+
end
|