Class: Herb::Embedded::CustomRuleLoader

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

Overview

Loads project-local rules from .herb/rules/**/*.mjs. Upstream's own loader uses pathToFileURL, tinyglobby, and dynamic import() — none of which exist in an embedded engine. Ruby replaces the loader entirely: it globs and reads the files, rewrites their one supported import (@herb-tools/linter) into a destructure from the already-loaded bundle, and hands the result to the engine for registration.

Defined Under Namespace

Classes: InvalidRuleError, UnsupportedImportError

Constant Summary collapse

RULES_GLOB =
File.join(".herb", "rules", "**", "*.mjs")
ALLOWED_SPECIFIER =
"@herb-tools/linter"
IMPORT_LINE =
/^import\b.*$/
NAMED_IMPORT_LINE =
/^import\s*\{([^}]*)\}\s*from\s*["']([^"']+)["'];?\s*$/
EXPORT_DEFAULT_CLASS =
/export\s+default\s+class\s+(\w+)/

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(root:, bridge:) ⇒ CustomRuleLoader

Returns a new instance of CustomRuleLoader.



21
22
23
24
# File 'lib/herb/embedded/custom_rule_loader.rb', line 21

def initialize(root:, bridge:)
  @root = root
  @bridge = bridge
end

Class Method Details

.rewrite(source, path) ⇒ Object

Only a single supported specifier is rewritten: named imports from "@herb-tools/linter" become a destructure from the bundle-global HerbLinter. Anything else — a different specifier, or an import form other than named braces — fails loudly rather than producing a mysterious ReferenceError at rule-execution time.



44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/herb/embedded/custom_rule_loader.rb', line 44

def self.rewrite(source, path)
  named_imports = extract_named_imports(source, path)
  body = strip_import_lines(source)
  body = rewrite_default_export(body, path)
  destructure = named_imports.empty? ? "" : "const { #{named_imports.join(", ")} } = HerbLinter;\n"

  <<~JS
    (function () {
      #{destructure}#{body}
      return __herbCustomRule;
    })()
  JS
end

Instance Method Details

#load_allObject



26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/herb/embedded/custom_rule_loader.rb', line 26

def load_all
  Dir.glob(File.join(@root, RULES_GLOB)).map do |path|
    rewritten = self.class.rewrite(File.read(path), path)
    result = @bridge.register_custom_rule(rewritten, path)

    if result["overrode"]
      warn("Custom rule '#{result["ruleName"]}' at #{path} overrides a built-in rule of the same name")
    end

    result["ruleName"]
  end
end