Module: Tina4::ScssCompiler

Defined in:
lib/tina4/scss_compiler.rb

Constant Summary collapse

SCSS_DIRS =
%w[src/scss scss src/styles styles].freeze
CSS_OUTPUT =
"src/public/css"
VARIABLE_FLAG =

Flags that may trail a variable declaration's value. !default means "assign only if this variable is not already set" -- the flag that makes a variable themeable. !global is the scope flag. Both are compiler directives: they are consumed at the declaration and must never reach the CSS, because padding: 1.5rem !default is invalid CSS and browsers drop the whole declaration. Sass flag names are case-SENSITIVE (!DEFAULT is an error in Dart Sass), so the match is deliberately case-sensitive.

/\s*!(default|global)\s*\z/

Class Method Summary collapse

Class Method Details

.add_import_path(path) ⇒ Object

Add a search path for @import resolution.



24
25
26
27
# File 'lib/tina4/scss_compiler.rb', line 24

def add_import_path(path)
  @import_paths ||= []
  @import_paths << path
end

.compile(source) ⇒ Object

Compile an SCSS string to CSS.



37
38
39
40
41
42
43
44
45
46
# File 'lib/tina4/scss_compiler.rb', line 37

def compile(source)
  @variables ||= {}
  @import_paths ||= []
  # Inject preset variables
  unless @variables.empty?
    var_block = @variables.map { |k, v| "$#{k}: #{v};" }.join("\n")
    source = "#{var_block}\n#{source}"
  end
  basic_compile(source, @import_paths.first || Dir.pwd)
end

.compile_all(root_dir = Dir.pwd) ⇒ Object

Compile all .scss files from known directories.



49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/tina4/scss_compiler.rb', line 49

def compile_all(root_dir = Dir.pwd)
  output_dir = File.join(root_dir, CSS_OUTPUT)
  FileUtils.mkdir_p(output_dir)

  SCSS_DIRS.each do |dir|
    scss_dir = File.join(root_dir, dir)
    next unless Dir.exist?(scss_dir)

    Dir.glob(File.join(scss_dir, "**/*.scss")).each do |scss_file|
      next if File.basename(scss_file).start_with?("_") # Skip partials
      compile_file(scss_file, output_dir, scss_dir)
    end
  end
end

.compile_file(scss_file, output_dir = nil, base_dir = nil) ⇒ Object

Compile a single SCSS file. If output_dir is provided, writes CSS there. Always returns the compiled CSS string.



66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/tina4/scss_compiler.rb', line 66

def compile_file(scss_file, output_dir = nil, base_dir = nil)
  base_dir ||= File.dirname(scss_file)
  scss_content = File.read(scss_file)
  css_content = compile_scss(scss_content, File.dirname(scss_file))

  if output_dir
    relative = scss_file.sub(base_dir, "").sub(/\.scss$/, ".css")
    css_file = File.join(output_dir, relative)
    FileUtils.mkdir_p(File.dirname(css_file))
    existing = File.exist?(css_file) ? File.read(css_file) : nil
    if existing != css_content
      File.write(css_file, css_content)
      Tina4::Log.debug("Compiled SCSS: #{scss_file} -> #{css_file}")
    end
  end

  css_content
rescue => e
  Tina4::Log.error("SCSS compilation failed: #{scss_file} - #{e.message}")
  ""
end

.compile_scss(content, base_dir) ⇒ Object

Compile an SCSS content string with a base directory for import resolution.



89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/tina4/scss_compiler.rb', line 89

def compile_scss(content, base_dir)
  # Try sassc gem first
  begin
    require "sassc"
    return SassC::Engine.new(content, style: :expanded, load_paths: [base_dir]).render
  rescue LoadError
    # Fall through to basic compiler
  end

  # Basic SCSS to CSS conversion (handles common patterns)
  basic_compile(content, base_dir)
end

.set_variable(name, value) ⇒ Object

Set a variable that will be available during compilation.



30
31
32
33
34
# File 'lib/tina4/scss_compiler.rb', line 30

def set_variable(name, value)
  @variables ||= {}
  name = name.sub(/^\$/, "")
  @variables[name] = value
end