Class: Browsable::Doctor
- Inherits:
-
Object
- Object
- Browsable::Doctor
- Defined in:
- lib/browsable/doctor.rb
Overview
Verifies that the external tools browsable shells out to are installed, and guides the user through installing whatever is missing.
Herb is a gem dependency and runs in-process, so it is never checked here —only node, npm, stylelint, eslint and eslint-plugin-compat.
Defined Under Namespace
Constant Summary collapse
- TOOLS =
[ Tool.new(key: :node, label: "node", binary: "node", npm_package: nil, purpose: "JavaScript runtime that stylelint and eslint run on", enables: %i[css js], required: true), Tool.new(key: :npm, label: "npm", binary: "npm", npm_package: nil, purpose: "installs the CSS/JS tooling (used by `doctor --fix`)", enables: [], required: false), Tool.new(key: :stylelint, label: "stylelint", binary: "stylelint", npm_package: "stylelint stylelint-no-unsupported-browser-features", purpose: "audits CSS for unsupported browser features", enables: %i[css], required: true), Tool.new(key: :eslint, label: "eslint", binary: "eslint", npm_package: "eslint eslint-plugin-compat", purpose: "audits JavaScript for unsupported browser features", enables: %i[js], required: true), Tool.new(key: :eslint_plugin_compat, label: "eslint-plugin-compat", binary: nil, npm_package: "eslint-plugin-compat", purpose: "the eslint plugin that performs the JS compat checks", enables: %i[js], required: true) ].freeze
- ALWAYS_AVAILABLE =
Analyzer kinds that need no external tooling at all.
%i[erb html].freeze
Instance Method Summary collapse
-
#available_kinds ⇒ Object
Which analyzer kinds can actually run on this machine right now.
-
#fix!(io: $stdout, input: $stdin, assume_yes: false) ⇒ Object
Attempt to install everything that is missing.
- #missing ⇒ Object
-
#ok? ⇒ Boolean
True when every required tool is present.
-
#render(color: $stdout.tty?) ⇒ Object
A formatted, colourised dependency report.
- #statuses ⇒ Object
Instance Method Details
#available_kinds ⇒ Object
Which analyzer kinds can actually run on this machine right now.
68 69 70 71 72 73 74 75 76 77 78 79 |
# File 'lib/browsable/doctor.rb', line 68 def available_kinds # In dry-run mode the external tools are never invoked, so treat them all # as available — this keeps specs and `BROWSABLE_DRY_RUN` audits working. return %i[css erb html js] if ENV.key?("BROWSABLE_DRY_RUN") kinds = ALWAYS_AVAILABLE.dup %i[css js].each do |kind| needed = TOOLS.select { |tool| tool.enables.include?(kind) } kinds << kind if needed.all? { |tool| installed?(tool) } end kinds end |
#fix!(io: $stdout, input: $stdin, assume_yes: false) ⇒ Object
Attempt to install everything that is missing. Runnable commands only (npm/brew); anything that needs a manual download is reported, not run.
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 |
# File 'lib/browsable/doctor.rb', line 106 def fix!(io: $stdout, input: $stdin, assume_yes: false) return true if ok? runnable, manual = install_commands.partition { |cmd| cmd.start_with?("npm ", "brew ") } manual.each { |cmd| io.puts "Manual step required: #{cmd}" } return ok? if runnable.empty? io.puts "browsable will run:" runnable.each { |cmd| io.puts " #{cmd}" } unless assume_yes io.print "Proceed? [y/N] " answer = input.gets&.strip&.downcase return false unless %w[y yes].include?(answer) end runnable.each do |cmd| io.puts "+ #{cmd}" system(cmd) end @statuses = nil @installed_cache = nil ok? end |
#missing ⇒ Object
63 64 65 |
# File 'lib/browsable/doctor.rb', line 63 def missing statuses.reject(&:installed?).map(&:tool) end |
#ok? ⇒ Boolean
True when every required tool is present.
59 60 61 |
# File 'lib/browsable/doctor.rb', line 59 def ok? statuses.select { |s| s.tool.required }.all?(&:installed?) end |
#render(color: $stdout.tty?) ⇒ Object
A formatted, colourised dependency report.
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 |
# File 'lib/browsable/doctor.rb', line 82 def render(color: $stdout.tty?) pastel = Pastel.new(enabled: color) lines = [pastel.bold("browsable doctor — system dependencies"), ""] statuses.each do |status| mark = status.installed? ? pastel.green("✓") : pastel.red("✗") lines << " #{mark} #{pastel.bold(status.tool.label)} — #{status.tool.purpose}" lines << pastel.dim(" #{status.detail}") if status.detail end lines << "" if ok? lines << pastel.green.bold("All required tools are installed. You're ready to audit.") else lines << pastel.red.bold("Missing required tools — install them with:") install_commands.each { |cmd| lines << " #{pastel.cyan(cmd)}" } lines << "" lines << pastel.dim("Or run `browsable doctor --fix` to install them automatically.") end lines.join("\n") end |