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
- 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).
- Ship
config/rubocop/docs_kit.yml with both cops enabled and scoped to app/views/docs/**/* by default.
- 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.
- Install generator +
docs-kit new: append the require/inherit_gem lines to the site's .rubocop.yml (create a minimal one if absent), idempotently.
- 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)
- 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).
- Move/implement the cops +
lib/docs_kit/rubocop.rb + the shipped YAML.
- Generator wiring (idempotent
.rubocop.yml injection) + generator spec addition.
- Run the new cops over the gem's own
docs/ app; fix what they flag (dogfood).
- 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.
Part of #8 (Phase 5).
Problem / Goal
Both consumer sites carry a byte-identical hand-copied cop —
RuboCop::Cop::DocsKit::RenderComponentPreferred(enforcesDocsUI::Code(...)kit-form overrender DocsUI::Code.new(...)) atdaisyui/docs/lib/rubocop/cop/docs_kit/...andphlex-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.ymlgets 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)..rubocop.ymlrequire:lines — the wiring to replace.lib/rubocop/cop/docs_kit/+ aconfig/default.yml-styledocs_kit.yml, loaded viarequire: [docs_kit/rubocop]+inherit_gem:.\#{...}scattered through docs pages) — the one NEW cop worth adding.Decision
lib/rubocop/cop/docs_kit/render_component_preferred.rb, entry pointlib/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 needrubocopin their Gemfile, which every generated site has).config/rubocop/docs_kit.ymlwith both cops enabled and scoped toapp/views/docs/**/*by default.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.docs-kit new: append therequire/inherit_gemlines to the site's.rubocop.yml(create a minimal one if absent), idempotently.Alternatives rejected: a separate
rubocop-docs_kitgem (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)
spec/rubocop/cop/docs_kit/render_component_preferred_spec.rb(port the site's spec if one exists, else write: registers offense onrender DocsUI::X.new(...)inside docs page paths, autocorrects to kit form, ignores non-DocsUI renders) andescaped_interpolation_in_heredoc_spec.rb(offense on\#{in double-quoted heredoc; no offense in<<~'X'; safe-autocorrect case). Userubocop-rspec'sexpect_offenseharness; addrubocopto the gemspec development dependencies (it's already in the Gemfile for linting).lib/docs_kit/rubocop.rb+ the shipped YAML..rubocop.ymlinjection) + generator spec addition.docs/app; fix what they flag (dogfood).Verification gates
bundle exec rspec spec/rubocop— green.bundle exec rubocop— no offenses (including the new cops against the dogfood app if wired there)..rubocop.ymlrequires the gem path andbundle exec rubocopruns the docs-kit cops.lib/rubocop/**is outside the gem's loader dirs — confirmloader.ignoreisn't needed (it only pusheslib/docs_kit+app/components/docs_ui); a spec boots the gem and asserts no zeitwerk complaint.Out of scope