rolemodel-rails 2.1.0 → 2.3.0

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 (97) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +30 -9
  3. data/Rakefile +3 -3
  4. data/lib/generators/rolemodel/all_generator.rb +1 -0
  5. data/lib/generators/rolemodel/core_setup/README.md +27 -0
  6. data/lib/generators/rolemodel/core_setup/USAGE +12 -0
  7. data/lib/generators/rolemodel/core_setup/core_setup_generator.rb +23 -0
  8. data/lib/generators/rolemodel/github/README.md +4 -0
  9. data/lib/generators/rolemodel/github/templates/workflows/deploy-production.yml +34 -0
  10. data/lib/generators/rolemodel/github/templates/workflows/deploy-staging.yml +35 -0
  11. data/lib/generators/rolemodel/good_job/good_job_generator.rb +7 -13
  12. data/lib/generators/rolemodel/heroku/README.md +20 -1
  13. data/lib/generators/rolemodel/heroku/heroku_generator.rb +20 -0
  14. data/lib/generators/rolemodel/heroku/templates/app.json.tt +5 -2
  15. data/lib/generators/rolemodel/heroku/templates/deploy-app/SKILL.md +202 -0
  16. data/lib/generators/rolemodel/linters/eslint/eslint_generator.rb +1 -0
  17. data/lib/generators/rolemodel/linters/rubocop/rubocop_generator.rb +5 -1
  18. data/lib/generators/rolemodel/lograge/README.md +1 -1
  19. data/lib/generators/rolemodel/lograge/templates/config/initializers/lograge.rb +1 -1
  20. data/lib/generators/rolemodel/optics/base/base_generator.rb +3 -2
  21. data/lib/generators/rolemodel/react/react_generator.rb +1 -0
  22. data/lib/generators/rolemodel/readme/templates/README.md.erb +2 -4
  23. data/lib/generators/rolemodel/saas/devise/devise_generator.rb +3 -3
  24. data/lib/generators/rolemodel/sentry/README.md +25 -0
  25. data/lib/generators/rolemodel/sentry/USAGE +13 -0
  26. data/lib/generators/rolemodel/sentry/sentry_generator.rb +114 -0
  27. data/lib/generators/rolemodel/sentry/templates/app/controllers/concerns/sentry_user.rb +13 -0
  28. data/lib/generators/rolemodel/sentry/templates/app/javascript/initializers/sentry.js +34 -0
  29. data/lib/generators/rolemodel/sentry/templates/config/initializers/sentry.rb +68 -0
  30. data/lib/generators/rolemodel/simple_form/README.md +6 -0
  31. data/lib/generators/rolemodel/simple_form/USAGE +3 -0
  32. data/lib/generators/rolemodel/simple_form/simple_form_generator.rb +11 -0
  33. data/lib/generators/rolemodel/simple_form/templates/app/inputs/switch_checkbox_input.rb +1 -1
  34. data/lib/generators/rolemodel/slim/templates/app/views/application/_head.html.slim.tt +0 -4
  35. data/lib/generators/rolemodel/tailored_select/USAGE +2 -1
  36. data/lib/generators/rolemodel/tailored_select/tailored_select_generator.rb +15 -0
  37. data/lib/generators/rolemodel/testing/factory_bot/templates/support/factory_bot.rb +2 -0
  38. data/lib/generators/rolemodel/testing/jasmine_playwright/jasmine_playwright_generator.rb +2 -2
  39. data/lib/generators/rolemodel/testing/rspec/rspec_generator.rb +8 -10
  40. data/lib/generators/rolemodel/testing/rspec/templates/spec/rails_helper.rb.tt +4 -1
  41. data/lib/generators/rolemodel/testing/rspec/templates/spec/support/capybara_drivers.rb.tt +2 -0
  42. data/lib/generators/rolemodel/testing/rspec/templates/spec/support/helpers/action_cable_helper.rb +2 -0
  43. data/lib/generators/rolemodel/testing/rspec/templates/spec/support/helpers/test_element_helper.rb +2 -0
  44. data/lib/generators/rolemodel/testing/rspec/templates/spec/support/helpers.rb.tt +3 -1
  45. data/lib/generators/rolemodel/testing/vitest/vitest_generator.rb +1 -0
  46. data/lib/generators/rolemodel/turbo/all/README.md +16 -0
  47. data/lib/generators/rolemodel/turbo/all/USAGE +5 -0
  48. data/lib/generators/rolemodel/turbo/all/all_generator.rb +15 -0
  49. data/lib/generators/rolemodel/turbo/confirm/README.md +25 -0
  50. data/lib/generators/rolemodel/turbo/confirm/USAGE +5 -0
  51. data/lib/generators/rolemodel/turbo/confirm/confirm_generator.rb +35 -0
  52. data/lib/generators/rolemodel/turbo/form/README.md +8 -0
  53. data/lib/generators/rolemodel/turbo/form/USAGE +5 -0
  54. data/lib/generators/rolemodel/turbo/form/form_generator.rb +29 -0
  55. data/lib/generators/rolemodel/turbo/form/templates/app/javascript/controllers/turbo_form_controller.js +15 -0
  56. data/lib/generators/rolemodel/turbo/form/templates/spec/support/helpers/turbo_form_helper.rb +8 -0
  57. data/lib/generators/rolemodel/{ui_components → turbo}/modals/README.md +0 -2
  58. data/lib/generators/rolemodel/turbo/modals/USAGE +5 -0
  59. data/lib/generators/rolemodel/{ui_components → turbo}/modals/modals_generator.rb +9 -22
  60. data/lib/generators/rolemodel/{ui_components → turbo}/modals/templates/app/helpers/turbo_frame_link_helper.rb +2 -0
  61. data/lib/generators/rolemodel/turbo/ready/README.md +12 -0
  62. data/lib/generators/rolemodel/turbo/ready/USAGE +5 -0
  63. data/lib/generators/rolemodel/turbo/ready/ready_generator.rb +48 -0
  64. data/lib/generators/rolemodel/turbo/ready/templates/app/javascript/controllers/prevent_morph_controller.js +22 -0
  65. data/lib/generators/rolemodel/ui_components/navbar/navbar_generator.rb +11 -14
  66. data/lib/generators/rolemodel/webpack/README.md +2 -1
  67. data/lib/generators/rolemodel/webpack/templates/postcss.config.cjs +0 -3
  68. data/lib/generators/rolemodel/webpack/templates/webpack.config.js +6 -14
  69. data/lib/generators/rolemodel/webpack/webpack_generator.rb +18 -18
  70. data/lib/rolemodel/generator_base.rb +15 -0
  71. data/lib/rolemodel/version.rb +1 -1
  72. data/lib/rolemodel/yarn.rb +42 -0
  73. data/lib/rolemodel-rails.rb +0 -1
  74. metadata +58 -28
  75. data/lib/generators/rolemodel/linters/rubocop/templates/lib/cops/form_error_response.rb +0 -54
  76. data/lib/generators/rolemodel/linters/rubocop/templates/lib/cops/no_chrome_tag.rb +0 -45
  77. data/lib/generators/rolemodel/semaphore/README.md +0 -15
  78. data/lib/generators/rolemodel/semaphore/USAGE +0 -11
  79. data/lib/generators/rolemodel/semaphore/semaphore_generator.rb +0 -25
  80. data/lib/generators/rolemodel/semaphore/templates/heroku-deployment-commands.sh +0 -3
  81. data/lib/generators/rolemodel/semaphore/templates/production-deploy.yml.erb +0 -17
  82. data/lib/generators/rolemodel/semaphore/templates/semaphore.yml.erb +0 -145
  83. data/lib/generators/rolemodel/semaphore/templates/staging-deploy.yml.erb +0 -17
  84. data/lib/generators/rolemodel/ui_components/modals/USAGE +0 -8
  85. data/lib/generators/rolemodel/ui_components/modals/templates/app/javascript/controllers/morph_controller.js +0 -23
  86. data/lib/generators/rolemodel/webpack/templates/app/assets/stylesheets/application.scss +0 -5
  87. data/lib/generators/rolemodel/webpack/templates/app/javascript/initializers/honeybadger.js +0 -26
  88. /data/lib/generators/rolemodel/{simple_form → tailored_select}/templates/app/inputs/tailored_select_input.rb +0 -0
  89. /data/lib/generators/rolemodel/{ui_components/modals/templates/app/javascript/initializers/turbo_confirm.js → turbo/confirm/templates/app/javascript/initializers/turbo_confirm.js.tt} +0 -0
  90. /data/lib/generators/rolemodel/{ui_components/modals/templates/app/views/application/_confirm.html.slim → turbo/confirm/templates/app/views/application/_confirm.html.slim.tt} +0 -0
  91. /data/lib/generators/rolemodel/{ui_components → turbo}/modals/templates/app/javascript/controllers/toggle_controller.js +0 -0
  92. /data/lib/generators/rolemodel/{ui_components → turbo}/modals/templates/app/javascript/initializers/frame_missing_handler.js +0 -0
  93. /data/lib/generators/rolemodel/{ui_components → turbo}/modals/templates/app/views/layouts/modal.html.slim.tt +0 -0
  94. /data/lib/generators/rolemodel/{ui_components → turbo}/modals/templates/app/views/layouts/panel.html.slim.tt +0 -0
  95. /data/lib/generators/rolemodel/{ui_components/modals → turbo/ready}/templates/app/javascript/initializers/before_morph_handler.js +0 -0
  96. /data/lib/generators/rolemodel/ui_components/navbar/templates/app/assets/stylesheets/components/shoelace/{index.scss → index.css} +0 -0
  97. /data/lib/generators/rolemodel/ui_components/navbar/templates/app/views/{layouts → application}/_navbar.html.slim +0 -0
