Repository navigation
Conversation
Glob results come back in filesystem and runtime dependent order, so the linter's and formatter's output order could differ between machines. Sort them in `Config#findFilesForTool` and in the linter's and formatter's own glob paths. Cap the linter's default number of workers at 8. Each worker loads its own WASM parser and config, so on machines with many cores the startup cost of one worker per core outweighs the extra parallelism. Also declare the linter's missing build dependency on @herb-tools/client, which it imports from `@herb-tools/client/directives`. Claude-Session: https://claude.ai/code/session_017jYmXZLoC2KgCkEWLEQqyy
…ghter` gems Package `herb-lint`, `herb-format` and `herb-highlight` as standalone executables compiled with `bun build --compile`, distributed as platform gems so Ruby projects can lint and format templates without Node.js. - `rake standalone:gems` builds gems for arm64-darwin, x86_64-darwin, aarch64-linux-gnu, aarch64-linux-musl, x86_64-linux-gnu, x86_64-linux-musl and x64-mingw-ucrt, plus a plain `ruby` gem that falls back to `npx` on other platforms. - The musl gems bundle libstdc++ and libgcc from Alpine Linux, so they run on a stock Alpine image. - Custom rules and rewriters that import Herb packages by name are served the copies embedded in the binary, including in the linter's workers. - `herb lint`, `herb format` and `herb highlight` use the gem's executable when the project doesn't have the npm package installed. - `bin/standalone-smoke-test` checks the installed gems without Node.js, and with `--compare-node` against the npm packages. - The Standalone Gems workflow builds the gems, smoke tests them on every platform and pushes them on release. Claude-Session: https://claude.ai/code/session_017jYmXZLoC2KgCkEWLEQqyy
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Builds on #2856, which should be merged first (this branch includes its commit).
This packages
herb-lint,herb-formatandherb-highlightas standalone executables, so Ruby projects can lint and format templates without Node.js, in CI and in local development. They're published as theherb-linter,herb-formatterandherb-highlightergems you reserved:bundle add herb-linter --group development bundle exec herb-lintherb lint,herb formatandherb highlightpick up the gem automatically.@marcoroth Before you spend time reviewing: is Bun the direction you'd like to go for this, or would you rather port it to something else? I compared the alternatives below and Bun came out ahead for now, but this is your call, and I'm happy to rework it or close it.
How it works
bun build --compilecompiles each CLI, including the WASM parser and highlighter themes, into a single executable. Bun cross-compiles every platform from one Linux machine.rake standalone:gemsbuilds one gem per platform, plus a plainrubygem. The gems' version followsHerb::VERSION..herb/rulesand.herb/rewritersthat import@herb-tools/linter,@herb-tools/rewriterand so on by name get the copies embedded in the binary. This usesBun.plugin, and it also works in the linter's worker threads. The entrypoints are injavascript/packages/{linter,formatter}/src/standalone/.libstdc++, which Alpine doesn't install by default. The musl gems bundlelibstdc++andlibgccfrom Alpine's packages (pinned with checksums), and their RUNPATH points at them, so they run on a stockruby:*-alpineimage. These libraries are GPL-3.0 with the GCC Runtime Library Exception. The musl gems list that license and include aNOTICEwith a link to the source.herb lintand the other commands try, in order: the project'snode_modules/.bin, then the gem's executable, then thePATH, thennpx.Platforms
herbx86_64-linux-gnu,aarch64-linux-gnux86_64-linux-musl,aarch64-linux-muslarm64-darwin,x86_64-darwinx64-mingw-ucrtarm-linux-*,x86-linux-*(32-bit)On unsupported platforms, the
rubygem still installs, so a sharedGemfile.lockkeeps working. Itsherb-lintruns the npm package vianpxwhen Node.js is available, and otherwise explains what to install.Alternatives considered
module.registerHooksand worker threads work, so the entrypoints would carry over almost one-to-one. But SEA doesn't support Intel macOS (nodejs/node#62893), it skips Alpine in its tests, Node has no official arm64 musl build, and the glibc binaries also needlibatomic. The SEA virtual file system is also still marked "early development".deno compile): has no musl targets.Things to decide
Gemfile.lock(bundle lock --add-platform x86_64-linux). Otherwise Bundler resolves therubygem in CI, which then needs Node. This should probably go in the CI integration docs.docs/could get a "Ruby project without Node.js" example.javascript/packages/**orsrc/**changes. It could build fewer platforms on pull requests.Setup before the first release
The push job uses trusted publishing, the same as
build-gems.yml, sostandalone-gems.ymlneeds to be added as a trusted publisher for all three gems on RubyGems.org.Testing
bin/standalone-smoke-testruns the installed gems without Node.js. It checks built-in rules, a custom rule both in parallel workers and with--jobs 1,--fix,herb-format --check, formatting with a custom rewriter, and highlighting.--compare-nodealso checks that the output matches the npm packages.--compare-node, and on stockruby:3.4-alpineon arm64 withoutlibstdc++installed.https://claude.ai/code/session_017jYmXZLoC2KgCkEWLEQqyy