herb 0.3.1-arm-linux-gnu → 0.4.2-arm-linux-gnu

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.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +2 -2
  3. data/README.md +80 -115
  4. data/ext/herb/error_helpers.c +1 -1
  5. data/ext/herb/error_helpers.h +1 -1
  6. data/ext/herb/nodes.c +2 -2
  7. data/ext/herb/nodes.h +1 -1
  8. data/lib/herb/3.0/herb.so +0 -0
  9. data/lib/herb/3.1/herb.so +0 -0
  10. data/lib/herb/3.2/herb.so +0 -0
  11. data/lib/herb/3.3/herb.so +0 -0
  12. data/lib/herb/3.4/herb.so +0 -0
  13. data/lib/herb/ast/node.rb +6 -1
  14. data/lib/herb/ast/nodes.rb +1 -1
  15. data/lib/herb/cli.rb +18 -2
  16. data/lib/herb/errors.rb +1 -1
  17. data/lib/herb/parse_result.rb +7 -2
  18. data/lib/herb/project.rb +79 -33
  19. data/lib/herb/version.rb +1 -1
  20. data/lib/herb/visitor.rb +1 -1
  21. data/sig/herb/ast/node.rbs +3 -0
  22. data/sig/herb/parse_result.rbs +3 -0
  23. data/sig/serialized_ast_errors.rbs +1 -1
  24. data/sig/serialized_ast_nodes.rbs +1 -1
  25. data/src/analyze.c +61 -16
  26. data/src/analyze_helpers.c +15 -6
  27. data/src/ast_nodes.c +1 -1
  28. data/src/ast_pretty_print.c +1 -1
  29. data/src/errors.c +1 -1
  30. data/src/extract.c +6 -2
  31. data/src/include/analyze_helpers.h +1 -1
  32. data/src/include/ast_nodes.h +1 -1
  33. data/src/include/ast_pretty_print.h +1 -1
  34. data/src/include/errors.h +1 -1
  35. data/src/include/parser_helpers.h +7 -2
  36. data/src/include/pretty_print.h +48 -8
  37. data/src/include/prism_helpers.h +4 -1
  38. data/src/include/token_struct.h +2 -1
  39. data/src/include/version.h +1 -1
  40. data/src/lexer.c +12 -2
  41. data/src/parser.c +13 -4
  42. data/src/parser_helpers.c +10 -3
  43. data/src/pretty_print.c +50 -10
  44. data/src/prism_helpers.c +4 -1
  45. data/src/token.c +1 -0
  46. data/src/visitor.c +1 -1
  47. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 57f60552dc37faec94f5ecd3db8ddee812b00111432f40140647eb9bf9591300
4
- data.tar.gz: 07c1cde2a905f7125fa0c690b113a3f650f0356b20fc80fea0ea6d66ef563b12
3
+ metadata.gz: f27aaaad81ad225f5316278ca8a3c2412d624597d2a28e7eb5cafce3db32316e
4
+ data.tar.gz: d6c58ecbfed32f6c6afda64e1956d24f8b354bff4647ec6b01c93846742e51c5
5
5
  SHA512:
6
- metadata.gz: 6c72b409bfc7f607253efb56713a4f592effedcbbf71f2cb6159c90831153b21413834103e08d923621c942575e4cac91c48e1b0d86cf3f0a3c18a5d26b9a8f1
7
- data.tar.gz: 4c2a911b366ef7de04f0303b4a292cdc3b52e13b6c0a54a6555da724c51cf04984a665dfc8864b2be5ff7a4c8e40ecb5f8d829f4243c702baaaa49411884a931
6
+ metadata.gz: 871f6d51b0eb6be8d052b4ede53807e598a4e65a2b1d94139b2e834f731d11b136624dbe1e9351cc357e100afb1b98f4fe447b114b45328a51d6858747b6d2d2
7
+ data.tar.gz: 8ad246404e5f6e1e3c6926bfba00a7ea99860d82e8824b0290c29123edc71a11ab0a52801f196819b4e714754ec0a985d28f6f1151ae47987a7d19a0380b1b06
data/LICENSE.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2024 Marco Roth
3
+ Copyright (c) 2024-2025 Marco Roth
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
18
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
19
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
20
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
- THE SOFTWARE.
21
+ THE SOFTWARE.
data/README.md CHANGED
@@ -1,170 +1,135 @@
1
1
  <div align="center">