@@ -0,0 +1,5 @@
1
+ Description:
2
+ Enable Dynamic Form Interactions
3
+
4
+ Example:
5
+ rails generate rolemodel:turbo:form
@@ -0,0 +1,29 @@
1
+ module Rolemodel
2
+ module Turbo
3
+ class FormGenerator < GeneratorBase
4
+ source_root File.expand_path('templates', __dir__)
5
+
6
+ def add_rails_request_package
7
+ say 'Installing @rails/request.js package', :green
8
+
9
+ ensure_yarn
10
+ run 'yarn add @rails/request.js'
11
+ end
12
+
13
+ def add_stimulus_controller
14
+ say 'Adding Turbo Form Stimulus Controller', :green
15
+
16
+ directory 'app/javascript/controllers'
17
+
18
+ rails_command 'stimulus:manifest:update'
19
+ end
20
+
21
+ def add_system_spec_helper
22
+ say 'Adding Turbo Form System Spec Helper', :green
23
+
24
+ directory 'spec/support/helpers'
25
+ end
26
+ end
27
+ end
28
+ end
29
+
@@ -0,0 +1,15 @@
1
+ import { Controller } from '@hotwired/stimulus'
2
+ import { patch } from '@rails/request.js'
3
+
4
+ export default class extends Controller {
5
+ static values = { url: String, count: Number }
6
+
7
+ async perform({ params: { url: urlParam, query: queryParams } }) {
8
+ const body = new FormData(this.element)
9
+
10
+ if (queryParams) Object.keys(queryParams).forEach(key => body.append(key, queryParams[key]))
11
+
12
+ const response = await patch(urlParam || this.urlValue, { body, responseKind: 'turbo-stream' })
13
+ if (response.ok) this.countValue += 1
14
+ }
15
+ }
@@ -0,0 +1,8 @@
1
+ module TurboFormHelper
2
+ def expect_turbo_form_request
3
+ count_value = find("[data-controller='turbo-form']")['data-turbo-form-count-value'] || 0
4
+ yield
5
+ expect(page).to have_selector("[data-turbo-form-count-value='#{count_value.to_i + 1}']"),
6
+ 'Expected a successful Turbo-Form request, but none was detected'
7
+ end
8
+ end
@@ -9,10 +9,8 @@
9
9
 
