Class: Herb::Embedded::Bridge

Inherits:
Object
  • Object
show all
Defined in:
lib/herb/embedded/bridge.rb

Overview

Boots the JS engine: loads the host shim and vendored bundle, attaches the six Ruby callbacks HerbBackend needs, then loads the RubyBackend subclass and waits for its async init to resolve.

Defined Under Namespace

Classes: BootError, NotBootedError, VersionMismatchError

Constant Summary collapse

HOST_SHIM_PATH =
File.expand_path("../../../js/host_shim.js", __dir__)
RUBY_BACKEND_PATH =
File.expand_path("../../../js/ruby_backend.js", __dir__)
READY_CHECK_JS =
<<~JS
  function __herbEmbeddedReady() { return __herbEmbeddedBridge.ready; }
  function __herbEmbeddedError() { return __herbEmbeddedBridge.error; }
  function __herbEmbeddedVersion() { return __herbEmbeddedBridge.instance.version; }
JS

Instance Method Summary collapse

Constructor Details

#initialize(adapter:, bundle:) ⇒ Bridge

Returns a new instance of Bridge.



31
32
33
34
35
# File 'lib/herb/embedded/bridge.rb', line 31

def initialize(adapter:, bundle:)
  @adapter = adapter
  @bundle = bundle
  @booted = false
end

Instance Method Details

#autofix(source, file:, rules: nil, unsafe: false) ⇒ Object

A formatter-adjacent tool must never write source that fails to parse into a user's working tree. If the fixed output fails to parse while the original input parsed cleanly, the fix is discarded rather than trusted.



86
87
88
89
90
91
92
93
94
95
96
# File 'lib/herb/embedded/bridge.rb', line 86

def autofix(source, file:, rules: nil, unsafe: false)
  ensure_booted!
  raw = JSON.parse(@adapter.call("__herbAutofix", source, file, rules, unsafe))

  if source_parses?(source) && !source_parses?(raw["source"])
    return { source: source, applied: [], discarded: true }
  end

  applied = raw["fixed"].map { |offense| Diagnostic.from_js(offense, file: file) }
  { source: raw["source"], applied: applied, discarded: false }
end

#backend_versionObject



61
62
63
64
# File 'lib/herb/embedded/bridge.rb', line 61

def backend_version
  ensure_booted!
  @adapter.call("__herbEmbeddedVersion")
end

#bootObject



37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/herb/embedded/bridge.rb', line 37

def boot
  # Herb is pre-1.0: the major version never moves (0.8 -> 0.9 -> 0.10
  # are all "0"), so semver-range gating can't distinguish a validated
  # version from an unvalidated one. Gate on the recorded set instead.
  unless @bundle.herb_versions.include?(::Herb::VERSION)
    raise VersionMismatchError,
          "herb #{::Herb::VERSION} has not been validated against this bundle " \
          "(validated versions: #{@bundle.herb_versions.join(", ")})"
  end

  @adapter.load(File.read(HOST_SHIM_PATH))
  @adapter.load(@bundle.source)
  attach_callbacks
  @adapter.load(File.read(RUBY_BACKEND_PATH))
  @adapter.load(READY_CHECK_JS)

  unless @adapter.call("__herbEmbeddedReady")
    raise BootError, "Bridge failed to boot: #{@adapter.call("__herbEmbeddedError")}"
  end

  @booted = true
  self
end

#disposeObject



98
99
100
# File 'lib/herb/embedded/bridge.rb', line 98

def dispose
  @adapter.dispose
end

#lint(source, file:, rules: nil) ⇒ Object



71
72
73
74
75
# File 'lib/herb/embedded/bridge.rb', line 71

def lint(source, file:, rules: nil)
  ensure_booted!
  offenses = JSON.parse(@adapter.call("__herbLint", source, file, rules))
  offenses.map { |offense| Diagnostic.from_js(offense, file: file) }
end

#register_custom_rule(source, path) ⇒ Object



77
78
79
80
# File 'lib/herb/embedded/bridge.rb', line 77

def register_custom_rule(source, path)
  ensure_booted!
  @adapter.call("__herbRegisterCustomRule", source, path)
end

#rule_namesObject



66
67
68
69
# File 'lib/herb/embedded/bridge.rb', line 66

def rule_names
  ensure_booted!
  @adapter.call("__herbRuleNames")
end