artisans 2.0.6
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/.gitignore +9 -0
- data/.travis.yml +4 -0
- data/Gemfile +11 -0
- data/LICENSE.txt +21 -0
- data/README.md +83 -0
- data/Rakefile +3 -0
- data/artisans.gemspec +39 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/artisans/cached_environment.rb +79 -0
- data/lib/artisans/configuration.rb +16 -0
- data/lib/artisans/environment.rb +106 -0
- data/lib/artisans/errors.rb +5 -0
- data/lib/artisans/liquid/drops/settings_drop.rb +33 -0
- data/lib/artisans/sass/sass_liquid_importer.rb +67 -0
- data/lib/artisans/sass/script/lexer.rb +81 -0
- data/lib/artisans/sass/settings_processor.rb +31 -0
- data/lib/artisans/theme_compiler.rb +129 -0
- data/lib/artisans/version.rb +3 -0
- data/lib/artisans.rb +21 -0
- metadata +164 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: f866e3e8c8b4527ba12f809994af9d52f27cf784cac9bd2f3b9041b2819c11da
|
4
|
+
data.tar.gz: 0520a752f5b37d10f6dc58d453f0645bfa6f6c557a0ada819dc82c325dde9f88
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5f19344a38c395334092753e73af630474b4085a4f15c0a1e482eaa05c10d966981c7accdf7f2792688c4cc30574e310a301d61e8074b770a5d2ce468b577ad9
|
7
|
+
data.tar.gz: 5393ad558dde18e70442ddeaabde0b66f16a60857b01d710b9f97062ec7a2b15962acbcd02b056f340b25014981b590118a77ea563a5ca5e993f508ab3c54f6b
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2016 Myroslava Stavnycha
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
# Artisans
|
2
|
+
|
3
|
+
Artisans is a gem that helps to compile stylesheets assets in a format of scss.liquid, which might contain settings.
|
4
|
+
The main job of this gem is:
|
5
|
+
- To help existing sprocket FileImporter to locate scss files (@import directive), which has scss.liquid extension
|
6
|
+
- To make existing Sass compiler keep inline comments, which include pattern 'settings.xxx'
|
7
|
+
|
8
|
+
## Installation
|
9
|
+
|
10
|
+
Add this line to your application's Gemfile:
|
11
|
+
|
12
|
+
```ruby
|
13
|
+
gem 'artisans'
|
14
|
+
```
|
15
|
+
|
16
|
+
And then execute:
|
17
|
+
|
18
|
+
$ bundle
|
19
|
+
|
20
|
+
Or install it yourself as:
|
21
|
+
|
22
|
+
$ gem install artisans
|
23
|
+
|
24
|
+
## Usage
|
25
|
+
|
26
|
+
The gem can be used for compiling single assets (used directly in shoperb during theme editing) and compiling (and packing to .zip) the whole theme.
|
27
|
+
|
28
|
+
Single asset compiling generally looks as following:
|
29
|
+
|
30
|
+
```ruby
|
31
|
+
compiler = Artisans::ThemeCompiler.new(theme_sources_path, theme_assets_url, drops: liquid_drops_hash)
|
32
|
+
|
33
|
+
compiled_output = compiler.compiled_source('source/file/path')
|
34
|
+
|
35
|
+
# compiling whole theme. ThemeCompiler#compiled_file_with_derivatives yields every result file one by one
|
36
|
+
compiler.compiled_files do |file_path, content, type: :file|
|
37
|
+
# type might be :symlink. type = :file is default
|
38
|
+
end
|
39
|
+
|
40
|
+
# compilation of only 1 file with its derivatives is also possible:
|
41
|
+
# in this case compilation of sources/emails.liquid.haml will result in:
|
42
|
+
# - sources/emails/xxx.liquid.haml -> just returns source itself
|
43
|
+
# - emails/xxx.liquid -> compiled version
|
44
|
+
#
|
45
|
+
# Compilation of 'sources/translations/en.json' will result in:
|
46
|
+
# - sources/translations/en.json -> the source itself
|
47
|
+
# - translations/en.json -> a symlink to the source
|
48
|
+
#
|
49
|
+
# Compilation of sources/stylesheets/application.sass.liquid will result in:
|
50
|
+
# - sources/stylesheets/application.sass.liquid
|
51
|
+
# - stylesheets/application.css
|
52
|
+
|
53
|
+
compiler.compiled_file_with_derivatives('path/to/source') do |file_path, content, type: :file|
|
54
|
+
# type might be :symlink. type = :file is default
|
55
|
+
end
|
56
|
+
|
57
|
+
```
|
58
|
+
|
59
|
+
Artisans gem have could of configs as well:
|
60
|
+
|
61
|
+
```ruby
|
62
|
+
Artisans.configure do |c|
|
63
|
+
c.verbose = false # by default
|
64
|
+
c.logger = MyCustomLogger # which should implement #notify method
|
65
|
+
end
|
66
|
+
```
|
67
|
+
|
68
|
+
|
69
|
+
## Development
|
70
|
+
|
71
|
+
After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
72
|
+
|
73
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
74
|
+
|
75
|
+
## Contributing
|
76
|
+
|
77
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/artisans.
|
78
|
+
|
79
|
+
|
80
|
+
## License
|
81
|
+
|
82
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
83
|
+
|
data/Rakefile
ADDED
data/artisans.gemspec
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'artisans/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "artisans"
|
8
|
+
spec.version = Artisans::VERSION
|
9
|
+
spec.required_ruby_version = ">= 3.2.0"
|
10
|
+
spec.authors = ["Shoperb"]
|
11
|
+
spec.email = ["engineering@shoperb.com"]
|
12
|
+
|
13
|
+
spec.summary = 'Tool for compiling scss+liquid assets'
|
14
|
+
spec.description = 'Artisans compiles SCSS + Liquid assets for Shoperb themes, bundling templates and styles into deployable packages.'
|
15
|
+
spec.homepage = "https://www.shoperb.dev"
|
16
|
+
spec.license = "MIT"
|
17
|
+
|
18
|
+
spec.metadata = {
|
19
|
+
"rubygems_mfa_required" => "true",
|
20
|
+
"homepage_uri" => "https://www.shoperb.com",
|
21
|
+
"documentation_uri" => "https://www.shoperb.dev",
|
22
|
+
"source_code_uri" => "https://github.com/shoperb/artisans",
|
23
|
+
"bug_tracker_uri" => "https://github.com/shoperb/artisans/issues"
|
24
|
+
}
|
25
|
+
|
26
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
27
|
+
spec.bindir = "exe"
|
28
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
29
|
+
spec.require_paths = ["lib"]
|
30
|
+
|
31
|
+
spec.add_development_dependency "bundler", "~> 2.0"
|
32
|
+
spec.add_development_dependency "rake", "~> 13.0"
|
33
|
+
|
34
|
+
spec.add_dependency "liquid", "~> 4.0"
|
35
|
+
spec.add_dependency "sass", "~> 3"
|
36
|
+
spec.add_dependency "haml", "~> 6"
|
37
|
+
spec.add_dependency "rubyzip", "~> 2.3"
|
38
|
+
spec.add_dependency "sprockets", "3.7.2"
|
39
|
+
end
|
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "artisans"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
module Artisans
|
2
|
+
class CachedEnvironment < ::Sprockets::CachedEnvironment
|
3
|
+
attr_accessor :assets_url, :environment, :file_reader, :drops
|
4
|
+
|
5
|
+
def initialize(environment, options={})
|
6
|
+
@assets_url = environment.assets_url
|
7
|
+
@environment = environment
|
8
|
+
@file_reader = environment.file_reader
|
9
|
+
@drops = environment.drops
|
10
|
+
super(environment)
|
11
|
+
end
|
12
|
+
|
13
|
+
def stat(filename)
|
14
|
+
if file_reader.respond_to?(:stat)
|
15
|
+
file_reader.stat(filename)
|
16
|
+
else
|
17
|
+
super(filename)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def read_file(filename, content_type = nil)
|
22
|
+
@file_reader.read(filename)
|
23
|
+
end
|
24
|
+
|
25
|
+
def load_from_unloaded(unloaded, force_native = false)
|
26
|
+
if file_reader.respond_to?(:load_from_unloaded) && !force_native
|
27
|
+
file_reader.load_from_unloaded(unloaded, self)
|
28
|
+
else
|
29
|
+
super(unloaded)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# def resolve_absolute_path(paths, filename, accept)
|
34
|
+
# if file_reader.respond_to?(:resolve_absolute_path)
|
35
|
+
# file_reader.resolve_absolute_path(paths, filename, accept, self)
|
36
|
+
# else
|
37
|
+
# super(paths, filename, accept)
|
38
|
+
# end
|
39
|
+
# end
|
40
|
+
|
41
|
+
def load(uri, force_native = false)
|
42
|
+
if file_reader.respond_to?(:load) && !force_native
|
43
|
+
file_reader.load(uri, self)
|
44
|
+
else
|
45
|
+
super(uri)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def file?(uri)
|
50
|
+
if file_reader.respond_to?(:file?)
|
51
|
+
file_reader.file?(uri)
|
52
|
+
else
|
53
|
+
super(uri)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# Overrides resolver path_matches in sprockets 3.5
|
58
|
+
def path_matches(load_path, logical_name, logical_basename)
|
59
|
+
if file_reader && file_reader.respond_to?(:path_matches)
|
60
|
+
file_reader.path_matches(load_path, logical_name, logical_basename)
|
61
|
+
else
|
62
|
+
super(load_path, logical_name, logical_basename)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# Overrides resolver for sprockets 4.0 to reuse path_matches
|
67
|
+
def resolve_alts_under_path(load_path, logical_name, mime_exts)
|
68
|
+
logical_basename = File.basename(logical_name)
|
69
|
+
|
70
|
+
candidates,deps = path_matches(load_path, logical_name, logical_basename) if method(:path_matches).super_method
|
71
|
+
if candidates.present?
|
72
|
+
c = candidates.first
|
73
|
+
return [{ filename: c[0], type: c[1] }], deps
|
74
|
+
end
|
75
|
+
|
76
|
+
super
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Artisans
|
2
|
+
class Configuration
|
3
|
+
|
4
|
+
attr_accessor :verbose, :logger
|
5
|
+
|
6
|
+
def initialize(*)
|
7
|
+
@verbose = false
|
8
|
+
@logger = Logger.new(STDOUT).tap do |logger|
|
9
|
+
logger.define_singleton_method(:notify) do |msg, &block|
|
10
|
+
info msg
|
11
|
+
block.call
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
require_relative 'cached_environment'
|
2
|
+
require_relative 'liquid/drops/settings_drop'
|
3
|
+
|
4
|
+
require_relative 'sass/sass_liquid_importer'
|
5
|
+
require_relative 'sass/settings_processor'
|
6
|
+
require_relative 'sass/script/lexer'
|
7
|
+
|
8
|
+
module Artisans
|
9
|
+
#
|
10
|
+
# Supplying sprockets environment with correct assets paths, custom file importer for Sass::Engine
|
11
|
+
# and custom SassProcessor
|
12
|
+
#
|
13
|
+
class Environment < ::Sprockets::Environment
|
14
|
+
attr_reader :sources_path, :settings, :assets_url, :file_reader
|
15
|
+
|
16
|
+
def initialize **options, &block
|
17
|
+
@sources_path = options[:sources_path]
|
18
|
+
@settings = options[:settings]
|
19
|
+
|
20
|
+
@assets_url = Pathname.new(options[:assets_url])
|
21
|
+
@file_reader = options[:file_reader]
|
22
|
+
|
23
|
+
super(&block)
|
24
|
+
|
25
|
+
# either calculating digest from file content
|
26
|
+
# or already existing digests are taken
|
27
|
+
context_class.class_eval %Q{
|
28
|
+
def asset_path(path, options = {})
|
29
|
+
full_path = File.join('#{assets_path}', path)
|
30
|
+
reader = environment.file_reader
|
31
|
+
|
32
|
+
digest = reader.try(:find_digest, full_path)
|
33
|
+
digest ||= Digest::MD5.hexdigest(File.read(full_path)) if File.exist?(full_path)
|
34
|
+
|
35
|
+
separator = reader.try(:find_separator, full_path)
|
36
|
+
separator ||= ''
|
37
|
+
|
38
|
+
ext = File.extname(path)
|
39
|
+
filename = [path.gsub(/\#{Regexp.quote(ext)}\\z/, ''), separator, digest, ext].map(&:presence).compact.join
|
40
|
+
|
41
|
+
# if is fixing after_processor AssetUrlProcessor
|
42
|
+
filename.start_with?('#{assets_url}') ? filename : File.join('#{assets_url}', filename)
|
43
|
+
end
|
44
|
+
}
|
45
|
+
|
46
|
+
# cant use 'append_path' with objects importers, so:
|
47
|
+
self.config = hash_reassoc(config, :paths) do |paths|
|
48
|
+
paths.push(assets_path.to_s)
|
49
|
+
paths.push(stylesheets_path)
|
50
|
+
paths.push(javascripts_path)
|
51
|
+
|
52
|
+
paths.push(sass_liquid_importer)
|
53
|
+
end
|
54
|
+
|
55
|
+
register_mime_type 'application/font-woff', extensions: ['.woff2'] # not registered by default
|
56
|
+
register_mime_type 'application/pdf', extensions: ['.pdf'] # not registered by default
|
57
|
+
register_mime_type 'application/liquid', extensions: ['.liquid'] # not registered by default
|
58
|
+
register_mime_type 'text/scss', extensions: ['.scss'], charset: :unicode
|
59
|
+
register_transformer 'text/scss', 'text/css', Artisans::Sass::SettingsProcessor
|
60
|
+
register_transformer 'application/liquid', 'text/css', Artisans::Sass::SettingsProcessor
|
61
|
+
register_transformer 'application/liquid', 'text/scss', Artisans::Sass::SettingsProcessor
|
62
|
+
|
63
|
+
register_preprocessor 'application/liquid', Sprockets::DirectiveProcessor.new(comments: ["//", ["/*", "*/"]])
|
64
|
+
|
65
|
+
# register_transformer is not working on current rails if engine is set,
|
66
|
+
# so add manually to engine to rewrite Sprockets::ScssProcessor transformer
|
67
|
+
self.config = hash_reassoc(config, :engines) do |engines|
|
68
|
+
engines.merge(".scss" => Artisans::Sass::SettingsProcessor)
|
69
|
+
end if config[:engines] # only for sprockets up to 3.7
|
70
|
+
|
71
|
+
Artisans::Sass::SassLiquidImporter.environment = self
|
72
|
+
end
|
73
|
+
|
74
|
+
def drops
|
75
|
+
@drops ||= {
|
76
|
+
settings: Artisans::SettingsDrop.new(settings)
|
77
|
+
}.stringify_keys
|
78
|
+
end
|
79
|
+
|
80
|
+
def read_file(filename)
|
81
|
+
cached.read_file(filename)
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
def sass_liquid_importer
|
87
|
+
Artisans::Sass::SassLiquidImporter.new(stylesheets_path, self)
|
88
|
+
end
|
89
|
+
|
90
|
+
def assets_path
|
91
|
+
@assets_path ||= sources_path.join('assets')
|
92
|
+
end
|
93
|
+
|
94
|
+
def stylesheets_path
|
95
|
+
@stylesheets_path ||= assets_path.join('stylesheets').to_s
|
96
|
+
end
|
97
|
+
|
98
|
+
def javascripts_path
|
99
|
+
@javascripts_path ||= assets_path.join('javascripts').to_s
|
100
|
+
end
|
101
|
+
|
102
|
+
def cached
|
103
|
+
Artisans::CachedEnvironment.new(self)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Artisans
|
2
|
+
class SettingsDrop < Liquid::Drop
|
3
|
+
|
4
|
+
attr_reader :settings
|
5
|
+
|
6
|
+
def initialize(settings = {})
|
7
|
+
@settings = settings || {}
|
8
|
+
|
9
|
+
@settings.each do |key, value|
|
10
|
+
define_singleton_method key do
|
11
|
+
format_value(key, value)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.invokable?(method_name)
|
17
|
+
true
|
18
|
+
end
|
19
|
+
|
20
|
+
def method_missing *args
|
21
|
+
nil
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
#
|
27
|
+
# adding a setting key around value in a comment
|
28
|
+
#
|
29
|
+
def format_value(key, value)
|
30
|
+
"'/*settings.#{key}[*/#{value}/*]*/'"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
#
|
2
|
+
# Artisans::Sass::SassLiquidImporter can read .scss.liquid files, and at the same time
|
3
|
+
# using custom 'file_reader' provided along with the environment
|
4
|
+
#
|
5
|
+
module Artisans
|
6
|
+
module Sass
|
7
|
+
class SassLiquidImporter < ::Sass::Importers::Filesystem
|
8
|
+
def self.environment= env
|
9
|
+
@environment = env
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.environment
|
13
|
+
@environment
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
def initialize(root, environment = nil)
|
18
|
+
@environment = environment||self.class.environment
|
19
|
+
root = @environment.send(:stylesheets_path)
|
20
|
+
|
21
|
+
super(root)
|
22
|
+
|
23
|
+
@root = root.to_s
|
24
|
+
@real_root = ::Sass::Util.realpath(@root).to_s
|
25
|
+
end
|
26
|
+
|
27
|
+
alias_method :to_str, :to_s
|
28
|
+
protected
|
29
|
+
|
30
|
+
def extensions
|
31
|
+
super.merge(
|
32
|
+
'sass.liquid' => :sass,
|
33
|
+
'scss.liquid' => :scss
|
34
|
+
)
|
35
|
+
end
|
36
|
+
|
37
|
+
def _find(dir, name, options)
|
38
|
+
return unless @environment
|
39
|
+
|
40
|
+
if @environment.file_reader.respond_to?(:find_real_file)
|
41
|
+
full_filename, syntax = @environment.file_reader.find_real_file(dir, name, options, extensions)
|
42
|
+
else
|
43
|
+
full_filename, syntax = ::Sass::Util.destructure(find_real_file(dir, name, options))
|
44
|
+
end
|
45
|
+
return unless full_filename
|
46
|
+
|
47
|
+
full_filename = full_filename.tr("\\", "/") if ::Sass::Util.windows?
|
48
|
+
|
49
|
+
options[:syntax] = syntax
|
50
|
+
options[:filename] = full_filename
|
51
|
+
options[:importer] = self
|
52
|
+
|
53
|
+
#
|
54
|
+
# below goes the modification of original function
|
55
|
+
#
|
56
|
+
file_content = @environment.read_file(full_filename)
|
57
|
+
|
58
|
+
if File.extname(full_filename) == '.liquid'
|
59
|
+
liquid_compiled = Liquid::Template.parse(file_content).render(@environment.drops)
|
60
|
+
::Sass::Engine.new(liquid_compiled, options)
|
61
|
+
else
|
62
|
+
::Sass::Engine.new(file_content, options)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
#
|
2
|
+
# Extending Sass Lexer to understand colors,
|
3
|
+
# surrounded with settings comments
|
4
|
+
#
|
5
|
+
module Artisans
|
6
|
+
module SettingsLexer
|
7
|
+
|
8
|
+
def self.prepended(base)
|
9
|
+
base.class_eval do
|
10
|
+
class << self
|
11
|
+
def settings_re(re)
|
12
|
+
Regexp::new("(\\'?/\\*settings\\.[a-z_]+\\[\\*/)(" + re.source + ")(/\\*\\]\\*/\\'?)")
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
self::REGULAR_EXPRESSIONS[:settings_color] = settings_re(self::REGULAR_EXPRESSIONS[:color])
|
17
|
+
self::REGULAR_EXPRESSIONS[:settings_rgb_color] = settings_re(/rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(,\s*(\d+.?\d*))?\s*\)/)
|
18
|
+
self::REGULAR_EXPRESSIONS[:settings_number] = settings_re(self::REGULAR_EXPRESSIONS[:number])
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
#
|
23
|
+
# taken from ::Sass::Script::Lexer
|
24
|
+
#
|
25
|
+
def token
|
26
|
+
if after_interpolation? && (interp = @interpolation_stack.pop)
|
27
|
+
interp_type, interp_value = interp
|
28
|
+
if interp_type == :special_fun
|
29
|
+
return special_fun_body(interp_value)
|
30
|
+
else
|
31
|
+
raise "[BUG]: Unknown interp_type #{interp_type}" unless interp_type == :string
|
32
|
+
return string(interp_value, true)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
#
|
37
|
+
# Injecting 'settings_color'
|
38
|
+
# lexer check in here
|
39
|
+
#
|
40
|
+
variable || settings_color || settings_rgb_color || string(:double, false) || string(:single, false) || color || number || id ||
|
41
|
+
selector || string(:uri, false) || raw(self.class::UNICODERANGE) || special_fun || special_val ||
|
42
|
+
ident_op || ident || op
|
43
|
+
end
|
44
|
+
|
45
|
+
#
|
46
|
+
# Modified "color" lexer: parses value as valid color.
|
47
|
+
# Stores 'representation' along with comments
|
48
|
+
#
|
49
|
+
def settings_color
|
50
|
+
return unless @scanner.match?(self.class::REGULAR_EXPRESSIONS[:settings_color])
|
51
|
+
return unless @scanner[2].length == 4 || @scanner[2].length == 7
|
52
|
+
scanned_color = scan(self.class::REGULAR_EXPRESSIONS[:settings_color])
|
53
|
+
script_color = ::Sass::Script::Value::Color.from_hex(@scanner[2])
|
54
|
+
script_color.instance_variable_set("@representation", @scanner[0].gsub(/^'/, '').gsub(/'$/, ''))
|
55
|
+
[:color, script_color]
|
56
|
+
end
|
57
|
+
|
58
|
+
def settings_rgb_color
|
59
|
+
return unless @scanner.match?(self.class::REGULAR_EXPRESSIONS[:settings_rgb_color])
|
60
|
+
scanned_color = scan(self.class::REGULAR_EXPRESSIONS[:settings_rgb_color])
|
61
|
+
script_color = ::Sass::Script::Value::Color.new([@scanner[3].to_f, @scanner[4].to_f, @scanner[5].to_f, @scanner[7].to_f || 1])
|
62
|
+
script_color.instance_variable_set("@representation", @scanner[0].gsub(/^'/, '').gsub(/'$/, ''))
|
63
|
+
[:color, script_color]
|
64
|
+
end
|
65
|
+
|
66
|
+
# def settings_number
|
67
|
+
# return unless scan(self.class::REGULAR_EXPRESSIONS[:settings_number])
|
68
|
+
|
69
|
+
# value = (@scanner[2] ? @scanner[2].to_f : @scanner[3].to_i)
|
70
|
+
# value *= 10**@scanner[4].to_i if @scanner[4]
|
71
|
+
# script_number = ::Sass::Script::Value::Number.new(value, Array(@scanner[4]))
|
72
|
+
# script_number.original = @scanner[1]
|
73
|
+
|
74
|
+
# [:number, script_number]
|
75
|
+
# end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
::Sass::Script::Lexer.send(:prepend, Artisans::SettingsLexer)
|
@@ -0,0 +1,31 @@
|
|
1
|
+
#
|
2
|
+
# Artisans::Sass::SettingsProcessor inherits all functionality of built-it
|
3
|
+
# sprockets scss processors class.
|
4
|
+
#
|
5
|
+
# Artisans::Sass::SettingsProcessor is intended to integerate custom file importer (Artisans::Sass::FileImporter),
|
6
|
+
# which is able to @import liquid files. In addition, ScssProcessor processed correctly
|
7
|
+
# inline comments in scss file.
|
8
|
+
#
|
9
|
+
require "sprockets/sass_processor.rb"
|
10
|
+
module Artisans
|
11
|
+
module Sass
|
12
|
+
class SettingsProcessor < Sprockets::ScssProcessor
|
13
|
+
|
14
|
+
def initialize(options={}, &block)
|
15
|
+
options[:importer] ||= Artisans::Sass::SassLiquidImporter
|
16
|
+
super(options, &block)
|
17
|
+
end
|
18
|
+
#
|
19
|
+
# Inherits default scss compiling.
|
20
|
+
# + Removes quates, which were artificially places around settings comments so the processor leaves them.
|
21
|
+
# "/*setting.setting_name[*/setting_value/*]*/" => /*setting.setting_name[*/setting_value/*]*/
|
22
|
+
#
|
23
|
+
def call(input)
|
24
|
+
super.tap do |hash|
|
25
|
+
hash[:data] = hash[:data].gsub(/["'](\/\*settings\..+\[.+\]\*\/)["']/, '\1')
|
26
|
+
hash[:data] = hash[:data].gsub(/(" (.*?) ")/, '"\2"') # if value has quotes, then some redundant spaces are added. we remove them here
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
require 'artisans/environment'
|
2
|
+
|
3
|
+
module Artisans
|
4
|
+
class ThemeCompiler
|
5
|
+
|
6
|
+
module DefaultFileReader
|
7
|
+
extend self
|
8
|
+
|
9
|
+
def read(file)
|
10
|
+
File.read(file) if File.file?(file)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
attr_reader :sources_path, :assets_url, :compile, :settings
|
15
|
+
|
16
|
+
def initialize(sources_path, assets_url, **options)
|
17
|
+
@sources_path = sources_path.is_a?(String) ? Pathname.new(sources_path) : sources_path
|
18
|
+
@assets_url = assets_url
|
19
|
+
@compile = options[:compile] || default_compilation_assets
|
20
|
+
@settings = options[:settings] || {}
|
21
|
+
@file_reader = options[:file_reader] || Artisans::ThemeCompiler::DefaultFileReader
|
22
|
+
|
23
|
+
@compile.symbolize_keys!
|
24
|
+
@compiled_assets = {}
|
25
|
+
end
|
26
|
+
|
27
|
+
def compiled_files(&block)
|
28
|
+
Pathname.glob(sources_path.join("**/*")) do |file|
|
29
|
+
process_file file do |*args|
|
30
|
+
logger.notify("Packing #{file}"){ block.call(*args) }
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def compiled_file_with_derivatives(filename, &block)
|
36
|
+
process_file Pathname.new(filename), &block
|
37
|
+
end
|
38
|
+
|
39
|
+
def compiled_source(asset_path)
|
40
|
+
asset = compiled_asset(asset_path)
|
41
|
+
asset ? asset.source : (raise "Asset not found: #{asset_path} in #{sources_path.join('assets')}")
|
42
|
+
end
|
43
|
+
|
44
|
+
def rack_response env
|
45
|
+
sprockets_env.call(env)
|
46
|
+
end
|
47
|
+
|
48
|
+
protected
|
49
|
+
attr_accessor :compiled_assets
|
50
|
+
|
51
|
+
def default_compilation_assets
|
52
|
+
{
|
53
|
+
javascripts: ['application.js'],
|
54
|
+
stylesheets: ['application.css']
|
55
|
+
}
|
56
|
+
end
|
57
|
+
|
58
|
+
def compile_without_ext
|
59
|
+
compile.each_with_object({}) do |(type, files), collection|
|
60
|
+
collection[type] = files.map{ |f| File.basename(f, '.*') }
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def process_file(file)
|
65
|
+
relative_path = file.relative_path_from(sources_path)
|
66
|
+
source_path = Pathname.new('sources').join(relative_path)
|
67
|
+
|
68
|
+
file_content = @file_reader.read(file)
|
69
|
+
|
70
|
+
case relative_path.to_s
|
71
|
+
when /\A(assets\/(stylesheets\/((?:#{compile_without_ext[:stylesheets].join("|")})\.(css(|\.sass|\.scss)|sass|scss)(\.liquid)?)))\z/
|
72
|
+
yield source_path, file_content
|
73
|
+
|
74
|
+
compiled = compiled_source($~[2])
|
75
|
+
filename = "#{$~[1].gsub(".#{$~[4]}", "")}.css"
|
76
|
+
yield Pathname.new(filename), compiled
|
77
|
+
when /\A(assets\/(javascripts\/((?:#{compile_without_ext[:javascripts].join("|")})\.(js|coffee|js\.coffee))))\z/
|
78
|
+
yield source_path, file_content
|
79
|
+
|
80
|
+
compiled = compiled_source($~[2])
|
81
|
+
filename = "#{$~[1].gsub(".#{$~[4]}", "")}.js"
|
82
|
+
yield Pathname.new(filename), compiled
|
83
|
+
when /\A((layouts|templates|emails|sections)\/(.*\.liquid))\z/,
|
84
|
+
/\A(assets\/((images|icons)\/(.*\.(png|jpg|jpeg|gif|swf|ico|svg|pdf|json))))\z/,
|
85
|
+
/\A(assets\/(fonts\/(.*\.(eot|woff|ttf|woff2|svg))))\z/
|
86
|
+
yield relative_path, file_content
|
87
|
+
yield source_path, relative_path, :symlink
|
88
|
+
when /\A((layouts|templates|emails|sections)\/(.*\.liquid))\.haml\z/
|
89
|
+
content_compiled = Haml::Engine.new(file_content).render
|
90
|
+
yield Pathname.new($1.dup), content_compiled
|
91
|
+
|
92
|
+
if file_content == content_compiled
|
93
|
+
yield source_path, Pathname.new($1.dup), :symlink
|
94
|
+
else
|
95
|
+
yield source_path, file_content
|
96
|
+
end
|
97
|
+
when /\A((presets|config|translations)\/(.*\.json))\z/
|
98
|
+
yield relative_path, file_content
|
99
|
+
yield source_path, relative_path, :symlink
|
100
|
+
when /\A(assets\/(javascripts\/(.*\.(js|coffee|js\.coffee))))\z/, /\A(assets\/(stylesheets\/(.*\.(css(|\.sass|\.scss)|sass|scss)(\.liquid)?)))\z/
|
101
|
+
yield source_path, file_content
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def compiled_asset(asset_path)
|
106
|
+
compiled_assets[asset_path] ||= begin
|
107
|
+
sprockets_env[asset_path]
|
108
|
+
rescue StandardError => e
|
109
|
+
puts e.message
|
110
|
+
puts e.backtrace
|
111
|
+
|
112
|
+
raise Artisans::CompilationError.new(e)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def logger
|
117
|
+
Artisans.configuration.logger
|
118
|
+
end
|
119
|
+
|
120
|
+
def sprockets_env
|
121
|
+
@sprockets_env ||= Artisans::Environment.new(
|
122
|
+
sources_path: sources_path,
|
123
|
+
assets_url: assets_url,
|
124
|
+
settings: settings,
|
125
|
+
file_reader: @file_reader
|
126
|
+
)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
data/lib/artisans.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'sass'
|
2
|
+
require 'sprockets'
|
3
|
+
require 'liquid'
|
4
|
+
require 'haml'
|
5
|
+
require 'zip'
|
6
|
+
|
7
|
+
require 'artisans/version'
|
8
|
+
require 'artisans/errors'
|
9
|
+
require 'artisans/configuration'
|
10
|
+
|
11
|
+
require 'artisans/theme_compiler'
|
12
|
+
|
13
|
+
module Artisans
|
14
|
+
def self.configure
|
15
|
+
yield(configuration)
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.configuration
|
19
|
+
@configuration ||= Artisans::Configuration.new
|
20
|
+
end
|
21
|
+
end
|
metadata
ADDED
@@ -0,0 +1,164 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: artisans
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 2.0.6
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Shoperb
|
8
|
+
bindir: exe
|
9
|
+
cert_chain: []
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
11
|
+
dependencies:
|
12
|
+
- !ruby/object:Gem::Dependency
|
13
|
+
name: bundler
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
15
|
+
requirements:
|
16
|
+
- - "~>"
|
17
|
+
- !ruby/object:Gem::Version
|
18
|
+
version: '2.0'
|
19
|
+
type: :development
|
20
|
+
prerelease: false
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
22
|
+
requirements:
|
23
|
+
- - "~>"
|
24
|
+
- !ruby/object:Gem::Version
|
25
|
+
version: '2.0'
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: rake
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
29
|
+
requirements:
|
30
|
+
- - "~>"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '13.0'
|
33
|
+
type: :development
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - "~>"
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '13.0'
|
40
|
+
- !ruby/object:Gem::Dependency
|
41
|
+
name: liquid
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - "~>"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '4.0'
|
47
|
+
type: :runtime
|
48
|
+
prerelease: false
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - "~>"
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '4.0'
|
54
|
+
- !ruby/object:Gem::Dependency
|
55
|
+
name: sass
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - "~>"
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '3'
|
61
|
+
type: :runtime
|
62
|
+
prerelease: false
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - "~>"
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '3'
|
68
|
+
- !ruby/object:Gem::Dependency
|
69
|
+
name: haml
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - "~>"
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '6'
|
75
|
+
type: :runtime
|
76
|
+
prerelease: false
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - "~>"
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '6'
|
82
|
+
- !ruby/object:Gem::Dependency
|
83
|
+
name: rubyzip
|
84
|
+
requirement: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - "~>"
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '2.3'
|
89
|
+
type: :runtime
|
90
|
+
prerelease: false
|
91
|
+
version_requirements: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - "~>"
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '2.3'
|
96
|
+
- !ruby/object:Gem::Dependency
|
97
|
+
name: sprockets
|
98
|
+
requirement: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - '='
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: 3.7.2
|
103
|
+
type: :runtime
|
104
|
+
prerelease: false
|
105
|
+
version_requirements: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - '='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: 3.7.2
|
110
|
+
description: Artisans compiles SCSS + Liquid assets for Shoperb themes, bundling templates
|
111
|
+
and styles into deployable packages.
|
112
|
+
email:
|
113
|
+
- engineering@shoperb.com
|
114
|
+
executables: []
|
115
|
+
extensions: []
|
116
|
+
extra_rdoc_files: []
|
117
|
+
files:
|
118
|
+
- ".gitignore"
|
119
|
+
- ".travis.yml"
|
120
|
+
- Gemfile
|
121
|
+
- LICENSE.txt
|
122
|
+
- README.md
|
123
|
+
- Rakefile
|
124
|
+
- artisans.gemspec
|
125
|
+
- bin/console
|
126
|
+
- bin/setup
|
127
|
+
- lib/artisans.rb
|
128
|
+
- lib/artisans/cached_environment.rb
|
129
|
+
- lib/artisans/configuration.rb
|
130
|
+
- lib/artisans/environment.rb
|
131
|
+
- lib/artisans/errors.rb
|
132
|
+
- lib/artisans/liquid/drops/settings_drop.rb
|
133
|
+
- lib/artisans/sass/sass_liquid_importer.rb
|
134
|
+
- lib/artisans/sass/script/lexer.rb
|
135
|
+
- lib/artisans/sass/settings_processor.rb
|
136
|
+
- lib/artisans/theme_compiler.rb
|
137
|
+
- lib/artisans/version.rb
|
138
|
+
homepage: https://www.shoperb.dev
|
139
|
+
licenses:
|
140
|
+
- MIT
|
141
|
+
metadata:
|
142
|
+
rubygems_mfa_required: 'true'
|
143
|
+
homepage_uri: https://www.shoperb.com
|
144
|
+
documentation_uri: https://www.shoperb.dev
|
145
|
+
source_code_uri: https://github.com/shoperb/artisans
|
146
|
+
bug_tracker_uri: https://github.com/shoperb/artisans/issues
|
147
|
+
rdoc_options: []
|
148
|
+
require_paths:
|
149
|
+
- lib
|
150
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
151
|
+
requirements:
|
152
|
+
- - ">="
|
153
|
+
- !ruby/object:Gem::Version
|
154
|
+
version: 3.2.0
|
155
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - ">="
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '0'
|
160
|
+
requirements: []
|
161
|
+
rubygems_version: 3.6.7
|
162
|
+
specification_version: 4
|
163
|
+
summary: Tool for compiling scss+liquid assets
|
164
|
+
test_files: []
|