10
10
  ## What you get
11
11
 
12
- * Custom confirm dialogs via `@rolemodel/turbo-confirm` integration
13
12
  * RoleModel Modal pattern integration. See Notes below for usage documentation.
14
13
  * RoleModel Panel pattern integration, if run with the `--panels` flag.
15
- * some generally useful, global Turbo before-morph handlers.
16
14
 
17
15
  > [!IMPORTANT]
18
16
  > This generator does not provide RoleModel Panel integration, unless run with the `--panels` flag.
@@ -0,0 +1,5 @@
1
+ Description:
2
+ Enable Turbo Frame Modals/Panels Pattern
3
+
4
+ Example:
5
+ rails generate rolemodel:turbo:modals
@@ -1,21 +1,13 @@
1
1
  module Rolemodel
2
- module UiComponents
3
- class ModalsGenerator < GeneratorBase
2
+ module Turbo
3
+ class ModalsGenerator < ::Rolemodel::GeneratorBase
4
4
  source_root File.expand_path('templates', __dir__)
5
5
  class_option :panels, type: :boolean, default: false, desc: 'Include RoleModel Panel Setup'
6
6
 
7
- def turbo_confirm
8
- say 'Installing Turbo Confirm package', :green
9
-
10
- run 'yarn add @rolemodel/turbo-confirm'
11
- end
12
-
13
7
  def helpers_and_views
