jet_ui 0.2.6 → 0.2.8

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: efe57dd136891f58327f2aed1a96f910ff7abd5a8b84de101478840e9e3e038e
4
- data.tar.gz: 51f5c493690b7cac04d13dd8e8c9bc2ac0294c00c943ef593a528c4493471b4a
3
+ metadata.gz: 7d81819a27f5eda163c7f4dd9d8b12973ae46cc82932cd1f272b59f40e2bf55d
4
+ data.tar.gz: de6951399df6add4915be32c9d55f1cdc1a9b1be4e090039f4d055bed24f4987
5
5
  SHA512:
6
- metadata.gz: a4a32d30ee92783c2ab643cda03987f66cab7dba666afd8fdeb3fcea336c3835b6b9f61da4a2a423efcb89d600239a8e6dfd7f0b4479781e33593e78d9f0fa16
7
- data.tar.gz: bdd0a366d4f2bac9e35a9a622835e499e3000541a6b143f5c357029c697e7efd20b7a9b5379f5c085c476f33cad25d409b369bff93f17ac7e360cf2627e488f1
6
+ metadata.gz: 9259ec8446ed0c0c6d6f790ad00c27e9f24c93cd065a3d6bba7fe07a5068b079ea1e70de6e70b47d37a2e4ecf25c211bca85a3ec1c940d991215273710aba7f1
7
+ data.tar.gz: e2c5e84a989af0b3e935e4f39bb0cfa3c0653e235f5839fe23ee39736349b749cba938b20f3511ad620f09d65ceaff27e92638d5b3f8d1506cea6ab4e61649fc
data/CHANGELOG.md CHANGED
@@ -7,6 +7,29 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.2.8] - 2026-07-09
11
+
12
+ ### Added
13
+ - `@jetrockets/jet-ui` npm package for Vite/esbuild/Webpack users — import Stimulus controllers without vendoring
14
+ - `JsRuntime` module for auto-detecting JS bundler (Vite, esbuild, Webpack) vs importmap
15
+ - `jet_ui:install` generator now detects runtime and prints runtime-specific setup instructions
16
+
17
+ ### Fixed
18
+ - Importmap crash (`Errno::EISDIR`) — removed invalid directory path from `importmap.paths`
19
+
20
+ ## [0.2.7] - 2026-05-29
21
+
22
+ ### Added
23
+ - Generator tests for `jet_ui:eject` and `jet_ui:install`
24
+
25
+ ### Changed
26
+ - `jet_ui:eject --help` now lists all available components derived directly from `MANIFEST` — always accurate, never stale
27
+ - `jet_ui:install --help` updated with gem overview and pointer to `jet_ui:eject --help`
28
+ - Upgrade development Ruby version to 4.0.5
29
+
30
+ ### Removed
31
+ - Stale `USAGE` files for `eject` and `install` generators — superseded by the class-level `desc`
32
+
10
33
  ## [0.2.6] - 2026-05-13
11
34
 
12
35
  ### Changed
data/README.md CHANGED
@@ -37,6 +37,36 @@ The generator:
37
37
 
38
38
  When the gem is updated, both CSS and JS are picked up automatically — no further changes needed. Safe to re-run after upgrades.
39
39
 
40
+ ### Alternative: npm package (Vite)
41
+
42
+ The above (importmap) setup needs no npm install — the gem ships the controllers itself. If your app bundles JavaScript with Vite instead, run the install generator: it detects Vite, **installs the `@jetrockets/jet_ui` npm package** for you (running `yarn add @jetrockets/jet_ui`, or the npm/pnpm/bun equivalent it detects), and prints the wiring steps:
43
+
44
+ ```bash
45
+ rails generate jet_ui:install
46
+ # detects Vite → runs: yarn add @jetrockets/jet_ui
47
+ ```
48
+
49
+ Then register the controllers you use (`@hotwired/stimulus` is a peer dependency):
50
+
51
+ ```javascript
52
+ import { ModalController } from "@jetrockets/jet_ui"
53
+
54
+ application.register("modal", ModalController)
55
+ ```
56
+
57
+ Import the component styles in your Tailwind/CSS entry point:
58
+
59
+ ```css
60
+ @import "@jetrockets/jet_ui/css";
61
+ ```
62
+
63
+ Or, if you'd rather manage styles through the bundler than the Rails asset pipeline, import the CSS straight from JavaScript:
64
+
65
+ ```javascript
66
+ import "@jetrockets/jet_ui/css" // all component styles
67
+ import "@jetrockets/jet_ui/css/btn.css" // a single component
68
+ ```
69
+
40
70
  ## Usage