2
- <img alt="Herb HTML+ERB parser" height="256px" src="https://github.com/user-attachments/assets/d0714ee1-ca33-4aa4-aaa9-d632ba79d54a">
2
+ <img alt="Herb HTML+ERB parser" style="height: 256px" height="256px" src="https://github.com/user-attachments/assets/d0714ee1-ca33-4aa4-aaa9-d632ba79d54a">
3
3
  </div>
4
4
 
5
5
  <h2 align="center">Herb</h2>
6
6
 
7
7
  <h4 align="center">HTML+ERB (HTML + Embedded Ruby)</h4>
8
8
 
9
- <div align="center">Powerful and seamless HTML-aware ERB parsing and tooling.</div>
9
+ <div align="center">Powerful and seamless HTML-aware ERB parsing and tooling.</div><br/>
10
10
 
11
- ## Contributing
12
-
13
- This project builds the Herb program and its associated unit tests using a Makefile for automation. The Makefile provides several useful commands for compiling, running tests, and cleaning the project.
14
-
15
- ### Requirements
16
-
17
- - [**Check**](https://libcheck.github.io/check/): For unit testing.
18
- - [**Clang 19**](https://clang.llvm.org): The compiler used to build this project.
19
- - [**Clang Format 19**](https://clang.llvm.org/docs/ClangFormat.html): For formatting the project.
20
- - [**Clang Tidy 19**](https://clang.llvm.org/extra/clang-tidy/): For linting the project.
21
- - [**Prism Ruby Parser v1.4.0**](https://github.com/ruby/prism/releases/tag/v1.4.0): We use Prism for Parsing the Ruby Source Code in the HTML+ERB files.
22
- - [**Ruby**](https://www.ruby-lang.org/en/): We need Ruby as a dependency for `bundler`.
23
- - [**Bundler**](https://bundler.io): We are using `bundler` to build [`prism`](https://github.com/ruby/prism) from source so we can build `herb` against it.
24
- - [**Emscripten**](https://emscripten.org): For the WebAssembly build of `libherb` so it can be used in the browser using the [`@herb-tools/browser`](https://github.com/marcoroth/herb/blob/main/javascript/packages/browser) package.
25
- - [**Doxygen**](https://www.doxygen.nl): For building the C-Reference documentation pages.
11
+ <p align="center">
12
+ <a href="https://rubygems.org/gems/herb"><img alt="Gem Version" src="https://img.shields.io/gem/v/herb"></a>
13
+ <a href="https://herb-tools.dev"><img alt="Documentation" src="https://img.shields.io/badge/documentation-available-green"></a>
14
+ <a href="https://herb-tools.dev/playground"><img alt="playground" src="https://img.shields.io/badge/playground-Try_it_in_the_browser!-green"></a>
15
+ <a href="https://github.com/marcoroth/herb/blob/main/LICENSE.txt"><img alt="License" src="https://img.shields.io/github/license/marcoroth/herb"></a>
16
+ <a href="https://github.com/marcoroth/herb/issues"><img alt="Issues" src="https://img.shields.io/github/issues/marcoroth/herb"></a>
17
+ </p>
26
18
 
27
- ##### For Linux
19
+ <br/><br/><br/>
28
20
 
29
- ```bash
30
- xargs sudo apt-get install < Aptfile
31
- ```
32
- or:
21
+ ## What is Herb?
33
22
 
34
- ```bash
35
- sudo apt-get install check clang-19 clang-tidy-19 clang-format-19 emscripten doxygen
36
- ```
23
+ **Herb** is an ecosystem of developer tooling built specifically around **HTML+ERB** (`.html.erb`) files. It is designed to simplify and enhance the experience of working with HTML+ERB templates through precise, accurate tooling.
37
24
 
38
- ##### For macOS (using Homebrew)
25
+ At the core of Herb is the **Herb Parser**, a fast, portable, and HTML-aware ERB parser written in C. The parser generates a detailed, accurate syntax tree that serves as the foundation for reliable code analysis, transformations, and developer tooling.
39
26
 
40
- ```bash
41
- brew bundle
42
- ```
43
- or:
27
+ The Herb ecosystem includes **developer tools** (CLI, language server, formatter, linter), **language bindings** (for Ruby, Node.js, and the Browser using WebAssembly), and **utility libraries** (language service, highlighter, minifier, printer). All these components leverage the Herb Parser's syntax tree to provide consistent, accurate, and helpful tooling experiences.
44
28
 
45
- ```bash
46
- brew install check llvm@19 emscripten doxygen
47
- ```
29
+ ## What Herb Can Do for You
48
30
 
49
- ### Building
31
+ Herb provides a complete ecosystem of HTML+ERB tooling, designed to simplify and enhance your daily workflow. Built on the **Herb Parser**, it offers multiple tools that integrate seamlessly into editors, developer environments, and CI pipelines:
50
32
 
51
- #### Clone the Repo
33
+ - **Herb Language Server** ([available now](https://herb-tools.dev/projects/language-server)):
34
+ Rich integration for editors like VS Code, Zed, Neovim, and more. It provides diagnostics and real-time feedback to keep your templates error-free.
52
35
 
53
- Clone the Git Repository:
36
+ - **Herb Formatter** ([experimental preview](https://herb-tools.dev/projects/formatter)):
37
+ Automatic, consistent formatting for HTML+ERB files, reducing manual styling and enforcing a standard across projects. Currently in experimental preview - use with caution on version-controlled files.
54
38
 
55
- ```
56
- git clone https://github.com/marcoroth/herb && cd herb/
57
- ```
39
+ - **Herb Linter** ([available now](https://herb-tools.dev/projects/linter)):
40
+ Static analysis for your HTML+ERB templates to enforce best practices and quickly identify common mistakes with 17 configurable rules.
58
41
 
59
- #### Build Herb
42
+ You can use Herb programmatically in **Ruby**, as well as in **JavaScript** via Node.js, WebAssembly, or directly in browsers.
60
43
 
61
- We can now compile all source files in `src/` and generate the `herb` executable.
44
+ For a complete overview of all available tools, libraries, and integrations, visit the [**Projects page**](https://herb-tools.dev/projects) on our documentation site.
62
45
 
63
- ```bash
64
- make all
65
- ```
46
+ ## Motivation
66
47
 
67
- > [!NOTE]
68
- For any consecutive builds you can just run `make`/`make all`.
48
+ HTML+ERB templates never really had good, accurate, and reliable tooling. While developer tooling for Ruby code improved significantly in the last few years (especially with the introduction of the new Prism parser), HTML+ERB files remained underserved, lacking fundamental support like syntax checking, auto-formatting, linting, and structural understanding.
69
49
 
70
- ### Run
50
+ At the same time, with the rise of tools like [Hotwire](https://hotwired.dev), [Stimulus](https://stimulus.hotwired.dev), [Turbo](https://turbo.hotwired.dev), [HTMX](https://htmx.org), [Unpoly](https://unpoly.com), and [Alpine.js](https://alpinejs.dev), advanced HTML templating became increasingly relevant (again). Developers expect modern, reliable, and precise tooling, especially given the robust ecosystem available to JavaScript frameworks and libraries.
71
51
 
72
- The `herb` executable exposes a few commands for interacting with `.html.erb` files:
52
+ Herb was built to close this tooling gap, providing proper tooling for HTML+ERB that matches what modern developers expect in the age of language servers, LLMs, and AI-driven workflows.
73
53
 
74
- ```
75
- ❯ ./herb
76
- ./herb [command] [options]
54
+ ## Command-Line Usage
77
55
 
78
- Herb 🌿 Powerful and seamless HTML-aware ERB parsing and tooling.
56
+ Install the Herb gem via RubyGems:
79
57
 
80
- ./herb lex [file] - Lex a file
81
- ./herb lex_json [file] - Lex a file and return the result as json.
82
- ./herb parse [file] - Parse a file
83
- ./herb ruby [file] - Extract Ruby from a file
84
- ./herb html [file] - Extract HTML from a file
85
- ./herb prism [file] - Extract Ruby from a file and parse the Ruby source with Prism
58
+ ```sh
59
+ gem install herb
86
60
  ```
87
61
 
88
- Running the executable shows a pretty-printed output for the respective command and the time it took to execute:
62
+ Basic usage to analyze all HTML+ERB files in your project:
89
63
 
64
+ ```sh
65
+ herb analyze .
90
66
  ```
91
- ❯ ./herb lex examples/simple_erb.html.erb
92
-
93
- #<Herb::Token type="TOKEN_ERB_START" value="<%" range=[0, 2] start=(1:0) end=(1:2)>
94
- #<Herb::Token type="TOKEN_ERB_CONTENT" value=" title " range=[2, 9] start=(1:2) end=(1:9)>
95
- #<Herb::Token type="TOKEN_ERB_END" value="%>" range=[9, 11] start=(1:9) end=(1:11)>
96
- #<Herb::Token type="TOKEN_NEWLINE" value="\n" range=[11, 12] start=(1:0) end=(2:1)>
97
- #<Herb::Token type="TOKEN_EOF" value="" range=[12, 12] start=(2:1) end=(2:1)>
98
67
 
99
- Finished lexing in:
100
-
101
- 12 µs
102
- 0.012 ms
103
- 0.000012 s
68
+ This will give you an overview of how the Herb Parser sees your project:
104
69
  ```
70
+ --- SUMMARY --------------------------------------------------------------------
71
+ Total files: 145
72
+ ✅ Successful: 143 (98.6%)
73
+ ❌ Failed: 0 (0.0%)
74
+ ⚠️ Parse errors: 2 (1.4%)
75
+ ⏱️ Timed out: 0 (0.0%)
105
76
 
106
- ### Building the Ruby extension
107
-
108
- We use `rake` and `rake-compiler` to compile the Ruby extension. Running rake will generate the needed templates, run make, build the needed artifacts, and run the Ruby tests.
77
+ Files with parse errors:
78
+ - app/views/contributions/index.html.erb
79
+ - index.html.erb
109
80
 
110
- ```bash
111
- rake
81
+ Results saved to 2025-06-29_12-16-23_erb_parsing_result_rubyevents.log
112
82
  ```
113
83
 
114
- If `rake` was successful you can use `bundle console` to interact with `Herb`:
84
+ Herb also comes with other useful commands:
115
85
 
116
- ```bash
117
- bundle console
118
- ```
119
-
120
- ```
121
- irb(main):001> Herb.parse("<div></div>")
122
-
123
- # => #<Herb::ParseResult:0x0000000 ... >
124
- ```
125
-
126
- ### Test
127
-
128
- Builds the test suite from files in `test/` and creates the `run_herb_tests` executable to run the tests:
129
-
130
- #### For the C Tests
131
-
132
- ```bash
133
- make test && ./run_herb_tests
134
86
  ```
87
+ Herb 🌿 Powerful and seamless HTML-aware ERB parsing and tooling.
135
88
 
136
- #### For the Ruby Tests
89
+ Usage:
90
+ bundle exec herb [command] [options]
137
91
 
138
- ```bash
139
- rake test
92
+ Commands:
93
+ bundle exec herb lex [file] Lex a file.
94
+ bundle exec herb parse [file] Parse a file.
95
+ bundle exec herb analyze [path] Analyze a project by passing a directory to the root of the project
96
+ bundle exec herb ruby [file] Extract Ruby from a file.
97
+ bundle exec herb html [file] Extract HTML from a file.
98
+ bundle exec herb playground [file] Open the content of the source file in the playground
99
+ bundle exec herb version Prints the versions of the Herb gem and the libherb library.
140
100
  ```
141
101
 
142
- ### Clean
102
+ For detailed information, like how you can use Herb progamatiacally in Ruby and JavaScript, visit the [documentation site](https://herb-tools.dev/bindings/ruby/reference).
143
103
 
144
- Removes the `herb`, `run_herb_tests`, `prism` installation, and all `.o` files.
145
104
 
146
- ```bash
147
- make clean
148
- ```
105
+ ## Background and Talk
149
106
 
150
- ### Local Integration Testing
107
+ **Herb** was first introduced at [**RubyKaigi 2025**](https://rubykaigi.org/2025/presentations/marcoroth.html) in April 2025 with the talk [*Empowering Developers with HTML-Aware ERB Tooling*](https://www.rubyevents.org/talks/empowering-developers-with-html-aware-erb-tooling-rubykaigi-2025).
151
108
 
152
- The `bin/integration` script allows for quick local iteration. On every run it cleans the directory, builds the source from scratch and runs all checks, including the C-Tests, Ruby Tests, Linters, and examples in succession.
109
+ ## Contributing
153
110
 
154
- ```bash
155
- bin/integration
156
- ```
111
+ Bug reports and pull requests are welcome on [GitHub](https://github.com/marcoroth/herb). Please see the [CONTRIBUTING.md](https://github.com/marcoroth/herb/blob/main/CONTRIBUTING.md) document for guidelines on how to set up Herb for local development and how to contribute to **Herb**.
157
112
 
158
- The integration was successful if you see:
113
+ ## Prior Art & Inspiration
159
114
 
160
- ```
161
- ❯ bin/integration
115
+ While Herb brings a fresh approach to HTML+ERB tooling, it builds upon and learns from several existing tools and approaches in the ecosystem:
162
116
 
163
- [...]
117
+ - [**Tree-sitter**](https://tree-sitter.github.io/tree-sitter/)
118
+ - [**tree-sitter-embedded-template**](https://github.com/tree-sitter/tree-sitter-embedded-template)
119
+ - [**Prism Ruby Parser**](https://github.com/ruby/prism)
120
+ - [**Ruby LSP**](https://github.com/Shopify/ruby-lsp)
121
+ - [**better-html**](https://github.com/Shopify/better-html)
122
+ - [**erb_lint**](https://github.com/Shopify/erb_lint)
123
+ - [**erb-formatter**](https://github.com/nebulab/erb-formatter)
124
+ - [**erb-formatter-vscode**](https://github.com/nebulab/erb-formatter-vscode)
125
+ - [**deface**](https://github.com/spree/deface)
126
+ - [**html_press**](https://github.com/stereobooster/html_press)
127
+ - [**htmlbeautifier**](https://github.com/threedaymonk/htmlbeautifier)
128
+ - [**vscode-erb-beautify**](https://github.com/aliariff/vscode-erb-beautify)
129
+ - [**vscode-erb-linter**](https://github.com/manuelpuyol/vscode-erb-linter)
164
130
 
165
- Integration successful!
166
- ```
131
+ Herb differentiates itself by being HTML-aware from the ground up, providing a unified parsing approach that understands both HTML and ERB as first-class citizens, rather than treating one as embedded within the other.
167
132
 
168
133
  ## License
169
134
 
170
- This project is licensed under the MIT License - see the [LICENSE](LICENSE.txt) file for details.
135
+ This project is available as open source under the terms of the [MIT License](https://github.com/marcoroth/herb/blob/main/LICENSE.txt).
@@ -1,5 +1,5 @@
1
1
  // NOTE: This file is generated by the templates/template.rb script and should not
2
- // be modified manually. See /Users/marcoroth/Development/herb-release/templates/ext/herb/error_helpers.c.erb
2
+ // be modified manually. See /Users/marcoroth/Development/herb-release-6/templates/ext/herb/error_helpers.c.erb
3
3
 
4
4
  #include <ruby.h>
5
5
 
@@ -1,5 +1,5 @@
1
1
  // NOTE: This file is generated by the templates/template.rb script and should not
2
- // be modified manually. See /Users/marcoroth/Development/herb-release/templates/ext/herb/error_helpers.h.erb
2
+ // be modified manually. See /Users/marcoroth/Development/herb-release-6/templates/ext/herb/error_helpers.h.erb
3
3
 
4
4
  #ifndef HERB_EXTENSION_ERROR_HELPERS_H
5
5
  #define HERB_EXTENSION_ERROR_HELPERS_H
data/ext/herb/nodes.c CHANGED
@@ -1,5 +1,5 @@
1
1
  // NOTE: This file is generated by the templates/template.rb script and should not
2
- // be modified manually. See /Users/marcoroth/Development/herb-release/templates/ext/herb/nodes.c.erb
2
+ // be modified manually. See /Users/marcoroth/Development/herb-release-6/templates/ext/herb/nodes.c.erb
3
3
 
4
4
  #include <ruby.h>
5
5
 
@@ -415,7 +415,7 @@ static VALUE rb_erb_content_node_from_c_struct(AST_ERB_CONTENT_NODE_T* erb_conte
415
415
  VALUE erb_content_node_tag_opening = rb_token_from_c_struct(erb_content_node->tag_opening);
416
416
  VALUE erb_content_node_content = rb_token_from_c_struct(erb_content_node->content);
417
417
  VALUE erb_content_node_tag_closing = rb_token_from_c_struct(erb_content_node->tag_closing);
418
- /* #<Herb::Template::AnalyzedRubyField:0x00007fffe335bca0 @name="analyzed_ruby", @options={kind: nil}> */
418
+ /* #<Herb::Template::AnalyzedRubyField:0x00007fffe335ba20 @name="analyzed_ruby", @options={kind: nil}> */
419
419
  VALUE erb_content_node_analyzed_ruby = Qnil;
420
420
  VALUE erb_content_node_parsed = (erb_content_node->parsed) ? Qtrue : Qfalse;
421
421
  VALUE erb_content_node_valid = (erb_content_node->valid) ? Qtrue : Qfalse;
data/ext/herb/nodes.h CHANGED
@@ -1,5 +1,5 @@
1
1
  // NOTE: This file is generated by the templates/template.rb script and should not
2
- // be modified manually. See /Users/marcoroth/Development/herb-release/templates/ext/herb/nodes.h.erb
2
+ // be modified manually. See /Users/marcoroth/Development/herb-release-6/templates/ext/herb/nodes.h.erb
3
3
 
4
4
  #ifndef HERB_EXTENSION_NODES_H
5
5
  #define HERB_EXTENSION_NODES_H
data/lib/herb/3.0/herb.so CHANGED
Binary file
data/lib/herb/3.1/herb.so CHANGED
Binary file
data/lib/herb/3.2/herb.so CHANGED
Binary file
data/lib/herb/3.3/herb.so CHANGED
Binary file
data/lib/herb/3.4/herb.so CHANGED
Binary file
data/lib/herb/ast/node.rb CHANGED
@@ -51,7 +51,7 @@ module Herb
51
51
  output = +""
52
52
 
53
53
  if array.any?
54
- output += "(#{array.count} #{array.count == 1 ? item_name : "#{item_name}s"})"
54
+ output += "(#{array.count} #{array.one? ? item_name : "#{item_name}s"})"
55
55
  output += "\n"
56
56
 
57
57
  items = array.map { |item|
@@ -92,6 +92,11 @@ module Herb
92
92
  def compact_child_nodes
93
93
  child_nodes.compact
94
94
  end
95
+
96
+ #: () -> Array[Herb::Errors::Error]
97
+ def recursive_errors
98
+ errors + compact_child_nodes.flat_map(&:recursive_errors)
99
+ end
95
100
  end
96
101
  end
97
102
  end
@@ -2,7 +2,7 @@
2
2
  # typed: true
3
3
 
4
4
  # NOTE: This file is generated by the templates/template.rb script and should not be
5
- # modified manually. See /Users/marcoroth/Development/herb-release/templates/lib/herb/ast/nodes.rb.erb
5
+ # modified manually. See /Users/marcoroth/Development/herb-release-6/templates/lib/herb/ast/nodes.rb.erb
6
6
 
7
7
  module Herb
8
8
  module AST
data/lib/herb/cli.rb CHANGED
@@ -6,7 +6,7 @@
6
6
  require "optparse"
7
7
 
8
8
  class Herb::CLI
9
- attr_accessor :json, :silent
9
+ attr_accessor :json, :silent, :no_interactive, :no_log_file, :no_timing
10
10
 
11
11
  def initialize(args)
12
12
  @args = args
@@ -106,7 +106,11 @@ class Herb::CLI
106
106
  def result
107
107
  @result ||= case @command
108
108
  when "analyze"
109
- Herb::Project.new(directory).parse!
109
+ project = Herb::Project.new(directory)
110
+ project.no_interactive = no_interactive
111
+ project.no_log_file = no_log_file
112
+ project.no_timing = no_timing
113
+ project.parse!
110
114
  exit(0)
111
115
  when "parse"
112
116
  Herb.parse(file_content)
@@ -162,6 +166,18 @@ class Herb::CLI
162
166
  parser.on("-s", "--silent", "Log no result to stdout") do
163
167
  self.silent = true
164
168
  end
169
+
170
+ parser.on("-n", "--non-interactive", "Disable interactive output (progress bars, terminal clearing)") do
171
+ self.no_interactive = true
172
+ end
173
+
174
+ parser.on("--no-log-file", "Disable log file generation") do
175
+ self.no_log_file = true
176
+ end
177
+
178
+ parser.on("--no-timing", "Disable timing output") do
179
+ self.no_timing = true
180
+ end
165
181
  end
166
182
  end
167
183
 
data/lib/herb/errors.rb CHANGED
@@ -2,7 +2,7 @@
2
2
  # typed: true
3
3
 
4
4
  # NOTE: This file is generated by the templates/template.rb script and should not be
5
- # modified manually. See /Users/marcoroth/Development/herb-release/templates/lib/herb/errors.rb.erb
5
+ # modified manually. See /Users/marcoroth/Development/herb-release-6/templates/lib/herb/errors.rb.erb
6
6
 
7
7
  module Herb
8
8
  module Errors
@@ -12,9 +12,14 @@ module Herb
12
12
  super(source, warnings, errors)
13
13
  end
14
14
 
15
+ #: () -> Array[Herb::Errors::Error]
16
+ def errors
17
+ super + value.recursive_errors
18
+ end
19
+
15
20
  #: () -> bool
16
21
  def failed?
17
- errors.any? || value.errors.any? # TODO: this should probably be recursive
22
+ errors.any?
18
23
  end
19
24
 
20
25
  #: () -> bool
@@ -24,7 +29,7 @@ module Herb
24
29
 
25
30
  #: () -> String
26
31
  def pretty_errors
27
- JSON.pretty_generate(errors + value.errors)
32
+ JSON.pretty_generate(errors)
28
33
  end
29
34
 
30
35
  #: (Visitor) -> void
data/lib/herb/project.rb CHANGED
@@ -8,10 +8,17 @@ require "timeout"
8
8
  require "tempfile"
9
9
  require "pathname"
10
10
  require "English"
11
+ require "stringio"
11
12
 
12
13
  module Herb
13
14
  class Project
14
- attr_accessor :project_path, :output_file
15
+ attr_accessor :project_path, :output_file, :no_interactive, :no_log_file, :no_timing
16
+
17
+ def interactive?
18
+ return false if no_interactive
19
+
20
+ !IO.console.nil?
21
+ end
15
22
 
16
23
  def initialize(project_path, output_file: nil)
17
24
  @project_path = Pathname.new(
@@ -39,7 +46,15 @@ module Herb
39
46
  end
40
47
 
41
48
  def parse!
42
- File.open(output_file, "w") do |log|
49
+ start_time = Time.now unless no_timing
50
+
51
+ log = if no_log_file
52
+ StringIO.new
53
+ else
54
+ File.open(output_file, "w")
55
+ end
56
+
57
+ begin
43
58
  log.puts heading("METADATA")
44
59
  log.puts "Herb Version: #{Herb.version}"
45
60
  log.puts "Reported at: #{Time.now.strftime("%Y-%m-%dT%H:%M:%S")}\n\n"
@@ -54,10 +69,10 @@ module Herb
54
69
  message = "No .html.erb files found using #{full_path_glob}"
55
70
  log.puts message
56
71
  puts message
57
- next
72
+ return
58
73
  end
59
74
 
60
- print "\e[H\e[2J"
75
+ print "\e[H\e[2J" if interactive?
61
76
 
62
77
  successful_files = []
63
78
  failed_files = []
@@ -77,37 +92,43 @@ module Herb
77
92
  lines_to_clear += 3 if total_timeout.positive?
78
93
  lines_to_clear += 3 if total_errors.positive?
79
94
 
80
- lines_to_clear.times { print "\e[1A\e[K" } if index.positive?
95
+ lines_to_clear.times { print "\e[1A\e[K" } if index.positive? && interactive?
81
96
 
82
- puts "Parsing .html.erb files in: #{project_path}"
83
- puts "Total files to process: #{files.count}\n"
97
+ if interactive?
98
+ puts "Parsing .html.erb files in: #{project_path}"
99
+ puts "Total files to process: #{files.count}\n"
84
100
 
85
- relative_path = file_path.sub("#{project_path}/", "")
101
+ relative_path = file_path.sub("#{project_path}/", "")
86
102
 
87
- puts
88
- puts progress_bar(index + 1, files.count)
89
- puts
90
- puts "Processing [#{index + 1}/#{files.count}]: #{relative_path}"
91
-
92
- if failed_files.any?
93
103
  puts
94
- puts "Files that failed:"
95
- failed_files.each { |file| puts " - #{file}" }
104
+ puts progress_bar(index + 1, files.count)
96
105
  puts
106
+ else
107
+ relative_path = file_path.sub("#{project_path}/", "")
97
108
  end
109
+ puts "Processing [#{index + 1}/#{files.count}]: #{relative_path}"
98
110
 
99
- if timeout_files.any?
100
- puts
101
- puts "Files that timed out:"
102
- timeout_files.each { |file| puts " - #{file}" }
103
- puts
104
- end
111
+ if interactive?
112
+ if failed_files.any?
113
+ puts
114
+ puts "Files that failed:"
115
+ failed_files.each { |file| puts " - #{file}" }
116
+ puts
117
+ end
105
118
 
106
- if error_files.any?
107
- puts
108
- puts "Files with parse errors:"
109
- error_files.each { |file| puts " - #{file}" }
110
- puts
119
+ if timeout_files.any?
120
+ puts
121
+ puts "Files that timed out:"
122
+ timeout_files.each { |file| puts " - #{file}" }
123
+ puts
124
+ end
125
+
126
+ if error_files.any?
127
+ puts
128
+ puts "Files with parse errors:"
129
+ error_files.each { |file| puts " - #{file}" }
130
+ puts
131
+ end
111
132
  end
112
133
 
113
134
  begin
@@ -214,10 +235,13 @@ module Herb
214
235
  end
215
236
  end
216
237
 
217
- print "\e[1A\e[K"
218
- puts "Completed processing all files."
219
-
220
- print "\e[H\e[2J"
238
+ if interactive?
239
+ print "\e[1A\e[K"
240
+ puts "Completed processing all files."
241
+ print "\e[H\e[2J"
242
+ else
243
+ puts "Completed processing all files."
244
+ end
221
245
 
222
246
  log.puts ""
223
247
 
@@ -333,13 +357,23 @@ module Herb
333
357
  end
334
358
  end
335
359
 
336
- puts "\nResults saved to #{output_file}"
360
+ unless no_timing
361
+ end_time = Time.now
362
+ duration = end_time - start_time
363
+ timing_message = "\n⏱️ Total time: #{format_duration(duration)}"
364
+ log.puts timing_message
365
+ puts timing_message
366
+ end
367
+
368
+ puts "\nResults saved to #{output_file}" unless no_log_file
369
+ ensure
370
+ log.close unless no_log_file
337
371
  end
338
372
  end
339
373
 
340
374
  private
341
375
 
342
- def progress_bar(current, total, width = IO.console.winsize[1] - "[] 100% (#{total}/#{total})".length)
376
+ def progress_bar(current, total, width = (IO.console&.winsize&.[](1) || 80) - "[] 100% (#{total}/#{total})".length)
343
377
  progress = current.to_f / total
344
378
  completed_length = (progress * width).to_i
345
379
  completed = "█" * completed_length
@@ -366,5 +400,17 @@ module Herb
366
400
 
367
401
  prefix + ("-" * (80 - prefix.length))
368
402
  end
403
+
404
+ def format_duration(seconds)
405
+ if seconds < 1
406
+ "#{(seconds * 1000).round(2)}ms"
407
+ elsif seconds < 60
408
+ "#{seconds.round(2)}s"
409
+ else
410
+ minutes = (seconds / 60).to_i
411
+ remaining_seconds = seconds % 60
412
+ "#{minutes}m #{remaining_seconds.round(2)}s"
413
+ end
414
+ end
369
415
  end
370
416
  end
data/lib/herb/version.rb CHANGED
@@ -2,5 +2,5 @@
2
2
  # typed: true
3
3
 
4
4
  module Herb
5
- VERSION = "0.3.1"
5
+ VERSION = "0.4.2"
6
6
  end
data/lib/herb/visitor.rb CHANGED
@@ -2,7 +2,7 @@
2
2
  # typed: true
3
3
 
4
4
  # NOTE: This file is generated by the templates/template.rb script and should not be
5
- # modified manually. See /Users/marcoroth/Development/herb-release/templates/lib/herb/visitor.rb.erb
5
+ # modified manually. See /Users/marcoroth/Development/herb-release-6/templates/lib/herb/visitor.rb.erb
6
6
 
7
7
  module Herb
8
8
  class Visitor
@@ -43,6 +43,9 @@ module Herb
43
43
 
44
44
  # : () -> Array[Herb::AST::Node]
45
45
  def compact_child_nodes: () -> Array[Herb::AST::Node]
46
+
47
+ # : () -> Array[Herb::Errors::Error]
48
+ def recursive_errors: () -> Array[Herb::Errors::Error]
46
49
  end
47
50
  end
48
51
  end