14
8
  say 'generating views & helpers', :green
15
9
 
16
- copy_file 'app/helpers/turbo_frame_link_helper.rb'
17
-
18
- directory 'app/views/application'
10
+ directory 'app/helpers'
19
11
  directory 'app/views/layouts'
20
12
  end
21
13
 
@@ -26,9 +18,7 @@ module Rolemodel
26
18
  directory 'app/javascript/initializers'
27
19
 
28
20
  append_to_file 'app/javascript/application.js', <<~JS
29
- import './initializers/turbo_confirm.js'
30
21
  import './initializers/frame_missing_handler.js'
31
- import './initializers/before_morph_handler.js'
32
22
  JS
33
23
  end
34
24
 
@@ -38,7 +28,6 @@ module Rolemodel
38
28
  inject_into_file 'app/views/layouts/application.html.slim', after: /\bbody.*\n/ do
39
29
  optimize_indentation <<~SLIM, 4
40
30
  = turbo_frame_tag 'modal'
41
- = render 'confirm'
42
31
  SLIM
43
32
  end
44
33
  end
@@ -47,18 +36,15 @@ module Rolemodel
47
36
  if options.panels?
48
37
  say 'Setting Up RoleModel Panel', :green
49
38
 
50
- inject_into_file 'app/views/layouts/application.html.slim', after: /\bturbo_frame_tag 'modal'\n/ do
39
+ inject_into_file 'app/views/layouts/application.html.slim', after: /\bturbo_frame_tag 'modal'.*\n/ do
51
40
  optimize_indentation <<~SLIM, 4
52
41
  = turbo_frame_tag 'panel'
53
42
  SLIM
54
43
  end
55
44
 
56
- inject_into_file 'app/assets/stylesheets/application.scss',
57
- after: "@import '@rolemodel/optics/dist/css/optics';\n" do
58
- <<~SCSS
59
- @import '@rolemodel/optics/dist/css/addons/panel';
60
- SCSS
61
- end
45
+ append_to_file application_stylesheet_path, <<~CSS
46
+ @import '@rolemodel/optics/dist/css/addons/panel';
47
+ CSS
62
48
  else
