browsable 0.2.0 → 0.2.1

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: a5cc4db4361dc825b6a61dc6cca4fcefd98e8583e0f2962b9e365fdc41344100
4
- data.tar.gz: a4fe3eff5c21e781e2fe707147cb0c614b2c7a4a93947c21b8dd36ce06eaf9b0
3
+ metadata.gz: e0e36477f3fd4bb8e494212d0bc76eb3b9bd7ec96fcb51af0e3e4e8df04e9428
4
+ data.tar.gz: 5f25664cd5d1e76644d77952844559de5ed95407d2d73e5a2ce2e9a8e18b9e22
5
5
  SHA512:
6
- metadata.gz: 237fb7ab7d9218781175c9a0f3cd3d4389da33256b91b58d16bd38ddc52a8019de9d2e85553991b47672300f0c66745a3502725b2fec2052f3388133271998f6
7
- data.tar.gz: 33e9031f23d9d7acfa1fcea3d92be4664f1335229257495504f688ed86361a2659c15a05d65527185e9da54020fbb30969152f7540b7cc0d4a33415a473e3d63
6
+ metadata.gz: 8d1cb051b2e1c9e16f4dc56af0d76bf51d883860676f0877057a746a3029ff4ba8173f19b3f9d597171aa3541294ac05a96bd0807dc65eeedcd72def9e29c42b
7
+ data.tar.gz: dc5143e15d4dc5c17e2b1ecaa715b6413272df5bbb3e792ffafd90f6ff69696c599992f3cdbb62515afc6adc5567cd9d9b067112bd7292ddd15a7a9dcadb4ec6
data/CHANGELOG.md CHANGED
@@ -5,6 +5,26 @@ All notable changes to the `browsable` gem are documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.2.1] - 2026-05-25
9
+
10
+ ### Added — Sprockets asset-pipeline support
11
+
12
+ - **Pipeline detection (`Browsable::AssetPipeline`).** Identifies whether the
13
+ project uses Propshaft, Sprockets, both, or neither. Surfaces the name in the
14
+ audit header (`pipeline: sprockets`) and as a top-level field in `--json`
15
+ output (`"pipeline": "sprockets"`). Live `defined?` checks win when available;
16
+ otherwise the `Gemfile.lock` is the fallback.
17
+ - **Sprockets-layout JS discovery.** Default JS globs now include
18
+ `app/assets/javascripts/**/*.{js,mjs}` alongside the Propshaft/importmap
19
+ `app/javascript/**`. Sprockets directives (`//= require`, `*= require_tree`)
20
+ live inside comments and are passed through untouched — no preprocessing.
21
+ - **SCSS routing.** `.scss` and `.sass` are discovered and routed to stylelint
22
+ with `--customSyntax postcss-scss` when any are present.
23
+ - **`postcss-scss` in `doctor`.** Listed as an optional dependency, only flagged
24
+ as missing when the project actually has SCSS files on disk.
25
+ - **`examples/sprockets_app`.** New fixture mirroring `examples/rails_app` with
26
+ the Sprockets layout.
27
+
8
28
  ## [0.2.0] - 2026-05-25
9
29
 
10
30
  ### Added — Runtime response auditing
