sasso-rails 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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ec674d60203b29114c080883f7e4cf438fa36dbc8b43daaebb02f7d197ffbf2e
4
- data.tar.gz: cd677fde1997dae468d8ad8acdd4de81a41847931ba6f72a9d7b102cfe523bbe
3
+ metadata.gz: 362a4798b8ebe03f26c32688f42048bc184b56a4e23a02db7d5cffbeaefcc4d8
4
+ data.tar.gz: 2bda2a2d286b992749ace28b314e8c86c9796bdd160d13c735a233967efc9a8d
5
5
  SHA512:
6
- metadata.gz: ca83ee104decbe95b065399c956584959c4f8599658c590c90c371859a920562f611eb7d41171d53e1581a3d66160ccb5bd02fd2545986ad87c9fba975075079
7
- data.tar.gz: 9df668f046ac42666b9ab9e65aba3b0b29fe3dfb0d018ccf8f7afc821f8f4378e76afbf0b5ca151ac6efc9b987a52160f0279fdcfeaa55215e3bb3738c39347c
6
+ metadata.gz: f23d4f4c8372103bf0fdaaeb86c85694eed3d861409aa99f915c580bfbde57842a811211a16f8806175db649474c9aa45421b4f4aea4266217d054dce3a32180
7
+ data.tar.gz: f097ec3da59d3e120408a9db2926e89d1d53d6f5b926699b7b126a7c6aa923f079f0f9d5c9ea5a342988570222bb3e34ea4043fa85660abb6254536deaa273a8
data/CHANGELOG.md CHANGED
@@ -8,6 +8,29 @@ the engine-gem version range it requires.
8
8
 
9
9
  ## [Unreleased]
10
10
 
11
+ ## [0.1.2] - 2026-06-14
12
+
13
+ Requires the `sasso` gem **>= 0.2.0** (for its source-map API).
14
+
15
+ ### Added
16
+
17
+ - **Source maps.** `sasso:build` writes a `<output>.map` sidecar next to each
18
+ compiled CSS and appends the `sourceMappingURL` footer. Controlled by
19
+ `config.sasso.source_map` (default: on outside production, off in production);
20
+ the map's `sources` are rewritten relative to the builds directory and `file`
21
+ points at the built CSS.
22
+
23
+ Makes `bin/rails generate sasso:install` drop-in on a fresh Rails 8 app.
24
+
25
+ ### Fixed
26
+
27
+ - The installer now removes the default `app/assets/stylesheets/application.css`,
28
+ which otherwise collides with the compiled `app/assets/builds/application.css`
29
+ on the asset load path (both resolve to the logical path `application.css`).
30
+ - The installer repoints the Rails 8 default layout's `stylesheet_link_tag :app`
31
+ to `stylesheet_link_tag "application"` so the compiled CSS is actually linked
32
+ (no-op if the layout already links `"application"` or was customized).
33
+
11
34
  ## [0.1.0] - 2026-06-13
12
35
 
13
36
  Initial release. Requires the `sasso` gem **>= 0.1.1, < 1**.
data/README.md CHANGED
@@ -30,15 +30,18 @@ bin/rails generate sasso:install
30
30
  ```
31
31
 
32
32
  The installer scaffolds `app/assets/stylesheets/application.scss`, creates
33
- `app/assets/builds/` (with a `.keep`), links the builds directory in a Sprockets
34
- manifest if one exists, and adds a watch process to `Procfile.dev`.
35
-
36
- Reference the compiled CSS in your layout:
33
+ `app/assets/builds/` (with a `.keep`), removes the default
34
+ `app/assets/stylesheets/application.css` (it would collide with the compiled
35
+ output), links the builds directory in a Sprockets manifest if one exists, adds
36
+ a watch process to `Procfile.dev`, and points your layout at the compiled CSS:
37
37
 
38
38
  ```erb
39
39
  <%= stylesheet_link_tag "application" %>
40
40
  ```
41
41
 
42
+ (On Rails 8 the default layout links `:app`; the installer repoints it to
43
+ `"application"`. If you wire the link yourself, use `"application"`.)
44
+
42
45
  ## Usage
43
46
 
44
47
  ```sh
@@ -69,6 +72,10 @@ config.sasso.style = :compressed
69
72
  # searched first, so sibling partials need no configuration.
70
73
  config.sasso.load_paths = [Rails.root.join("vendor/styles").to_s]
71
74
 
75
+ # Source maps: write a <output>.map sidecar + sourceMappingURL footer.
76
+ # Default: on outside production, off in production. Set true/false to force.
77
+ config.sasso.source_map = nil
78
+
72
79
  # Defaults shown:
73
80
  config.sasso.source_dir = "app/assets/stylesheets"
74
81
  config.sasso.build_dir = "app/assets/builds"
@@ -9,10 +9,33 @@ module Sasso
9
9
  class InstallGenerator < ::Rails::Generators::Base
10
10
  source_root File.expand_path("templates", __dir__)
11
11
 
12
+ # A fresh Propshaft/Sprockets app ships app/assets/stylesheets/application.css.
13
+ # Our compiled output is app/assets/builds/application.css — both resolve to
14
+ # the logical path "application.css" on the asset load path, which collides.
15
+ # The build dir owns the compiled CSS now (the cssbundling convention), so
16
+ # drop the default stub.
17
+ def remove_default_application_css
18
+ default = "app/assets/stylesheets/application.css"
19
+ remove_file default if File.exist?(File.join(destination_root, default))
20
+ end
21
+
12
22
  def create_stylesheet
13
23
  template "application.scss", "app/assets/stylesheets/application.scss"
14
24
  end
15
25
 
26
+ # Rails 8 layouts default to `stylesheet_link_tag :app`, which looks for
27
+ # "app.css" and won't pick up our compiled "application.css". Point it at
28
+ # the entrypoint we build. No-op if the layout is missing or already links
29
+ # "application" (so a customized layout is left alone).
30
+ def link_stylesheet_in_layout
31
+ layout = "app/views/layouts/application.html.erb"
32
+ full = File.join(destination_root, layout)
33
+ return unless File.exist?(full)
34
+ return if File.read(full).include?('stylesheet_link_tag "application"')
35
+
36
+ gsub_file layout, /stylesheet_link_tag\s+:app\b/, 'stylesheet_link_tag "application"'
37
+ end
38
+
16
39
  def ensure_builds_directory
17
40
  create_file "app/assets/builds/.keep" unless File.exist?(builds_keep_path)
18
41
 
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "fileutils"
4
+ require "json"
5
+ require "pathname"
4
6
 
5
7
  module Sasso
6
8
  module Rails
@@ -21,17 +23,18 @@ module Sasso
21
23
 
22
24
  ALLOWED_STYLES = %i[expanded compressed].freeze
23
25
 
24
- attr_reader :root, :builds, :style, :load_paths, :source_dir, :build_dir
26
+ attr_reader :root, :builds, :style, :load_paths, :source_dir, :build_dir, :source_map
25
27
 
26
28
  def initialize(root:, builds:, style: :expanded, load_paths: [],
27
29
  source_dir: "app/assets/stylesheets",
28
- build_dir: "app/assets/builds")
30
+ build_dir: "app/assets/builds", source_map: false)
29
31
  @root = File.expand_path(root.to_s)
30
32
  @builds = normalize_builds(builds)
31
33
  @style = normalize_style(style)
32
34
  @load_paths = Array(load_paths).map(&:to_s)
33
35
  @source_dir = source_dir.to_s
34
36
  @build_dir = build_dir.to_s
37
+ @source_map = source_map ? true : false
35
38
  end
36
39
 
37
40
  # Compile every entrypoint; returns the list of written output paths.
@@ -47,13 +50,18 @@ module Sasso
47
50
  raise Error, "sasso-rails: input stylesheet not found: #{src}"
48
51
  end
49
52
 
50
- # `Sasso.compile` already searches the entry file's own directory first
51
- # (for sibling @use/@import); pass any extra include dirs after it.
52
- css = ::Sasso.compile(src, style: @style, load_paths: @load_paths)
53
-
54
53
  dest = File.join(@root, @build_dir, output)
55
54
  FileUtils.mkdir_p(File.dirname(dest))
56
- File.write(dest, css)
55
+
56
+ # `Sasso.compile` already searches the entry file's own directory first
57
+ # (for sibling @use/@import); pass any extra include dirs after it.
58
+ if @source_map
59
+ result = ::Sasso.compile(src, style: @style, load_paths: @load_paths, source_map: true)
60
+ write_source_map(dest, result.source_map)
61
+ File.write(dest, result.css + source_map_footer(File.basename(dest)))
62
+ else
63
+ File.write(dest, ::Sasso.compile(src, style: @style, load_paths: @load_paths))
64
+ end
57
65
  dest
58
66
  end
59
67
 
@@ -76,6 +84,33 @@ module Sasso
76
84
 
77
85
  private
78
86
 