63
49
  remove_file 'app/views/layouts/panel.html.slim'
64
50
  end
@@ -67,8 +53,9 @@ module Rolemodel
67
53
  def register_stimulus_controller
68
54
  say 'updating stimulus manifest', :green
69
55
 
70
- run 'rails stimulus:manifest:update'
56
+ rails_command 'stimulus:manifest:update'
71
57
  end
72
58
  end
73
59
  end
74
60
  end
61
+
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module TurboFrameLinkHelper
2
4
  def panel_link_to(...)
3
5
  link_to_frame('panel', ...)
@@ -0,0 +1,12 @@
1
+ # Ready Generator
2
+
3
+ This generator is designed to make your application "Turbo Ready".
4
+
5
+ That is, "ready" for modern Turbo Features such as "morphing", "scroll preservation" and meta tag injection via Turbo-Rails helpers.
6
+
7
+ ## What you get
8
+
9
+ - a "head" outlet in your "head" partial (expected by all Turbo-Rails helpers)
10
+ - morphing and scroll preservation meta tags
11
+ - generally useful event handlers.
12
+ - a stimulus controller for skipping specific attribute morphing
@@ -0,0 +1,5 @@
1
+ Description:
2
+ Prepare your application for modern Turbo Integration
3
+
4
+ Example:
5
+ rails generate rolemodel:turbo:ready
@@ -0,0 +1,48 @@
1
+ module Rolemodel
2
+ module Turbo
3
+ class ReadyGenerator < ::Rolemodel::GeneratorBase
4
+ source_root File.expand_path('templates', __dir__)
5
+
6
+ def add_javascript_initializer
7
+ say 'generating & importing morph event handlers', :green
8
+
9
+ directory 'app/javascript/initializers'
10
+
11
+ append_to_file 'app/javascript/application.js', <<~JS
12
+ import './initializers/before_morph_handler.js'
13
+ JS
14
+ end
15
+
16
+ def add_stimulus_controller
17
+ say 'adding prevent-morph controller & updating manifest', :green
18
+
19
+ directory 'app/javascript/controllers'
20
+
21
+ rails_command 'stimulus:manifest:update'
22
+ end
23
+
24
+ def inject_meta_tags
25
+ say 'adding meta tags to head partial', :green
26
+
27
+ inject_into_file 'app/views/application/_head.html.slim', after: /meta name="viewport".*\n/ do
28
+ optimize_indentation <<~SLIM
29
+ meta name="turbo-refresh-method" content="morph"
30
+ meta name="turbo-refresh-scroll" content="preserve"
31
+ SLIM
32
+ end
33
+ end
34
+
35
+ def inject_head_outlet
36
+ say 'adding head outlet to head partial', :green
37
+
38
+ append_to_file 'app/views/application/_head.html.slim' do
39
+ optimize_indentation <<~SLIM
40
+
41
+ = yield :head
42
+ SLIM
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+
@@ -0,0 +1,22 @@
1
+ import { Controller } from "@hotwired/stimulus"
2
+
3
+ export default class extends Controller {
4
+ static values = {
5
+ attributeName: String
6
+ }
7
+
8
+ /**
9
+ * Use this method to prevent morphing of a specific attribute on specific elements. This is useful for attributes that may have changed due to user interaction
10
+ * NOTE: The "open" attribute is preserved by default in 'app/javascript/initializers/before_morph_handler.js' and does not require this controller to be used.
11
+ *
12
+ * Example:
13
+ * <details controller="prevent-morph" data-prevent-morph-attribute-name-value="open" data-action="turbo:before-morph-attribute->prevent-morph#onNamedAttribute">
14
+ *
15
+ * @param {import("@hotwired/turbo").TurboBeforeMorphAttributeEvent} event
16
+ */
17
+ onNamedAttribute(event) {
18
+ if (event.detail.attributeName === this.attributeNameValue) {
19
+ event.preventDefault()
20
+ }
21
+ }
22
+ }
@@ -8,7 +8,7 @@ module Rolemodel
8
8
  def copy_navbar_files
