Skip to content

feat(rubocop): ship the docs-kit RuboCop cops from the gem #22

Description

@mhenrixon

Part of #8 (Phase 5).

Problem / Goal

Both consumer sites carry a byte-identical hand-copied cop — RuboCop::Cop::DocsKit::RenderComponentPreferred (enforces DocsUI::Code(...) kit-form over render DocsUI::Code.new(...)) at daisyui/docs/lib/rubocop/cop/docs_kit/... and phlex-reactive/docs/lib/rubocop/cop/docs_kit/.... It is already namespaced as if it were the gem's. Copy-paste cops drift and new sites don't get them.

Goal: the cops ship in the gem; a consuming site's .rubocop.yml gets two lines (scaffolded); the site copies get deleted.

Context (read these first)

  • ~/Code/mhenrixon/daisyui/docs/lib/rubocop/cop/docs_kit/render_component_preferred.rb — the cop to upstream verbatim (it's proven; don't rewrite it).
  • Both sites' .rubocop.yml require: lines — the wiring to replace.
  • How gems ship cops: lib/rubocop/cop/docs_kit/ + a config/default.yml-style docs_kit.yml, loaded via require: [docs_kit/rubocop] + inherit_gem:.
  • The heredoc-escape foot-gun from the audits (\#{...} scattered through docs pages) — the one NEW cop worth adding.

Decision

  1. Move the cop into the gem at lib/rubocop/cop/docs_kit/render_component_preferred.rb, entry point lib/docs_kit/rubocop.rb (requires rubocop lazily — rubocop stays a development-time dependency of the HOST, not a runtime dep of docs-kit; document that sites need rubocop in their Gemfile, which every generated site has).
  2. Ship config/rubocop/docs_kit.yml with both cops enabled and scoped to app/views/docs/**/* by default.
  3. ONE new cop: DocsKit/EscapedInterpolationInHeredoc — flags \#{ inside a heredoc and suggests the single-quoted delimiter (<<~'RUBY') — kills the recurring escape tax found in every audited site. Autocorrect: unsafe (only when the heredoc contains no unescaped interpolation), otherwise suggestion-only.
  4. Install generator + docs-kit new: append the require/inherit_gem lines to the site's .rubocop.yml (create a minimal one if absent), idempotently.
  5. Consumer follow-up (their repos, not this one): delete the copies, switch to the gem require.

Alternatives rejected: a separate rubocop-docs_kit gem (release/version overhead for two cops; in-gem is the daisyui-gem-style precedent and the cop is already namespaced for it); porting the cop into a lint rake task (loses editor integration).

Implementation steps (TDD)

  1. Cop specs first, spec/rubocop/cop/docs_kit/render_component_preferred_spec.rb (port the site's spec if one exists, else write: registers offense on render DocsUI::X.new(...) inside docs page paths, autocorrects to kit form, ignores non-DocsUI renders) and escaped_interpolation_in_heredoc_spec.rb (offense on \#{ in double-quoted heredoc; no offense in <<~'X'; safe-autocorrect case). Use rubocop-rspec's expect_offense harness; add rubocop to the gemspec development dependencies (it's already in the Gemfile for linting).
  2. Move/implement the cops + lib/docs_kit/rubocop.rb + the shipped YAML.
  3. Generator wiring (idempotent .rubocop.yml injection) + generator spec addition.
  4. Run the new cops over the gem's own docs/ app; fix what they flag (dogfood).
  5. README note + entries in the consumer re-sync guide (separate issue).

Verification gates

  • bundle exec rspec spec/rubocop — green.
  • bundle exec rubocop — no offenses (including the new cops against the dogfood app if wired there).
  • In the dogfood app: .rubocop.yml requires the gem path and bundle exec rubocop runs the docs-kit cops.
  • Zeitwerk safety: lib/rubocop/** is outside the gem's loader dirs — confirm loader.ignore isn't needed (it only pushes lib/docs_kit + app/components/docs_ui); a spec boots the gem and asserts no zeitwerk complaint.

Out of scope

  • App-4's site-specific cops (raw_anchor/raw_button etc. — they enforce Zazu's own UI kit, not docs-kit's).
  • More style cops (add only when a third real duplication appears).
  • Editing the consumer repos.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions