Wire prism_nodes injection for the remaining 10 prism-dependent rules (herb-embedded-ada) - #47
Merged
Conversation
… (herb-embedded-ada) ResultEnvelope.parse now handles prism_nodes/prism_nodes_deep the same way gu7 handled prism_program: never forward the flag to Herb.parse itself (which returns raw ASCII-8BIT bytes and breaks .to_json), but compute the Prism bytes separately as a JSON-safe Integer array and inject them. Unlike prism_program's single whole-document parse, prism_nodes needs one Prism parse per AST_ERB_* node, scoped to just that node's own embedded-Ruby content token but still offset-correct against the whole file: every byte outside the node's content range gets blanked (preserving newlines) before re-dumping, so the resulting parse's only real statement is that node's own expression, at its true position in the file. Ruby's Prism.dump has no API to serialize an arbitrary sub-node, so the injected bytes always deserialize to a ProgramNode - but every prism_nodes-dependent rule expects prismNode to be the single embedded expression node directly (e.g. isAssignmentNode checks prismNode.constructor.name). js/ruby_backend.js patches every ERB*Node's prismNode getter, once, to unwrap a single-statement ProgramNode down to its inner node, rather than touching the vendored bundle. Manually verified all 10 target rules (a11y-no-autofocus-attribute, actionview-no-silent-helper, actionview-no-unnecessary-tag-attributes, erb-no-output-in-attribute-position, erb-no-silent-statement, erb-no-unsafe-raw, erb-no-unsafe-script-interpolation, erb-no-unused-expressions, erb-no-unused-literals, erb-prefer-direct-output) produce offense-for-offense matching diagnostics against the real reference linter, including offset-derived locations.
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.
Summary
ResultEnvelope.parsenow handlesprism_nodes/prism_nodes_deepthe same way gu7 handledprism_program: never forward the flag toHerb.parseitself (raw ASCII-8BIT bytes break.to_json), but compute the Prism bytes separately as a JSON-safeIntegerarray and inject them — one Prism parse perAST_ERB_*node, scoped to that node's own embedded-Ruby content but still offset-correct against the whole file (blank every byte outside the node's content range, preserving newlines, then re-dump).Prism.dumphas no API to serialize an arbitrary sub-node, so injected bytes always deserialize to aProgramNode— but everyprism_nodes-dependent rule expectsprismNodeto be the single embedded expression node directly (e.g.isAssignmentNodechecksprismNode.constructor.name).js/ruby_backend.jspatches everyERB*Node'sprismNodegetter, once, to unwrap a single-statementProgramNodedown to its inner node — a scoped patch in our own integration file, not the vendored bundle.CHARTER.md's prism_program/prism_nodes note (from gu7) is rewritten to document both mechanisms now being wired.Acceptance criteria
Bridge#lint— manually verified offense-for-offense against the real reference linter (npx @herb-tools/linter) for all 10 (a11y-no-autofocus-attribute,actionview-no-silent-helper,actionview-no-unnecessary-tag-attributes,erb-no-output-in-attribute-position,erb-no-silent-statement,erb-no-unsafe-raw,erb-no-unsafe-script-interpolation,erb-no-unused-expressions,erb-no-unused-literals,erb-prefer-direct-output), including rules whose offense location is derived fromprismNode's own byte offsets. Two of these (erb-no-unsafe-raw,erb-no-silent-statement) get automated end-to-end coverage inspec/bridge_lint_spec.rb.JSON::GeneratorErrorbug —FORWARDABLE_OPTIONSstill excludesprism_nodes/prism_nodes_deepfromHerb.parseitself; injected bytes are always a plainIntegerarray. New specs inspec/result_envelope_spec.rbassert JSON-safety and shape for bothprism_nodesandprism_nodes_deep.CHARTER.mdupdated to describe the implemented mechanism, which node types carry injected bytes, how offsets are computed, and why/where the JS-side unwrap patch lives.bundle exec rakepasses locally (105 examples, 0 failures, no RuboCop offenses).Test plan
bundle exec rake(RuboCop + full RSpec suite) passes locally.npx @herb-tools/linteron hand-built fixtures, plus negative cases (assignments correctly skipped, plain output correctly not flagged, mutation calls correctly excluded).🤖 Generated with Claude Code