9
9
  say 'Copying Navbar files', :green
10
10
 
11
- copy_file 'app/views/layouts/_navbar.html.slim'
11
+ directory 'app/views'
12
12
  end
13
13
 
14
14
  def insert_navbar_before_content
@@ -17,7 +17,7 @@ module Rolemodel
17
17
  insert_into_file 'app/views/layouts/application.html.slim', before: /\s+\.app__content/ do
18
18
  optimize_indentation <<~SLIM, 4
19
19
 
20
- = render 'layouts/navbar'
20
+ = render 'navbar'
21
21
  SLIM
22
22
  end
23
23
  end
@@ -25,29 +25,26 @@ module Rolemodel
25
25
  def install_shoelace
26
26
  say 'Installing Shoelace package', :green
27
27
 
28
+ ensure_yarn
28
29
  run 'yarn add @shoelace-style/shoelace'
29
30
  end
30
31
 
31
32
  def add_shoelace_javascript_imports
32
33
  say 'Copying Shoelace JS imports', :green
33
34
 
34
- copy_file 'app/javascript/lib/shoelace.js'
35
- append_to_file 'app/javascript/application.js' do
36
- <<~JS
37
- import './lib/shoelace.js'
38
- JS
39
- end
35
+ directory 'app/javascript/lib'
36
+ append_to_file 'app/javascript/application.js', <<~JS
37
+ import './lib/shoelace.js'
38
+ JS
40
39
  end
41
40
 
42
41
  def copy_shoelace_css_imports
43
42
  say 'Copying Shoelace CSS imports', :green
44
43
 
45
- copy_file 'app/assets/stylesheets/components/shoelace/index.scss'
46
- append_to_file 'app/assets/stylesheets/application.scss' do
47
- <<~SCSS
48
- @import 'components/shoelace/index.scss';
49
- SCSS
50
- end
44
+ copy_file 'app/assets/stylesheets/components/shoelace/index.css'
45
+ append_to_file application_stylesheet_path, <<~CSS
46
+ @import 'components/shoelace/index.css';
47
+ CSS
51
48
  end
52
49
  end
53
50
  end
@@ -5,4 +5,5 @@
5
5
  * Webpack v5
6
6
  * Uses `esbuild-loader` instead of Babel to transpile JS
7
7
  * Uses PostCSS to compile CSS and SCSS