41
71
 
42
72
  The `jet_ui` helper is available in all views:
@@ -9,35 +9,6 @@ module JetUi
9
9
  [File.expand_path('../../../..', __dir__)]
10
10
  end
11
11
 
12
- desc <<~DESC
13
- Copies one or more JetUi component source files into your application
14
- so they can be customised locally. Ejected files take precedence over
15
- the gem's built-in versions automatically — no extra configuration needed.
16
-
17
- Available components: #{%w[accordion btn card clipboard divider drawer dropdown empty flash header icon list modal navbar pagy popover sidebar spinner stat stepper table tabs timeline tooltip turbo_confirm].join(', ')}
18
-
19
- Examples:
20
- rails generate jet_ui:eject btn
21
- rails generate jet_ui:eject flash
22
- rails generate jet_ui:eject modal drawer
23
- rails generate jet_ui:eject btn --skip-test
24
- rails generate jet_ui:eject btn --skip-preview
25
- rails generate jet_ui:eject flash --skip-javascript
26
- rails generate jet_ui:eject btn --skip-test --skip-preview
27
- DESC
28
-
29
- argument :components, type: :array, banner: 'component [component ...]',
30
- desc: 'One or more component names to eject (e.g. btn card)'
31
-
32
- class_option :skip_test, type: :boolean, default: false,
33
- desc: 'Skip ejecting the component test file'
34
-
35
- class_option :skip_preview, type: :boolean, default: false,
36
- desc: 'Skip ejecting the ViewComponent preview file'
37
-
38
- class_option :skip_javascript, type: :boolean, default: false,
39
- desc: 'Skip ejecting the Stimulus controller JS file (for components that have one)'
40
-
41
12
  MANIFEST = {
42
13
  'btn' => {
43
14
  files: [
@@ -338,11 +309,41 @@ module JetUi
338
309
  }
339
310
  }.freeze
340
311
 
312
+ desc <<~DESC
313
+ Copies one or more JetUi component source files into your application
314
+ so they can be customised locally. Ejected files take precedence over
315
+ the gem's built-in versions automatically — no extra configuration needed.
316
+
317
+ Available components (#{MANIFEST.size}):
318
+ #{MANIFEST.keys.sort.join(', ')}
319
+
320
+ Examples:
321
+ rails generate jet_ui:eject btn
322
+ rails generate jet_ui:eject flash
323
+ rails generate jet_ui:eject modal drawer
324
+ rails generate jet_ui:eject btn --skip-test
325
+ rails generate jet_ui:eject btn --skip-preview
326
+ rails generate jet_ui:eject flash --skip-javascript
327
+ rails generate jet_ui:eject btn --skip-test --skip-preview
328
+ DESC
329
+
330
+ argument :components, type: :array, banner: 'component [component ...]',
331
+ desc: 'One or more component names to eject (e.g. btn card)'
332
+
333
+ class_option :skip_test, type: :boolean, default: false,
334
+ desc: 'Skip ejecting the component test file'
335
+
336
+ class_option :skip_preview, type: :boolean, default: false,
337
+ desc: 'Skip ejecting the ViewComponent preview file'
338
+
339
+ class_option :skip_javascript, type: :boolean, default: false,
340
+ desc: 'Skip ejecting the Stimulus controller JS file (for components that have one)'
341
+
341
342
  def eject_components
342
343
  unknown = components.map(&:downcase) - MANIFEST.keys
343
344
  if unknown.any?
344
345
  say "\nUnknown component(s): #{unknown.join(', ')}", :red
345
- say "Available: #{MANIFEST.keys.join(', ')}\n", :red
346
+ say "Available: #{MANIFEST.keys.join(', ')}", :red
346
347
  exit 1
347
348
  end
348
349
 
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'rails/generators'
4
+ require_relative '../eject/eject_generator'
5
+ require_relative '../js_runtime'
4
6
 
5
7
  module JetUi
6
8
  module Generators
@@ -11,63 +13,52 @@ module JetUi
11
13
  class InstallGenerator < Rails::Generators::Base
12
14
  source_root File.expand_path('templates', __dir__)
13
15
 
16
+ class_option :skip_install, type: :boolean, default: false,
17
+ desc: 'Vite apps: skip running the package manager install, just print the command'
18
+
14
19
  desc <<~DESC
15
- Sets up JetUi in your Rails application.
20
+ JetUi is a ViewComponent-based UI library for Rails — #{EjectGenerator::MANIFEST.size} ready-made
21
+ components styled with Tailwind CSS v4, matching the design system at
22
+ ui.jetrockets.com.
23
+
24
+ This generator detects how your app manages JavaScript and sets JetUi up
25
+ accordingly — no flags needed:
16
26
 
17
- 1. CSSDetects your Tailwind source file and injects a single @import
18
- that covers all JetUi component stylesheets. New components added in
19
- future gem updates are picked up automatically on the next CSS build.
27
+ Importmap apps injects the gem's CSS @import into your Tailwind source
28
+ and adds eagerLoadControllersFrom("jet_ui", application) to your Stimulus
29
+ controllers index. Controllers are auto-registered, no npm needed.
20
30
 
21
- 2. JSAdds eagerLoadControllersFrom("jet_ui", application) to your
22
- Stimulus controllers index. All current and future JetUi Stimulus
23
- controllers (flash, modal, etc.) are registered automatically when
24
- you update the gem — no further changes needed.
31
+ Vite apps installs the npm package with your package manager and
32
+ prints the controller registration and stylesheet @import. Vite apps own
33
+ their JS/CSS entry points, so those are not modified automatically.
25
34
 
26
- Safe to run multiple times — already-configured steps are skipped.
35
+ Safe to run multiple times — already-configured importmap steps are skipped.
27
36
 
28
- Supported Tailwind source file locations:
37
+ Importmap Tailwind source files detected for the CSS @import:
29
38
  app/assets/tailwind/application.css
30
39
  app/assets/stylesheets/application.tailwind.css
31
40
  app/assets/stylesheets/application.postcss.css
32
41
 
42
+ To see all available components with descriptions:
43
+ rails generate jet_ui:eject --help
44
+
33
45
  Example:
34
46
  rails generate jet_ui:install
35
47
  DESC
36
48
 
37
- def inject_css
38
- if (file = tailwind_source_file)
39
- content = File.read(File.join(destination_root, file))
40
- if content.include?('jet_ui')
41
- say " JetUi CSS already imported in #{file}", :yellow
42
- else
43
- # Tailwind CSS v4: import each file with an absolute path so the
44
- # Tailwind CLI can process @apply directives at build time.
45
- append_to_file file, tailwind_imports
46
- say " Injected JetUi imports into #{file}", :green
47
- end
48
- else
49
- say ' Could not detect a Tailwind CSS source file.', :yellow
50
- say ' Add the following to your Tailwind CSS source manually:', :yellow
51
- say tailwind_imports, :yellow
49
+ def show_runtime
50
+ case js_runtime
51
+ when :importmap then say ' Detected importmap — wiring JetUi via the gem.', :green
52
+ when :vite then say ' Detected Vite — wiring JetUi via the npm package.', :green
53
+ else say ' Could not detect importmap or Vite — printing both setups.', :yellow
52
54
  end
53
55
  end
54
56
 
55
- def inject_js
56
- path = File.join(destination_root, JS_CONTROLLERS_FILE)
57
- unless File.exist?(path)
58
- say ' No Stimulus controllers index found — skipping JS setup.', :yellow
59
- say ' If you use Stimulus, add this line to your controllers index manually:', :yellow
60
- say %( eagerLoadControllersFrom("jet_ui", application)), :yellow
61
- return
62
- end
63
-
64
- if File.read(path).include?('jet_ui')
65
- say " JetUi controllers already registered in #{JS_CONTROLLERS_FILE}", :yellow
66
- else
67
- insert_into_file JS_CONTROLLERS_FILE, after: /eagerLoadControllersFrom\("controllers".*\n/ do
68
- %(eagerLoadControllersFrom("jet_ui", application)\n)
69
- end
70
- say " Registered JetUi controllers in #{JS_CONTROLLERS_FILE}", :green
57
+ def setup
58
+ case js_runtime
59
+ when :importmap then setup_importmap
60
+ when :vite then setup_vite
61
+ else setup_unknown
71
62
  end
72
63
  end
73
64
 
@@ -89,6 +80,8 @@ module JetUi
89
80
  say " rails generate jet_ui:eject btn card\n"
90
81
  end
91
82
 
83
+ NPM_PACKAGE = '@jetrockets/jet_ui'
84
+
92
85
  JS_CONTROLLERS_FILE = 'app/javascript/controllers/index.js'
93
86
 
94
87
  TAILWIND_SOURCE_FILES = %w[
@@ -99,10 +92,94 @@ module JetUi
99
92
 
100
93
  private
101
94
 
95
+ def js_runtime
96
+ @js_runtime ||= JsRuntime.detect(destination_root)
97
+ end
98
+
99
+ # --- importmap: auto-wire from the gem -------------------------------
100
+
101
+ def setup_importmap
102
+ inject_importmap_css
103
+ inject_importmap_controllers
104
+ end
105
+
106
+ def inject_importmap_css
107
+ file = tailwind_source_file
108
+ unless file
109
+ say ' Could not detect a Tailwind CSS source file.', :yellow
110
+ say ' Add the following to your Tailwind CSS source manually:', :yellow
111
+ say gem_css_import, :yellow
112
+ return
113
+ end
114
+
115
+ if File.read(File.join(destination_root, file)).include?('jet_ui')
116
+ say " JetUi CSS already imported in #{file}", :yellow
117
+ else
118
+ # Tailwind CSS v4: import the gem's stylesheet by absolute path so the
119
+ # Tailwind CLI can process its @apply directives at build time.
120
+ append_to_file file, gem_css_import
121
+ say " Injected JetUi CSS into #{file}", :green
122
+ end
123
+ end
124
+
125
+ def inject_importmap_controllers
126
+ path = File.join(destination_root, JS_CONTROLLERS_FILE)
127
+ unless File.exist?(path)
128
+ say ' No Stimulus controllers index found — add this line to it manually:', :yellow
129
+ say %( eagerLoadControllersFrom("jet_ui", application)), :yellow
130
+ return
131
+ end
132
+
133
+ if File.read(path).include?('jet_ui')
134
+ say " JetUi controllers already registered in #{JS_CONTROLLERS_FILE}", :yellow
135
+ else
136
+ insert_into_file JS_CONTROLLERS_FILE, after: /eagerLoadControllersFrom\("controllers".*\n/ do
137
+ %(eagerLoadControllersFrom("jet_ui", application)\n)
138
+ end
139
+ say " Registered JetUi controllers in #{JS_CONTROLLERS_FILE}", :green
140
+ end
141
+ end
142
+
143
+ # --- vite: install the package, but never touch the app's entry points ---
144
+
145
+ def setup_vite
146
+ install_npm_package
147
+ say ' Ensure @hotwired/stimulus is installed — it is a peer dependency the controllers import.', :yellow
148
+ say ' Register the controllers you use, in your Stimulus controllers index:'
149
+ say %( import { ModalController, DrawerController } from "#{NPM_PACKAGE}")
150
+ say %( application.register("modal", ModalController))
151
+ say %( application.register("drawer", DrawerController))
152
+ say ' Import the styles, in your Tailwind/CSS entry point:'
153
+ say %( @import "#{NPM_PACKAGE}/css";)
154
+ end
155
+
156
+ def install_npm_package
157
+ command = JsRuntime.install_command(destination_root, NPM_PACKAGE)
158
+ if options[:skip_install]
159
+ say ' Skipped install — run it yourself:', :yellow
160
+ say " #{command}"
161
+ else
162
+ say " Installing #{NPM_PACKAGE}…", :green
163
+ run command
164
+ end
165
+ end
166
+
167
+ def setup_unknown
168
+ say ' Importmap apps — add to your Stimulus controllers index:', :yellow
169
+ say %( eagerLoadControllersFrom("jet_ui", application))
170
+ say ' Vite apps — install the npm package and import its styles:', :yellow
171
+ say " #{JsRuntime.install_command(destination_root, NPM_PACKAGE)}"
172
+ say %( @import "#{NPM_PACKAGE}/css";)
173
+ end
174
+
102
175
  def tailwind_source_file
103
176
  TAILWIND_SOURCE_FILES.find { |f| File.exist?(File.join(destination_root, f)) }
104
177
  end
105
178
 
179
+ def gem_css_import
180
+ %(\n@import "#{File.join(gem_stylesheets_path, 'jet_ui.css')}";\n)
181
+ end
182
+
106
183
  def gem_stylesheets_path
107
184
  spec = begin
108
185
  Gem::Specification.find_by_name('jet_ui')
@@ -112,10 +189,6 @@ module JetUi
112
189
  base = spec&.gem_dir || File.expand_path('../../../../..', __dir__)
113
190
  File.join(base, 'app/assets/stylesheets')
114
191
  end
115
-
116
- def tailwind_imports
117
- %(\n@import "#{File.join(gem_stylesheets_path, 'jet_ui.css')}";\n)
118
- end
119
192
  end
120
193
  end
121
194
  end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+
5
+ module JetUi
6
+ module Generators
7
+ # Detects how a host application manages its JavaScript, so the install
8
+ # generator can wire JetUi the right way without asking: pin controllers via
9
+ # importmap, or point the app at the npm package for Vite.
10
+ module JsRuntime
11
+ VITE_CONFIGS = %w[
12
+ vite.config.js vite.config.mjs vite.config.ts
13
+ config/vite.json
14
+ ].freeze
15
+
16
+ # Ordered: the first lockfile found wins.
17
+ PACKAGE_MANAGERS = {
18
+ 'bun.lockb' => :bun,
19
+ 'pnpm-lock.yaml' => :pnpm,
20
+ 'yarn.lock' => :yarn,
21
+ 'package-lock.json' => :npm
22
+ }.freeze
23
+
24
+ module_function
25
+
26
+ # Vite wins when both are present: a working Vite config is a stronger
27
+ # signal than a leftover config/importmap.rb.
28
+ def detect(root)
29
+ return :vite if vite?(root)
30
+ return :importmap if importmap?(root)
31
+
32
+ :unknown
33
+ end
34
+
35
+ def vite?(root)
36
+ VITE_CONFIGS.any? { |file| File.exist?(File.join(root, file)) } ||
37
+ package_json_vite?(root)
38
+ end
39
+
40
+ def importmap?(root)
41
+ File.exist?(File.join(root, 'config/importmap.rb'))
42
+ end
43
+
44
+ def package_manager(root)
45
+ PACKAGE_MANAGERS.each { |lock, name| return name if File.exist?(File.join(root, lock)) }
46
+ package_manager_field(root) || :npm
47
+ end
48
+
49
+ # corepack's `"packageManager": "yarn@4.5.0"` — the declared manager when
50
+ # there's no lockfile yet (e.g. a fresh vite_rails clone).
51
+ def package_manager_field(root)
52
+ path = File.join(root, 'package.json')
53
+ return unless File.exist?(path)
54
+
55
+ field = JSON.parse(File.read(path))['packageManager']
56
+ return unless field
57
+
58
+ PACKAGE_MANAGERS.values.find { |name| name.to_s == field.split('@').first }
59
+ rescue JSON::ParserError
60
+ nil
61
+ end
62
+
63
+ def install_command(root, package)
64
+ manager = package_manager(root)
65
+ verb = manager == :npm ? 'install' : 'add'
66
+
67
+ "#{manager} #{verb} #{package}"
68
+ end
69
+
70
+ def package_json_vite?(root)
71
+ path = File.join(root, 'package.json')
72
+ return false unless File.exist?(path)
73
+
74
+ deps = JSON.parse(File.read(path)).values_at('dependencies', 'devDependencies').compact
75
+ deps.flat_map(&:keys).include?('vite')
76
+ rescue JSON::ParserError
77
+ false
78
+ end
79
+ end
80
+ end
81
+ end
data/lib/jet_ui/engine.rb CHANGED
@@ -17,7 +17,6 @@ module JetUi
17
17
  root.join('app/assets/javascripts/jet_ui'),
18
18
  under: 'jet_ui'
19
19
  )
20
- app.config.importmap.paths << root.join('app/assets/javascripts')
21
20
  app.config.importmap.cache_sweepers << root.join('app/assets/javascripts')
22
21
  end
23
22
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module JetUi
4
- VERSION = '0.2.6'
4
+ VERSION = '0.2.8'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jet_ui
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.6
4
+ version: 0.2.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - JetRockets
@@ -633,10 +633,9 @@ files:
633
633
  - app/components/jet_ui/turbo_confirm/component.html.erb
634
634
  - app/components/jet_ui/turbo_confirm/component.rb
635
635
  - app/helpers/jet_ui_helper.rb
636
- - lib/generators/jet_ui/eject/USAGE
637
636
  - lib/generators/jet_ui/eject/eject_generator.rb
638
- - lib/generators/jet_ui/install/USAGE
639
637
  - lib/generators/jet_ui/install/install_generator.rb
638
+ - lib/generators/jet_ui/js_runtime.rb
640
639
  - lib/jet_ui.rb
641
640
  - lib/jet_ui/engine.rb
642
641
  - lib/jet_ui/version.rb
@@ -1,20 +0,0 @@
1
- Description:
2
- Ejects one or more JetUi components into your application so they can
3
- be customised locally. The ejected files take precedence over the gem's
4
- built-in versions, similar to how `rails generate devise:views` works.
5
-
6
- For components that include a Stimulus controller (e.g. flash), the JS
7
- file is also ejected alongside the Ruby and CSS files.
8
-
9
- Available components:
10
- btn, card, icon, spinner, avatar, breadcrumbs, tabs, empty, list,
11
- divider, timeline, stepper, table, pagy, flash
12
-
13
- Examples:
14
- rails generate jet_ui:eject btn
15
- rails generate jet_ui:eject flash
16
- rails generate jet_ui:eject btn card flash
17
- rails generate jet_ui:eject btn --skip-test
18
- rails generate jet_ui:eject btn --skip-preview
19
- rails generate jet_ui:eject flash --skip-javascript
20
- rails generate jet_ui:eject btn --skip-test --skip-preview
@@ -1,11 +0,0 @@
1
- Description:
2
- Sets up JetUi in your Rails application.
3
-
4
- - Injects the JetUi CSS import into your Tailwind entrypoint
5
- - Registers JetUi Stimulus controllers via eagerLoadControllersFrom
6
- (requires @hotwired/stimulus-loading, included in default Rails apps)
7
-
8
- Safe to run multiple times — already-configured steps are skipped.
9
-
10
- Example:
11
- rails generate jet_ui:install