data/README.md CHANGED
@@ -31,6 +31,7 @@ The name is a play on Rails 8's `allow_browser` controller API. Instead of *decl
31
31
  - [CLI reference](#cli-reference)
32
32
  - [Configuration](#configuration)
33
33
  - [How it works](#how-it-works)
34
+ - [Asset pipelines](#asset-pipelines)
34
35
  - [Runtime auditing (test-suite mode)](#runtime-auditing-test-suite-mode)
35
36
  - [Per-controller policies](#per-controller-and-per-action-policies)
36
37
  - [Suggested policy fixes](#suggested-allow_browser-fix)
@@ -115,6 +116,7 @@ Browsable shells out to a few external tools that live globally on your machine:
115
116
  | `node` | JavaScript runtime for `stylelint` & `eslint` | Yes |
116
117
  | `stylelint` | CSS compatibility analysis | Yes (CSS audits) |
117
118
  | `eslint` + `eslint-plugin-compat` | JavaScript compatibility analysis | Yes (JS audits) |
119
+ | `postcss-scss` | Lets `stylelint` parse SCSS sources | Optional (SCSS audits only) |
118
120
  | `browserslist` | Live resolution of `defaults` queries | Optional |
119
121
  | `herb` | ERB parsing | Bundled (gem dep) |
120
122
 
@@ -256,6 +258,37 @@ It's a suggestion, not an instruction. Tightening the policy is one fix; changin
256
258
 
257
259
  The suggestion is derived from HTML/ERB findings, which carry exact version data. It also appears in `--json` output as `suggested_policy` and as a GitHub Actions notice.
258
260
 
261
+ ## Asset pipelines
262
+
263
+ Browsable's audit pipeline (sources → analyzers → report) is **pipeline-agnostic**: the analyzers don't care how Rails assembles your assets. Only the static-mode source-discovery layer needs to know where to look.
264
+
265
+ | Pipeline | Static-mode support | What gets discovered |
266
+ | --- | --- | --- |
267
+ | **Propshaft** *(primary target)* | Full | `app/javascript/**`, `app/assets/stylesheets/**`, `app/assets/builds/**`, importmap pins |
268
+ | **Sprockets** | Full | `app/assets/javascripts/**`, `app/assets/stylesheets/**` (incl. `.scss`) |
269
+ | **Both (migration)** | Full — Sprockets discovery wins | Superset of both layouts |
270
+ | **Neither** | Best-effort | Whatever the default globs find on disk |
271
+
272
+ The detected pipeline appears in the audit header (e.g. `pipeline: sprockets`) and as a top-level field in `--json` output (`"pipeline": "sprockets"`). Detection prefers a live `defined?(Sprockets)` / `defined?(Propshaft)` check (set by the railtie inside a Rails process) and falls back to your `Gemfile.lock` for standalone CLI runs.
273
+
274
+ ### SCSS audits
275
+
276
+ SCSS files (`.scss`) are routed to stylelint with `--customSyntax postcss-scss`. Install the parser globally:
277
+
278
+ ```bash
279
+ npm install -g postcss-scss
280
+ ```
281
+
282
+ `browsable doctor` flags `postcss-scss` as missing **only when** the project actually has SCSS files. Without it, SCSS files are still analyzed — but as plain CSS, so SCSS-specific syntax (nested selectors, variables) may produce parse warnings.
283
+
284
+ ### What is not analyzed
285
+
286
+ - **CoffeeScript** (`*.coffee`) — no static-mode support.
287
+ - **ERB-templated JS/CSS** (`*.js.erb`, `*.css.erb`) — only the literal source is read; the ERB is not expanded.
288
+ - **Indented Sass** (`*.sass`) — discovered, but `postcss-scss` parses braced SCSS, not the indented dialect.
289
+
290
+ These are documented limitations of static mode. **Runtime mode** (below) sidesteps them entirely by reading the HTML, CSS, and JS that Rails actually renders during a test run — so any pipeline, preprocessor, or templating that ends up serving real content is covered.
291
+
259
292
  ## Runtime auditing (test-suite mode)
260
293
 
261
294
  Static mode answers the question *“does my codebase satisfy a single browser-support target?”*. Runtime mode answers a sharper one: *“for every endpoint in my app, does the HTML it actually renders satisfy that endpoint's policy?”* It does this without trying to build a static asset → endpoint graph — instead, it lets Rails itself say what each endpoint renders during a test run, and audits *that*.
@@ -10,13 +10,26 @@ module Browsable
10
10
  # project's target. browsable supplies the config; stylelint (and its
11
11
  # bundled caniuse data) does the actual compatibility reasoning.
12
12
  class CSS < Base
13
+ # Extensions that stylelint cannot parse with its default PostCSS
14
+ # syntax. We route them through postcss-scss when any are present.
15
+ SCSS_EXTENSIONS = %w[.scss .sass].freeze
16
+
17
+ def self.scss_like?(file)
18
+ SCSS_EXTENSIONS.include?(File.extname(file).downcase)
19
+ end
20
+
13
21
  def required_tools = ["stylelint"]
14
22
 
15
23
  def analyze(files)
16
24
  return [] if files.empty?
17
25
 
18
26
  argv = ["stylelint", "--config", write_stylelintrc,
19
- "--formatter", "json", *files]
27
+ "--formatter", "json"]
28
+ # SCSS is a strict superset of CSS for the constructs we audit; running
29
+ # plain .css through postcss-scss is safe, so one invocation covers
30
+ # mixed inputs as long as any SCSS-like file is present.
31
+ argv.push("--customSyntax", "postcss-scss") if files.any? { |f| self.class.scss_like?(f) }
32
+ argv.concat(files)
20
33
  parse(shell_out(argv, dry_run_key: "BROWSABLE_DRY_RUN_CSS"))
21
34
  end
22
35
 
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Browsable
4
+ # Identifies which Rails asset pipeline (Propshaft, Sprockets, both, or
5
+ # neither) is in use for a project. Reported in the audit header so the user
6
+ # sees at a glance which pipeline browsable inferred — and surfaced in the
7
+ # JSON output so editor integrations and CI can branch on it.
8
+ #
9
+ # Detection prefers a live `defined?` check (set when running inside the host
10
+ # Rails process, e.g. via the railtie or a rake task). Standalone CLI runs
11
+ # never load the host app, so the fallback inspects the project's
12
+ # Gemfile.lock — the canonical record of which asset-pipeline gem the app
13
+ # actually uses.
14
+ class AssetPipeline
15
+ PROPSHAFT = "propshaft"
16
+ SPROCKETS = "sprockets"
17
+ BOTH = "sprockets+propshaft"
18
+ NONE = "none"
19
+
20
+ # Build a pipeline descriptor for the project at `root`.
21
+ def self.detect(root:)
22
+ new(root: root)
23
+ end
24
+
25
+ attr_reader :root
26
+
27
+ def initialize(root:)
28
+ @root = File.expand_path(root)
29
+ end
30
+
31
+ # One of PROPSHAFT, SPROCKETS, BOTH, NONE.
32
+ def name
33
+ @name ||= identify
34
+ end
35
+
36
+ def propshaft? = name == PROPSHAFT || name == BOTH
37
+ def sprockets? = name == SPROCKETS || name == BOTH
38
+ def none? = name == NONE
39
+
40
+ # When both pipelines are loaded (typical during a Propshaft migration),
41
+ # prefer Sprockets-style discovery — its source tree is the broader
42
+ # superset, so its globs match everything Propshaft would have found too.
43
+ def prefer_sprockets_layout? = sprockets?
44
+
45
+ private
46
+
47
+ def identify
48
+ live_propshaft = defined?(::Propshaft) ? true : false
49
+ live_sprockets = defined?(::Sprockets) ? true : false
50
+
51
+ # Live `defined?` is authoritative — the host app actually loaded these.
52
+ return BOTH if live_propshaft && live_sprockets
53
+ return PROPSHAFT if live_propshaft
54
+ return SPROCKETS if live_sprockets
55
+
56
+ # Standalone CLI mode: fall back to what the Gemfile.lock declares.
57
+ lock_propshaft = gemfile_lock_mentions?("propshaft")
58
+ lock_sprockets = gemfile_lock_mentions?("sprockets-rails") ||
59
+ gemfile_lock_mentions?("sprockets")
60
+
61
+ return BOTH if lock_propshaft && lock_sprockets
62
+ return PROPSHAFT if lock_propshaft
63
+ return SPROCKETS if lock_sprockets
64
+
65
+ NONE
66
+ end
67
+
68
+ def gemfile_lock_mentions?(gem_name)
69
+ path = File.join(root, "Gemfile.lock")
70
+ return false unless File.file?(path)
71
+
72
+ # `bundle` indents direct gem entries with four spaces; transitive deps
73
+ # appear under DEPENDENCIES with two. Either form counts as "in use".
74
+ File.read(path).match?(/^\s+#{Regexp.escape(gem_name)}\b/)
75
+ end
76
+ end
77
+ end
data/lib/browsable/cli.rb CHANGED
@@ -61,7 +61,7 @@ module Browsable
61
61
  option :fix, type: :boolean, default: false,
62
62
  desc: "Attempt to install missing dependencies via brew/npm"
63
63
  def doctor
64
- doc = Doctor.new
64
+ doc = Doctor.new(root: Dir.pwd)
65
65
  puts doc.render(color: color?)
66
66
 
67
67
  if options[:fix] && !doc.ok?
@@ -172,12 +172,15 @@ module Browsable
172
172
  # --- pipeline ------------------------------------------------------------
173
173
 
174
174
  def run_audit(root:, config:, target:, file_list: nil)
175
- available = Doctor.new.available_kinds
175
+ doctor = Doctor.new(root: root)
176
+ available = doctor.available_kinds
176
177
  skips = []
177
178
  files_by_kind = file_list ? route_files(file_list) : discover_files(root: root, config: config)
178
179
 
179
180
  collect_importmap(root: root, config: config, files_by_kind: files_by_kind, skips: skips) if file_list.nil?
180
181
 
182
+ check_scss_tooling!(doctor: doctor, files_by_kind: files_by_kind, skips: skips)
183
+
181
184
  findings = []
182
185
  ANALYZERS.each do |kind, analyzer_class|
183
186
  files = files_by_kind[kind] || []
@@ -212,7 +215,22 @@ module Browsable
212
215
  policies = file_list ? [] : PolicyScanner.call(root)
213
216
 
214
217
  Report.new(findings: findings, skips: skips, notes: notes, policies: policies,
215
- target: target, root: root, config_file: config.config_file)
218
+ target: target, root: root, config_file: config.config_file,
219
+ pipeline: AssetPipeline.detect(root: root).name)
220
+ end
221
+
222
+ # Warn (and skip the SCSS subset) when SCSS files were discovered but
223
+ # postcss-scss — the syntax stylelint needs to parse them — is missing.
224
+ def check_scss_tooling!(doctor:, files_by_kind:, skips:)
225
+ css_files = files_by_kind[:css] || []
226
+ return unless css_files.any? { |file| Analyzers::CSS.scss_like?(file) }
227
+ return if doctor.postcss_scss_installed?
228
+
229
+ skips << Report::Skip.new(
230
+ kind: :scss,
231
+ reason: "postcss-scss not found — SCSS files will be analyzed as plain CSS. " \
232
+ "Run `browsable doctor` for setup instructions."
233
+ )
216
234
  end
217
235
 
218
236
  def collect_importmap(root:, config:, files_by_kind:, skips:)
@@ -253,10 +271,10 @@ module Browsable
253
271
  buckets = { css: [], erb: [], html: [], js: [] }
254
272
  files.each do |file|
255
273
  case File.extname(file).downcase
256
- when ".css", ".scss" then buckets[:css] << file
257
- when ".js", ".mjs" then buckets[:js] << file
258
- when ".erb" then buckets[:erb] << file
259
- when ".html", ".htm" then buckets[:html] << file
274
+ when ".css", ".scss", ".sass" then buckets[:css] << file
275
+ when ".js", ".mjs" then buckets[:js] << file
276
+ when ".erb" then buckets[:erb] << file
277
+ when ".html", ".htm" then buckets[:html] << file
260
278
  end
261
279
  # TODO(v0.2): route Ruby view components (app/components/**/*.rb).
262
280
  end
@@ -20,11 +20,16 @@ module Browsable
20
20
  "manual_query" => "defaults"
21
21
  },
22
22
  "sources" => {
23
- "stylesheets" => ["app/assets/stylesheets/**/*.{css,scss}"],
23
+ "stylesheets" => ["app/assets/stylesheets/**/*.{css,scss,sass}"],
24
24
  "builds" => ["app/assets/builds/**/*.css"],
25
25
  "views" => ["app/views/**/*.{html.erb,turbo_stream.erb}",
26
26
  "app/components/**/*.{rb,html.erb}"],
27
- "javascript" => ["app/javascript/**/*.{js,mjs}"],
27
+ # `app/javascript` is the Propshaft/importmap convention;
28
+ # `app/assets/javascripts` is the Sprockets convention. Globbing both
29
+ # is harmless on a Propshaft-only app (no files match) and lets the
30
+ # CLI work on Sprockets apps with zero configuration.
31
+ "javascript" => ["app/javascript/**/*.{js,mjs}",
32
+ "app/assets/javascripts/**/*.{js,mjs}"],
28
33
  "importmap" => true,
29
34
  "public" => ["public/**/*.{html,css,js}"],
30
35
  "custom" => []
@@ -42,12 +42,25 @@ module Browsable
42
42
  Tool.new(key: :eslint_plugin_compat, label: "eslint-plugin-compat", binary: nil,
43
43
  npm_package: "eslint-plugin-compat",
44
44
  purpose: "the eslint plugin that performs the JS compat checks",
45
- enables: %i[js], required: true)
45
+ enables: %i[js], required: true),
46
+ Tool.new(key: :postcss_scss, label: "postcss-scss", binary: nil,
47
+ npm_package: "postcss-scss",
48
+ purpose: "lets stylelint parse SCSS sources (Sprockets apps)",
49
+ enables: %i[scss], required: false)
46
50
  ].freeze
47
51
 
48
52
  # Analyzer kinds that need no external tooling at all.
49
53
  ALWAYS_AVAILABLE = %i[erb html].freeze
50
54
 
55
+ # @param root [String, nil] the project root. When provided, optional tools
56
+ # (e.g. postcss-scss) are only flagged as missing if the project actually
57
+ # has files that need them.
58
+ def initialize(root: nil)
59
+ @root = root && File.expand_path(root)
60
+ end
61
+
62
+ attr_reader :root
63
+
51
64
  def statuses
52
65
  @statuses ||= TOOLS.map do |tool|
53
66
  present = installed?(tool)
@@ -64,6 +77,33 @@ module Browsable
64
77
  statuses.reject(&:installed?).map(&:tool)
65
78
  end
66
79
 
80
+ def postcss_scss_installed?
81
+ tool = TOOLS.find { |t| t.key == :postcss_scss }
82
+ installed?(tool)
83
+ end
84
+
85
+ # Whether the project at `root` actually has files that need this tool.
86
+ # For unconditional tools (e.g. node, stylelint) this is always true; for
87
+ # optional tools (e.g. postcss-scss) it depends on what's on disk.
88
+ def needs_tool?(tool)
89
+ return true if tool.required
90
+ return true if tool.enables.empty? # tools that enable nothing are housekeeping
91
+ return true unless root # no project context: assume needed
92
+
93
+ tool.enables.all? { |kind| project_has_kind?(kind) }
94
+ end
95
+
96
+ # Optional tools that *would* be needed by this project but aren't installed.
97
+ # Used by the audit CLI to surface targeted skips (e.g. postcss-scss missing
98
+ # only when the project has SCSS files).
99
+ def needed_optional_missing
100
+ missing.select do |tool|
101
+ next false if tool.required
102
+
103
+ needs_tool?(tool)
104
+ end
105
+ end
106
+
67
107
  # Which analyzer kinds can actually run on this machine right now.
68
108
  def available_kinds
69
109
  # In dry-run mode the external tools are never invoked, so treat them all
@@ -84,8 +124,9 @@ module Browsable
84
124
  lines = [pastel.bold("browsable doctor — system dependencies"), ""]
85
125
 
86
126
  statuses.each do |status|
87
- mark = status.installed? ? pastel.green("✓") : pastel.red("✗")
88
- lines << " #{mark} #{pastel.bold(status.tool.label)} — #{status.tool.purpose}"
127
+ mark = render_mark(pastel, status)
128
+ suffix = render_suffix(pastel, status)
129
+ lines << " #{mark} #{pastel.bold(status.tool.label)} — #{status.tool.purpose}#{suffix}"
89
130
  lines << pastel.dim(" #{status.detail}") if status.detail
90
131
  end
91
132
 
@@ -130,8 +171,12 @@ module Browsable
130
171
 
131
172
  private
132
173
 
174
+ # Install commands cover every required tool plus any optional tool the
175
+ # current project actually needs (e.g. postcss-scss when SCSS is present).
133
176
  def install_commands
134
- missing.map { |tool| install_command(tool) }.uniq
177
+ missing.select { |tool| tool.required || needs_tool?(tool) }
178
+ .map { |tool| install_command(tool) }
179
+ .uniq
135
180
  end
136
181
 
137
182
  def install_command(tool)
@@ -146,11 +191,40 @@ module Browsable
146
191
  def detail_for(tool, present)
147
192
  if present
148
193
  tool.binary? ? (tool_version(tool) || "installed") : "installed"
149
- else
194
+ elsif tool.required || needs_tool?(tool)
150
195
  "not found — #{install_command(tool)}"
196
+ else
197
+ "not installed (not needed for this project)"
198
+ end
199
+ end
200
+
201
+ def project_has_kind?(kind)
202
+ case kind
203
+ when :scss
204
+ Dir.glob(File.join(root, "app/assets/stylesheets/**/*.{scss,sass}"),
205
+ File::FNM_EXTGLOB).any?
206
+ else
207
+ true
151
208
  end
152
209
  end
153
210
 
211
+ # Optional tools that are missing-but-needed get the same red ✗ as required
212
+ # tools. Optional tools the project doesn't need are shown as a dim "—".
213
+ def render_mark(pastel, status)
214
+ return pastel.green("✓") if status.installed?
215
+ return pastel.red("✗") if status.tool.required || needs_tool?(status.tool)
216
+
217
+ pastel.dim("—")
218
+ end
219
+
220
+ def render_suffix(pastel, status)
221
+ return "" if status.tool.required
222
+ return pastel.dim(" (optional)") if status.installed?
223
+ return pastel.yellow(" (optional, but needed for this project)") if needs_tool?(status.tool)
224
+
225
+ pastel.dim(" (optional)")
226
+ end
227
+
154
228
  def installed?(tool)
155
229
  @installed_cache ||= {}
156
230
  @installed_cache.fetch(tool.key) do
@@ -33,6 +33,7 @@ module Browsable
33
33
  lines << pastel.dim("target: #{target.query} (#{browsers})")
34
34
  end
35
35
  lines << pastel.dim("config: #{report.config_file || 'none (no config file)'}")
36
+ lines << pastel.dim("pipeline: #{report.pipeline}") if report.pipeline
36
37
  lines.join("\n") + "\n"
37
38
  end
38
39
 
@@ -69,6 +69,7 @@ module Browsable
69
69
  def policies = []
70
70
  def root = data.dig("target", "root") || Dir.pwd
71
71
  def config_file = nil
72
+ def pipeline = data["pipeline"]
72
73
 
73
74
  def target
74
75
  tdata = data["target"]
@@ -15,14 +15,16 @@ module Browsable
15
15
  # `bumps` maps each raised browser to { from:, to: }.
16
16
  Suggestion = Data.define(:line, :bumps)
17
17
 
18
- attr_reader :findings, :skips, :notes, :policies, :target, :root, :config_file
18
+ attr_reader :findings, :skips, :notes, :policies, :target, :root, :config_file, :pipeline
19
19
 
20
20
  # @param notes [Array<String>] caveats about the run itself (e.g. a target
21
21
  # that could not be inferred) — distinct from per-file findings.
22
22
  # @param policies [Array<PolicyScanner::Policy>] every allow_browser callsite
23
23
  # discovered across the app's controllers — the policy landscape.
24
+ # @param pipeline [String, nil] the detected asset pipeline
25
+ # ("propshaft", "sprockets", "sprockets+propshaft", or "none").
24
26
  def initialize(findings: [], skips: [], notes: [], policies: [],
25
- target: nil, root: nil, config_file: nil)
27
+ target: nil, root: nil, config_file: nil, pipeline: nil)
26
28
  @findings = findings
27
29
  @skips = skips
28
30
  @notes = notes
@@ -30,6 +32,7 @@ module Browsable
30
32
  @target = target
31
33
  @root = root
32
34
  @config_file = config_file
35
+ @pipeline = pipeline
33
36
  end
34
37
 
35
38
  def errors = findings.select(&:error?)
@@ -74,6 +77,7 @@ module Browsable
74
77
  def as_json
75
78
  {
76
79
  target: target&.as_json,
80
+ pipeline: pipeline,
77
81
  notes: notes,
78
82
  summary: {
79
83
  errors: errors.size,
@@ -69,7 +69,8 @@ module Browsable
69
69
  policies: PolicyScanner.call(root),
70
70
  target: batch_target,
71
71
  root: root,
72
- config_file: config.config_file
72
+ config_file: config.config_file,
73
+ pipeline: AssetPipeline.detect(root: root).name
73
74
  )
74
75
  end
75
76
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Browsable
4
- VERSION = "0.2.0"
4
+ VERSION = "0.2.1"
5
5
  end
@@ -25,10 +25,12 @@ target:
25
25
 
26
26
  # sources:
27
27
  # # Globs are relative to the project root. The values shown are the defaults.
28
- # stylesheets: ["app/assets/stylesheets/**/*.{css,scss}"]
28
+ # stylesheets: ["app/assets/stylesheets/**/*.{css,scss,sass}"]
29
29
  # builds: ["app/assets/builds/**/*.css"]
30
30
  # views: ["app/views/**/*.{html.erb,turbo_stream.erb}", "app/components/**/*.{rb,html.erb}"]
31
- # javascript: ["app/javascript/**/*.{js,mjs}"]
31
+ # # Both Propshaft/importmap (app/javascript) and Sprockets
32
+ # # (app/assets/javascripts) layouts are discovered by default.
33
+ # javascript: ["app/javascript/**/*.{js,mjs}", "app/assets/javascripts/**/*.{js,mjs}"]
32
34
  # importmap: true # resolve config/importmap.rb pins and audit pinned JS
33
35
  # public: ["public/**/*.{html,css,js}"]
34
36
  # custom: [] # extra globs to fold into the audit
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: browsable
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Roman Hood
@@ -174,6 +174,7 @@ files:
174
174
  - lib/browsable/analyzers/erb.rb
175
175
  - lib/browsable/analyzers/html.rb
176
176
  - lib/browsable/analyzers/javascript.rb
177
+ - lib/browsable/asset_pipeline.rb
177
178
  - lib/browsable/asset_resolver.rb
178
179
  - lib/browsable/audit_log.rb
179
180
  - lib/browsable/cli.rb