8
- * Honeybadger error monitoring for JS. Run `bundle exec honeybadger install [YOUR API KEY HERE]` to setup Honeybadger for Ruby.
8
+
9
+ For error monitoring, run the `rolemodel:sentry` generator.
@@ -1,9 +1,6 @@
1
1
  module.exports = {
2
2
  syntax: require('postcss-scss'),
3
3
  plugins: [
4
- require('@csstools/postcss-sass')({
5
- includePaths: ['node_modules']
6
- }),
7
4
  require('postcss-preset-env')({
8
5
  autoprefixer: {
9
6
  flexbox: 'no-2009'
@@ -1,7 +1,5 @@
1
1
  import path from 'path'
2
2
  import webpack from 'webpack'
3
- import TerserPlugin from 'terser-webpack-plugin'
4
- import HoneybadgerSourceMapPlugin from '@honeybadger-io/webpack'
5
3
  import MiniCssExtractPlugin from 'mini-css-extract-plugin'
6
4
  import CssMinimizerPlugin from 'css-minimizer-webpack-plugin'
7
5
 
@@ -17,7 +15,7 @@ export default {
17
15
  entry: {
18
16
  application: [
19
17
  './app/javascript/application.js',
20
- './app/assets/stylesheets/application.scss'
18
+ './app/assets/stylesheets/application.css'
21
19
  ]
22
20
  },
23
21
  output: {
@@ -62,9 +60,11 @@ export default {
62
60
  {
63
61
  loader: 'css-loader',
64
62
  options: {
65
- url: false
63
+ url: false,
64
+ importLoaders: 1
66
65
  }
67
66
  },
67
+ 'strip-sourcemap-loader',
68
68
  'postcss-loader'
69
69
  ]
70
70
  }
@@ -73,7 +73,8 @@ export default {
73
73
  optimization: {
74
74
  minimize: mode === 'production',
75
75
  minimizer: [
76
- new TerserPlugin(),
76
+ // '...' keeps webpack 5's built-in JS minimizer (terser)
77
+ '...',
77
78
  new CssMinimizerPlugin()
78
79
  ]
79
80
  },
@@ -88,17 +89,8 @@ export default {
88
89
 
89
90
  // Replace ENV variables at build time
90
91
  new webpack.DefinePlugin({
91
- 'process.env.HONEYBADGER_API_KEY': JSON.stringify(process.env.HONEYBADGER_API_KEY),
92
- 'process.env.HONEYBADGER_ENV': JSON.stringify(process.env.HONEYBADGER_ENV),
93
92
  'process.env.RAILS_ENV': JSON.stringify(process.env.RAILS_ENV),
94
93
  'process.env.SOURCE_VERSION': JSON.stringify(process.env.SOURCE_VERSION)
95
- }),
96
-
97
- // Send source maps to HoneyBadger in production for easier debugging
98
- (mode === 'production' && !process.env.CI) && new HoneybadgerSourceMapPlugin({
99
- apiKey: process.env.HONEYBADGER_API_KEY,
100
- assetsUrl: process.env.ASSETS_URL,
101
- revision: process.env.SOURCE_VERSION
102
94
  })
103
95
  ].filter(Boolean)
104
96
  }
@@ -3,8 +3,6 @@ module Rolemodel
3
3
  source_root File.expand_path('templates', __dir__)
4
4
 
5
5
  DEV_DEPS = %w[
6
- @honeybadger-io/webpack
7
- @honeybadger-io/js
8
6
  esbuild
9
7
  esbuild-loader
10
8
  webpack
@@ -25,12 +23,30 @@ module Rolemodel
25
23
  mini-css-extract-plugin
26
24
  ]
27
25
 
26
+ def ensure_no_importmap
27
+ return unless Rails.root.join(destination_root, 'config/importmap.rb').exist?
28
+
29
+ remove_file 'config/importmap.rb'
30
+ bundle_command 'remove importmap-rails'
31
+ end
32
+
33
+ def ensure_jsbundling
34
+ bundle_command 'add jsbundling-rails'
35
+ run_bundle
36
+ end
37
+
28
38
  def ensure_node_version
29
39
  say "Establish development environment Node version of #{set_color(NODE_VERSION, :yellow)}", :green
30
40
 
31
41
  create_file '.node-version', NODE_VERSION, force: true
32
42
  end
33
43
 
44
+ def enable_corepack_and_yarn
45
+ say 'Enabling Corepack and pinning the project to Yarn 4+', :green
46
+
47
+ ensure_yarn
48
+ end
49
+
34
50
  def force_node_to_use_es_modules
35
51
  say 'Configuring project to use ES Modules instead of CommonJS', :green
36
52
 
@@ -50,22 +66,6 @@ module Rolemodel
50
66
  run "yarn add --dev #{dependencies.join(' ')}"
51
67
  end
52
68
 
53
- def honeybadger_setup
54
- say 'Setting up Honeybadger for JS error reporting', :green
55
-
56
- copy_file 'app/javascript/initializers/honeybadger.js'
57
- append_to_file 'app/javascript/application.js', <<~JS
58
- import './initializers/honeybadger'
59
- JS
60
- end
61
-
62
- def replace_css_entrypoint_with_scss
63
- say 'Replacing CSS entrypoint file with SCSS version', :green
64
-
65
- remove_file 'app/assets/stylesheets/application.css'
66
- copy_file 'app/assets/stylesheets/application.scss'
67
- end
68
-
69
69
  def add_webpack_config
70
70
  say 'Copying PostCSS & Webpack config files', :green
71
71
 
@@ -3,15 +3,30 @@
3
3
  require 'rails/generators'
4
4
  require 'rails/generators/bundle_helper'
5
5
  require_relative 'replace_content_helper'
6
+ require 'rolemodel/yarn'
6
7
 
7
8
  module Rolemodel
8
9
  class GeneratorBase < ::Rails::Generators::Base
9
10
  include ::Rails::Generators::BundleHelper, ReplaceContentHelper
10
11
 
12
+
11
13
  private
12
14
  # based on https://github.com/rails/rails/blob/main/railties/lib/rails/generators/app_base.rb#L713
13
15
  def run_bundle
14
16
  bundle_command("install --quiet", "BUNDLE_IGNORE_MESSAGES" => "1")
15
17
  end
18
+
19
+ # Enable Corepack and pin the project to Yarn 4+ before running any `yarn`
20
+ # command. Empty args/opts are required: without them, Thor forwards this
21
+ # generator's own CLI args (e.g. the test harness's --skip-bundle
22
+ # --skip-bootsnap) into Rolemodel::Yarn#setup, which takes none and raises
23
+ # Thor::InvocationError.
24
+ def ensure_yarn
25
+ invoke 'rolemodel:yarn:setup', [], {}
26
+ end
27
+
28
+ def application_stylesheet_path
29
+ Dir.glob('app/assets/stylesheets/application.*').first
30
+ end
16
31
  end
17
32
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Rolemodel
4
- VERSION = '2.1.0'
4
+ VERSION = '2.3.0'
5
5
  end
@@ -0,0 +1,42 @@
1
+ require 'thor'
2
+ require 'thor/actions'
3
+
4
+ module Rolemodel
5
+ class Yarn < ::Thor
6
+ include ::Thor::Actions
7
+
8
+ # Enable Corepack and pin the project to Yarn 4+ (instead of the classic
9
+ # Yarn 1.22). Idempotent, so any generator can call it before running a
10
+ # `yarn` command to guarantee the modern toolchain is in place.
11
+ desc 'setup', 'Set the current stable version of Yarn, enable corepack, and initialize a package.json file'
12
+ def setup
13
+ # Configure the node-modules linker so webpack, Playwright, and the Rails
14
+ # asset pipeline keep working (Yarn 4 defaults to Plug'n'Play otherwise).
15
+ # skip if exists? to avoid overwriting any other existing config
16
+ create_file '.yarnrc.yml', "nodeLinker: node-modules\n", skip: true
17
+
18
+ yes! 'yarn set version stable'
19
+ yes! 'corepack enable'
20
+ yes! 'yarn init'
21
+
22
+ ignore_yarn_install_state
23
+ end
24
+
25
+ private
26
+
27
+ def ignore_yarn_install_state
28
+ gitignore = File.expand_path('.gitignore', destination_root)
29
+ return create_file '.gitignore', "/.yarn/install-state.gz\n" unless File.exist?(gitignore)
30
+ return if File.read(gitignore).include?('/.yarn/install-state.gz')
31
+
32
+ append_to_file '.gitignore', "\n/.yarn/install-state.gz\n"
33
+ end
34
+
35
+ def yes!(command)
36
+ Open3.popen3(command) do |stdin, stdout, stderr, wait_thr|
37
+ stdin.puts 'y'
38
+ stdin.close
39
+ end
40
+ end
41
+ end
42
+ end
@@ -2,7 +2,6 @@
2
2
 
3
3
  module Rolemodel
4
4
  NODE_VERSION = '24.12.0'
5
- RUBY_VERSION = '4.0.1'
6
5
 
7
6
  GEM_LIB = File.expand_path(__dir__)
8
7
  end