87
+ # Write the sidecar `<dest>.map`, pointing `file` at the built CSS and
88
+ # rewriting each source URL to a path relative to the builds directory
89
+ # (the .map lives next to the CSS). `source_map` is the parsed v3 Hash.
90
+ def write_source_map(dest, source_map)
91
+ from_dir = File.dirname(dest)
92
+ source_map["file"] = File.basename(dest)
93
+ source_map["sources"] = Array(source_map["sources"]).map { |s| relative_source(s, from_dir) }
94
+ File.write("#{dest}.map", JSON.generate(source_map))
95
+ end
96
+
97
+ # The `sourceMappingURL` footer for the built CSS. Matches dart-sass: the
98
+ # expanded footer sits on its own line after the trailing newline; the
99
+ # compressed footer is appended directly (no leading newline).
100
+ def source_map_footer(css_basename)
101
+ comment = "/*# sourceMappingURL=#{css_basename}.map */"
102
+ @style == :compressed ? "#{comment}\n" : "\n#{comment}\n"
103
+ end
104
+
105
+ # A source URL made relative to the .map's directory (so a DevTools that
106
+ # loads the .map resolves the original next to it). Falls back to the URL
107
+ # unchanged if it is not a relativizable path.
108
+ def relative_source(url, from_dir)
109
+ Pathname.new(url).relative_path_from(Pathname.new(from_dir)).to_s
110
+ rescue ArgumentError
111
+ url
112
+ end
113
+
79
114
  # Compile, downgrading a Sass/config error to a warning so a watcher does
80
115
  # not die on it (used at startup and on every recompile).
81
116
  def safe_build
@@ -12,6 +12,7 @@ module Sasso
12
12
  # config.sasso.load_paths # extra @use/@import include dirs (Array)
13
13
  # config.sasso.source_dir # default "app/assets/stylesheets"
14
14
  # config.sasso.build_dir # default "app/assets/builds"
15
+ # config.sasso.source_map # true | false (default: on outside production)
15
16
  #
16
17
  # The `sasso:build` rake task is enhanced onto `assets:precompile`, so the
17
18
  # CSS is generated before Propshaft/Sprockets fingerprints it on deploy.
@@ -22,6 +23,7 @@ module Sasso
22
23
  config.sasso.load_paths = []
23
24
  config.sasso.source_dir = "app/assets/stylesheets"
24
25
  config.sasso.build_dir = "app/assets/builds"
26
+ config.sasso.source_map = nil # resolved in `Sasso::Rails.source_map_for`
25
27
 
26
28
  # NOTE: no `rake_tasks do load ... end` — Rails::Engine already auto-loads
27
29
  # lib/tasks/**/*.rake into the host app. Loading it again here would define
@@ -40,9 +42,20 @@ module Sasso
40
42
  load_paths: cfg.load_paths,
41
43
  source_dir: cfg.source_dir,
42
44
  build_dir: cfg.build_dir,
45
+ source_map: source_map_for(cfg.source_map),
43
46
  )
44
47
  end
45
48
 
49
+ # Generate source maps outside production by default (debugging aid); an
50
+ # explicit `config.sasso.source_map` (true/false) always wins. Off in
51
+ # production by default to avoid shipping `.map` sidecars + the asset-digest
52
+ # interaction unless opted in.
53
+ def source_map_for(configured)
54
+ return configured ? true : false unless configured.nil?
55
+
56
+ !::Rails.env.production?
57
+ end
58
+
46
59
  # Default to compressed CSS in production (smaller payload), expanded
47
60
  # elsewhere (readable). An explicit `config.sasso.style` always wins.
48
61
  #
@@ -5,6 +5,6 @@ module Sasso
5
5
  # Versioned INDEPENDENTLY of both the `sasso` gem and the `sasso` crate.
6
6
  # The gemspec pins the engine gem with a range (sasso >= 0.1.1, < 1), so a
7
7
  # compiler bump does not force a lockstep release of this integration gem.
8
- VERSION = "0.1.0"
8
+ VERSION = "0.1.2"
9
9
  end
10
10
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sasso-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - momiji-rs
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-06-13 00:00:00.000000000 Z
11
+ date: 2026-06-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: railties
@@ -30,7 +30,7 @@ dependencies:
30
30
  requirements:
31
31
  - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: 0.1.1
33
+ version: 0.2.0
34
34
  - - "<"
35
35
  - !ruby/object:Gem::Version
36
36
  version: '1'
@@ -40,7 +40,7 @@ dependencies:
40
40
  requirements:
41
41
  - - ">="
42
42
  - !ruby/object:Gem::Version
43
- version: 0.1.1
43
+ version: 0.2.0
44
44
  - - "<"
45
45
  - !ruby/object:Gem::Version
46
46
  version: '1'