Class: HanamiSasso::Compiler

Inherits:
Object
  • Object
show all
Defined in:
lib/hanami-sasso/compiler.rb

Overview

Compiles configured Sass/SCSS entrypoints to plain CSS files. Deliberately framework-free so it can be unit-tested with a plain temp ‘root:` — the rake tasks just wire the Hanami app root + configuration in.

HanamiSasso::Compiler.new(
  root:       Dir.pwd,
  builds:     { "app.scss" => "app.css" },
  style:      :expanded,
  load_paths: [],                  # extra dirs, in addition to the source dir
  source_dir: "app/assets/css",
  build_dir:  "public/assets/css",
).build

Constant Summary collapse

Error =
Class.new(StandardError)
ALLOWED_STYLES =
%i[expanded compressed].freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(root:, builds:, style: :expanded, load_paths: [], source_dir: "app/assets/css", build_dir: "public/assets/css", source_map: false) ⇒ Compiler

Returns a new instance of Compiler.



28
29
30
31
32
33
34
35
36
37
38
# File 'lib/hanami-sasso/compiler.rb', line 28

def initialize(root:, builds:, style: :expanded, load_paths: [],
               source_dir: "app/assets/css",
               build_dir: "public/assets/css", source_map: false)
  @root       = File.expand_path(root.to_s)
  @builds     = normalize_builds(builds)
  @style      = normalize_style(style)
  @load_paths = Array(load_paths).map(&:to_s)
  @source_dir = source_dir.to_s
  @build_dir  = build_dir.to_s
  @source_map = source_map ? true : false
end

Instance Attribute Details

#build_dirObject (readonly)

Returns the value of attribute build_dir.



26
27
28
# File 'lib/hanami-sasso/compiler.rb', line 26

def build_dir
  @build_dir
end

#buildsObject (readonly)

Returns the value of attribute builds.



26
27
28
# File 'lib/hanami-sasso/compiler.rb', line 26

def builds
  @builds
end

#load_pathsObject (readonly)

Returns the value of attribute load_paths.



26
27
28
# File 'lib/hanami-sasso/compiler.rb', line 26

def load_paths
  @load_paths
end

#rootObject (readonly)

Returns the value of attribute root.



26
27
28
# File 'lib/hanami-sasso/compiler.rb', line 26

def root
  @root
end

#source_dirObject (readonly)

Returns the value of attribute source_dir.



26
27
28
# File 'lib/hanami-sasso/compiler.rb', line 26

def source_dir
  @source_dir
end

#source_mapObject (readonly)

Returns the value of attribute source_map.



26
27
28
# File 'lib/hanami-sasso/compiler.rb', line 26

def source_map
  @source_map
end

#styleObject (readonly)

Returns the value of attribute style.



26
27
28
# File 'lib/hanami-sasso/compiler.rb', line 26

def style
  @style
end

Instance Method Details

#buildObject

Compile every entrypoint; returns the list of written output paths.



41
42
43
# File 'lib/hanami-sasso/compiler.rb', line 41

def build
  builds.map { |input, output| build_one(input, output) }
end

#build_one(input, output) ⇒ Object

Compile a single ‘input` (relative to source_dir) to `output` (relative to build_dir), returning the absolute path written.

Raises:



56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/hanami-sasso/compiler.rb', line 56

def build_one(input, output)
  src = File.join(@root, @source_dir, input)
  raise Error, "hanami-sasso: input stylesheet not found: #{src}" unless File.file?(src)

  dest = File.join(@root, @build_dir, output)
  FileUtils.mkdir_p(File.dirname(dest))

  # `Sasso.compile` already searches the entry file's own directory first
  # (for sibling @use/@import); pass any extra include dirs after it.
  if @source_map
    result = ::Sasso.compile(src, style: @style, load_paths: @load_paths, source_map: true)
    write_source_map(dest, result.source_map)
    File.write(dest, result.css + source_map_footer(File.basename(dest)))
  else
    File.write(dest, ::Sasso.compile(src, style: @style, load_paths: @load_paths))
  end
  dest
end

#clobberObject

Remove the generated CSS (and any sidecar maps) for every entrypoint.



46
47
48
49
50
51
52
# File 'lib/hanami-sasso/compiler.rb', line 46

def clobber
  builds.filter_map do |_input, output|
    dest = File.join(@root, @build_dir, output)
    [dest, "#{dest}.map"].select { |f| File.file?(f) }.each { |f| FileUtils.rm_f(f) }
    File.directory?(File.dirname(dest)) ? dest : nil
  end
end

#watch(interval: 1.0) ⇒ Object

Recompile whenever a watched source file changes. Dependency-free poll loop (no ‘listen` gem): a cheap mtime scan of the source + load_path trees. Blocks. A compile error (including on the FIRST pass) is reported and the loop keeps running — the watcher must survive a mid-edit broken file.



79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/hanami-sasso/compiler.rb', line 79

def watch(interval: 1.0)
  safe_build
  snapshot = source_mtimes
  loop do
    sleep interval
    current = source_mtimes
    next if current == snapshot

    snapshot = current
    safe_build
  end
end