hanami-cli 2.1.0.rc1 → 2.1.0.rc3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '054178514bf7e420e00cd8da8a148cd4d479609668c180507fadbcd7a086f15d'
4
- data.tar.gz: 3ef8c388ad8c8734179ce042d3eb966dea6df881b72e7615f0b104a1890106a5
3
+ metadata.gz: 7e96c42bbcc6493a575f9ba2c716e7129259e4364dedd4a7d1757a239eea224e
4
+ data.tar.gz: 51f4237d382a25f7d35547b578434889ef16bd365a411413033b13301eed91d2
5
5
  SHA512:
6
- metadata.gz: c8e1abc0ded22d56ee2acd5dec6d9f7079c0331d187b1d4ff4b26858faf50ff870016e1adfa6b785673d39b6d0dcf097dca7c1b0526da442e1939ce0952f3799
7
- data.tar.gz: 8ecc3ceefc10856cbd2f60ec24bd1ecde442764610dbb7d179f064080ac434fa4829374efb05513c67420ab8b76b0280ad594fd566824fdfba0e8d14afc209fb
6
+ metadata.gz: fa474b08d69848674a29519a8fe5752ec2ac971f002e43a866c24db9d6f7293a0e0ff57ac160d73d0866107dd84bc39ba523be51d59a2e2a4cf04b4a2f293331
7
+ data.tar.gz: 4c1a7da944c8159bc99eb550b07661455c60a2e5fea933ead880e6deb0d1fdf459b8c68d526eededf3305a0105a5db06854d67fc8a357086e7c6a592f869a688
data/.rubocop.yml CHANGED
@@ -13,6 +13,8 @@ Lint/DuplicateBranch:
13
13
  Lint/EmptyFile:
14
14
  Exclude:
15
15
  - 'spec/fixtures/**/*.rb'
16
+ Lint/NonLocalExitFromIterator:
17
+ Enabled: false
16
18
  Naming/HeredocDelimiterNaming:
17
19
  Enabled: false
18
20
  Naming/MethodParameterName:
data/CHANGELOG.md CHANGED
@@ -2,6 +2,39 @@
2
2
 
3
3
  Hanami Command Line Interface
4
4
 
5
+ ## v2.1.0.rc3 - 2024-02-16
6
+
7
+ ### Changed
8
+
9
+ - [Tim Riley] For `hanami assets` commands, run a separate assets compilation process per slice (in parallel).
10
+ - [Tim Riley] Generate compiled assets into separate folders per slice, each with its own `assets.js` manifest file: `public/assets/` for the app, and `public/assets/[slice_name]/` for each slice.
11
+ - [Tim Riley] For `hanami assets` commands, directly detect and invoke the `config/assets.js` files. Look for this file within each slice, and fall back to the app-level file.
12
+ - [Tim Riley] Do not generate `"scripts": {"assets": "..."}` section in new app's `package.json`.
13
+ - [Tim Riley] Subclasses of `Hanami::CLI::Command` receive default args to their `#initialize` methods, and do not need to re-declare default args themselves.
14
+ - [Philip Arndt] Alphabetically sort hanami gems in the new app `Gemfile`.
15
+
16
+ ### Fixed
17
+
18
+ - [Nishiki (錦華)] Strip invalid characters from module name when generating new app.
19
+
20
+ ## v2.1.0.rc2 - 2023-11-08
21
+
22
+ ### Added
23
+
24
+ - [Tim Riley] Add `--skip-tests` for `hanami generate` commands. This CLI option will skip tests generation.
25
+
26
+ ### Changed
27
+
28
+ - [Tim Riley] Set `"type": "module"` in package.json, enabling ES modules by default
29
+ - [Tim Riley] Rename `config/assets.mjs` to `config/assets.js` (use a plain `.js` file extension)
30
+
31
+ ### Fixed
32
+
33
+ - [Tim Riley] Use correct helper names in generated app layout
34
+ - [Luca Guidi] Ensure to generate apps with correct pre-release version of `hanami-assets` NPM package
35
+ - [Sean Collins] Print to stderr NPM installation errors when running `hanami install`
36
+ - [Sean Collins] Ensure to install missing gems after `hanami install` is ran
37
+
5
38
  ## v2.1.0.rc1 - 2023-11-01
6
39
 
7
40
  ### Added
data/Gemfile CHANGED
@@ -19,6 +19,8 @@ gem "dry-files", github: "dry-rb/dry-files", branch: "main"
19
19
 
20
20
  gem "rack"
21
21
 
22
+ gem "hanami-devtools", github: "hanami/devtools", branch: "main"
23
+
22
24
  group :test do
23
25
  gem "pry"
24
26
  end
@@ -13,18 +13,40 @@ module Hanami
13
13
  class Command < Dry::CLI::Command
14
14
  # Returns a new command.
15
15
  #
16
- # This method does not need to be called directly when creating comments for the CLI. Commands
16
+ # Provides default values so they can be available to any subclasses defining their own
17
+ # {#initialize} methods.
18
+ #
19
+ # @see #initialize
20
+ #
21
+ # @since 2.1.0
22
+ # @api public
23
+ def self.new(
24
+ out: $stdout,
25
+ err: $stderr,
26
+ fs: Hanami::CLI::Files.new,
27
+ inflector: Dry::Inflector.new,
28
+ **opts
29
+ )
30
+ super(out: out, err: err, fs: fs, inflector: inflector, **opts)
31
+ end
32
+
33
+ # Returns a new command.
34
+ #
35
+ # This method does not need to be called directly when creating commands for the CLI. Commands
17
36
  # are registered as classes, and the CLI framework will initialize the command when needed.
18
- # This means that all parameters for `#initialize` should also be given default arguments.
37
+ # This means that all parameters for `#initialize` should also be given default arguments. See
38
+ # {.new} for the standard default arguments for all commands.
19
39
  #
20
40
  # @param out [IO] I/O stream for standard command output
21
41
  # @param err [IO] I/O stream for comment errror output
22
42
  # @param fs [Hanami::CLI::Files] object for managing file system interactions
23
43
  # @param inflector [Dry::Inflector] inflector for any command-level inflections
24
44
  #
45
+ # @see .new
46
+ #
25
47
  # @since 2.0.0
26
48
  # @api public
27
- def initialize(out: $stdout, err: $stderr, fs: Hanami::CLI::Files.new, inflector: Dry::Inflector.new)
49
+ def initialize(out:, err:, fs:, inflector:)
28
50
  super()
29
51
  @out = out
30
52
  @err = err
@@ -2,28 +2,71 @@
2
2
 
3
3
  require "shellwords"
4
4
  require_relative "../command"
5
- require_relative "../../../system_call"
5
+ require_relative "../../../interactive_system_call"
6
6
 
7
7
  module Hanami
8
8
  module CLI
9
9
  module Commands
10
10
  module App
11
11
  module Assets
12
+ # Base class for assets commands.
13
+ #
14
+ # Finds slices with assets present (anything in an `assets/` dir), then forks a child
15
+ # process for each slice to run the assets command (`config/assets.js`) for the slice.
16
+ #
17
+ # Prefers the slice's own `config/assets.js` if present, otherwise falls back to the
18
+ # app-level file.
19
+ #
20
+ # Passes `--path` and `--dest` arguments to this command to compile assets for the given
21
+ # slice only and save them into a dedicated directory (`public/assets/` for the app,
22
+ # `public/[slice_name]/` for slices).
23
+ #
24
+ # @see Watch
25
+ # @see Compile
26
+ #
12
27
  # @since 2.1.0
13
28
  # @api private
14
29
  class Command < App::Command
15
- def initialize(config: app.config.assets, system_call: SystemCall.new, **)
16
- super()
17
- @system_call = system_call
30
+ # @since 2.1.0
31
+ # @api private
32
+ def initialize(
33
+ out:, err:,
34
+ config: app.config.assets,
35
+ system_call: InteractiveSystemCall.new(out: out, err: err, exit_after: false),
36
+ **opts
37
+ )
38
+ super(out: out, err: err, **opts)
39
+
18
40
  @config = config
41
+ @system_call = system_call
19
42
  end
20
43
 
21
44
  # @since 2.1.0
22
45
  # @api private
23
46
  def call(**)
24
- cmd, *args = cmd_with_args
47
+ slices = slices_with_assets
48
+
49
+ if slices.empty?
50
+ out.puts "No assets found."
51
+ return
52
+ end
25
53
 
26
- system_call.call(cmd, *args)
54
+ slices.each do |slice|
55
+ unless assets_config(slice)
56
+ out.puts "No assets config found for #{slice}. Please create a config/assets.js."
57
+ return
58
+ end
59
+ end
60
+
61
+ pids = slices_with_assets.map { |slice| fork_child_assets_command(slice) }
62
+
63
+ Signal.trap("INT") do
64
+ pids.each do |pid|
65
+ Process.kill(sig, pid)
66
+ end
67
+ end
68
+
69
+ Process.waitall
27
70
  end
28
71
 
29
72
  private
@@ -38,22 +81,65 @@ module Hanami
38
81
 
39
82
  # @since 2.1.0
40
83
  # @api private
41
- def cmd_with_args
42
- [config.package_manager_run_command, "assets"]
84
+ def fork_child_assets_command(slice)
85
+ Process.fork do
86
+ cmd, *args = assets_command(slice)
87
+ system_call.call(cmd, *args, out_prefix: "[#{slice.slice_name}] ")
88
+ rescue Interrupt
89
+ # When this has been interrupted (by the Signal.trap handler in #call), catch the
90
+ # interrupt and exit cleanly, without showing the default full backtrace.
91
+ end
43
92
  end
44
93
 
45
94
  # @since 2.1.0
46
95
  # @api private
47
- def entry_points
48
- config.entry_points.map do |entry_point|
49
- escape(entry_point)
50
- end.join(" ")
96
+ def assets_command(slice)
97
+ cmd = [config.node_command, assets_config(slice).to_s, "--"]
98
+
99
+ if slice.eql?(slice.app)
100
+ cmd << "--path=app"
101
+ cmd << "--dest=public/assets"
102
+ else
103
+ cmd << "--path=#{slice.root.relative_path_from(slice.app.root)}"
104
+ cmd << "--dest=public/assets/#{slice.slice_name}"
105
+ end
106
+
107
+ cmd
51
108
  end
52
109
 
53
110
  # @since 2.1.0
54
111
  # @api private
55
- def destination
56
- escape(config.destination)
112
+ def slices_with_assets
113
+ slices = app.slices.with_nested + [app]
114
+ slices.select { |slice| slice_assets?(slice) }
115
+ end
116
+
117
+ # @since 2.1.0
118
+ # @api private
119
+ def slice_assets?(slice)
120
+ assets_path =
121
+ if slice.app.eql?(slice)
122
+ slice.root.join("app", "assets")
123
+ else
124
+ slice.root.join("assets")
125
+ end
126
+
127
+ assets_path.directory?
128
+ end
129
+
130
+ # Returns the path to the assets config (`config/assets.js`) for the given slice.
131
+ #
132
+ # Prefers a config file local to the slice, otherwise falls back to app-level config.
133
+ # Returns nil if no config can be found.
134
+ #
135
+ # @since 2.1.0
136
+ # @api private
137
+ def assets_config(slice)
138
+ config = slice.root.join("config", "assets.js")
139
+ return config if config.exist?
140
+
141
+ config = slice.app.root.join("config", "assets.js")
142
+ config if config.exist?
57
143
  end
58
144
 
59
145
  # @since 2.1.0
@@ -7,22 +7,25 @@ module Hanami
7
7
  module Commands
8
8
  module App
9
9
  module Assets
10
+ # Compiles assets for each slice.
11
+ #
10
12
  # @since 2.1.0
11
13
  # @api private
12
14
  class Compile < Assets::Command
13
15
  desc "Compile assets for deployments"
14
16
 
17
+ private
18
+
15
19
  # @since 2.1.0
16
20
  # @api private
17
- def cmd_with_args
18
- result = super
21
+ def assets_command(slice)
22
+ cmd = super
19
23
 
20
24
  if config.subresource_integrity.any?
21
- result << "--"
22
- result << "--sri=#{config.subresource_integrity.join(',')}"
25
+ cmd << "--sri=#{escape(config.subresource_integrity.join(','))}"
23
26
  end
24
27
 
25
- result
28
+ cmd
26
29
  end
27
30
  end
28
31
  end
@@ -1,28 +1,25 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "command"
4
- require_relative "../../../interactive_system_call"
5
4
 
6
5
  module Hanami
7
6
  module CLI
8
7
  module Commands
9
8
  module App
10
9
  module Assets
10
+ # Watches for asset changes within each slice.
11
+ #
11
12
  # @since 2.1.0
12
13
  # @api private
13
14
  class Watch < Assets::Command
14
15
  desc "Start assets watch mode"
15
16
 
16
- def initialize(config: app.config.assets, system_call: InteractiveSystemCall.new, **)
17
- super(config: config, system_call: system_call)
18
- end
19
-
20
17
  private
21
18
 
22
19
  # @since 2.1.0
23
20
  # @api private
24
- def cmd_with_args
25
- super + ["--", "--watch"]
21
+ def assets_command(slice)
22
+ super + ["--watch"]
26
23
  end
27
24
  end
28
25
  end
@@ -15,23 +15,28 @@ module Hanami
15
15
 
16
16
  # @since 2.1.0
17
17
  # @api private
18
- def initialize(interactive_system_call: InteractiveSystemCall.new, **)
19
- @interactive_system_call = interactive_system_call
20
- super()
18
+ def initialize(
19
+ out:, err:,
20
+ system_call: InteractiveSystemCall.new(out: out, err: err),
21
+ **opts
22
+ )
23
+ super(out: out, err: err, **opts)
24
+
25
+ @system_call = system_call
21
26
  end
22
27
 
23
28
  # @since 2.1.0
24
29
  # @api private
25
30
  def call(**)
26
31
  bin, args = executable
27
- interactive_system_call.call(bin, *args)
32
+ system_call.call(bin, *args)
28
33
  end
29
34
 
30
35
  private
31
36
 
32
37
  # @since 2.1.0
33
38
  # @api private
34
- attr_reader :interactive_system_call
39
+ attr_reader :system_call
35
40
 
36
41
  # @since 2.1.0
37
42
  # @api private
@@ -22,12 +22,25 @@ module Hanami
22
22
  DEFAULT_SKIP_VIEW = false
23
23
  private_constant :DEFAULT_SKIP_VIEW
24
24
 
25
+ DEFAULT_SKIP_TESTS = false
26
+ private_constant :DEFAULT_SKIP_TESTS
27
+
25
28
  argument :name, required: true, desc: "Action name"
26
29
  option :url, required: false, type: :string, desc: "Action URL"
27
30
  option :http, required: false, type: :string, desc: "Action HTTP method"
28
31
  # option :format, required: false, type: :string, default: DEFAULT_FORMAT, desc: "Template format"
29
- option :skip_view, required: false, type: :boolean, default: DEFAULT_SKIP_VIEW,
30
- desc: "Skip view and template generation"
32
+ option \
33
+ :skip_view,
34
+ required: false,
35
+ type: :boolean,
36
+ default: DEFAULT_SKIP_VIEW,
37
+ desc: "Skip view and template generation"
38
+ option \
39
+ :skip_tests,
40
+ required: false,
41
+ type: :boolean,
42
+ default: DEFAULT_SKIP_TESTS,
43
+ desc: "Skip test generation"
31
44
  option :slice, required: false, desc: "Slice name"
32
45
 
33
46
  # rubocop:disable Layout/LineLength
@@ -48,20 +61,32 @@ module Hanami
48
61
 
49
62
  # @since 2.0.0
50
63
  # @api private
51
- def initialize(fs: Hanami::CLI::Files.new, inflector: Dry::Inflector.new,
52
- naming: Naming.new(inflector: inflector),
53
- generator: Generators::App::Action.new(fs: fs, inflector: inflector), **)
64
+ def initialize(
65
+ fs:, inflector:,
66
+ naming: Naming.new(inflector: inflector),
67
+ generator: Generators::App::Action.new(fs: fs, inflector: inflector),
68
+ **opts
69
+ )
70
+ super(fs: fs, inflector: inflector, **opts)
71
+
54
72
  @naming = naming
55
73
  @generator = generator
56
- super(fs: fs)
57
74
  end
58
75
 
59
76
  # rubocop:disable Metrics/ParameterLists
60
77
 
61
78
  # @since 2.0.0
62
79
  # @api private
63
- def call(name:, url: nil, http: nil, format: DEFAULT_FORMAT, skip_view: DEFAULT_SKIP_VIEW, slice: nil,
64
- context: nil, **)
80
+ def call(
81
+ name:,
82
+ url: nil,
83
+ http: nil,
84
+ format: DEFAULT_FORMAT,
85
+ skip_view: DEFAULT_SKIP_VIEW,
86
+ skip_tests: DEFAULT_SKIP_TESTS, # rubocop:disable Lint/UnusedMethodArgument
87
+ slice: nil,
88
+ context: nil, **
89
+ )
65
90
  slice = inflector.underscore(Shellwords.shellescape(slice)) if slice
66
91
  name = naming.action_name(name)
67
92
  *controller, action = name.split(ACTION_SEPARATOR)
@@ -12,8 +12,17 @@ module Hanami
12
12
  # @since 2.1.0
13
13
  # @api private
14
14
  class Part < App::Command
15
+ DEFAULT_SKIP_TESTS = false
16
+ private_constant :DEFAULT_SKIP_TESTS
17
+
15
18
  argument :name, required: true, desc: "Part name"
16
19
  option :slice, required: false, desc: "Slice name"
20
+ option \
21
+ :skip_tests,
22
+ required: false,
23
+ type: :boolean,
24
+ default: DEFAULT_SKIP_TESTS,
25
+ desc: "Skip test generation"
17
26
 
18
27
  example [
19
28
  %(book (MyApp::Views::Parts::Book)),
@@ -25,18 +34,17 @@ module Hanami
25
34
  # @since 2.0.0
26
35
  # @api private
27
36
  def initialize(
28
- fs: Hanami::CLI::Files.new,
29
- inflector: Dry::Inflector.new,
37
+ fs:, inflector:,
30
38
  generator: Generators::App::Part.new(fs: fs, inflector: inflector),
31
- **
39
+ **opts
32
40
  )
41
+ super(fs: fs, inflector: inflector, **opts)
33
42
  @generator = generator
34
- super(fs: fs)
35
43
  end
36
44
 
37
45
  # @since 2.0.0
38
46
  # @api private
39
- def call(name:, slice: nil, **)
47
+ def call(name:, slice: nil, skip_tests: DEFAULT_SKIP_TESTS, **) # rubocop:disable Lint/UnusedMethodArgument
40
48
  slice = inflector.underscore(Shellwords.shellescape(slice)) if slice
41
49
 
42
50
  generator.call(app.namespace, name, slice)
@@ -23,10 +23,13 @@ module Hanami
23
23
 
24
24
  # @since 2.0.0
25
25
  # @api private
26
- def initialize(fs: Hanami::CLI::Files.new, inflector: Dry::Inflector.new,
27
- generator: Generators::App::Slice.new(fs: fs, inflector: inflector), **)
26
+ def initialize(
27
+ fs:, inflector:,
28
+ generator: Generators::App::Slice.new(fs: fs, inflector: inflector),
29
+ **opts
30
+ )
31
+ super(fs: fs, inflector: inflector, **opts)
28
32
  @generator = generator
29
- super(fs: fs)
30
33
  end
31
34
 
32
35
  # @since 2.0.0
@@ -33,13 +33,12 @@ module Hanami
33
33
  # @since 2.0.0
34
34
  # @api private
35
35
  def initialize(
36
- fs: Hanami::CLI::Files.new,
37
- inflector: Dry::Inflector.new,
36
+ fs:, inflector:,
38
37
  generator: Generators::App::View.new(fs: fs, inflector: inflector),
39
- **
38
+ **opts
40
39
  )
40
+ super(fs: fs, inflector: inflector, **opts)
41
41
  @generator = generator
42
- super(fs: fs)
43
42
  end
44
43
 
45
44
  # @since 2.0.0
@@ -46,8 +46,8 @@ module Hanami
46
46
 
47
47
  # @since 2.0.0
48
48
  # @api private
49
- def initialize(server: Hanami::CLI::Server.new)
50
- super()
49
+ def initialize(server: Hanami::CLI::Server.new, **opts)
50
+ super(**opts)
51
51
  @server = server
52
52
  end
53
53
 
@@ -63,17 +63,16 @@ module Hanami
63
63
  # @since 2.0.0
64
64
  # @api private
65
65
  def initialize(
66
- fs: Hanami::CLI::Files.new,
67
- inflector: Dry::Inflector.new,
66
+ fs:, inflector:,
68
67
  bundler: CLI::Bundler.new(fs: fs),
69
68
  generator: Generators::Gem::App.new(fs: fs, inflector: inflector),
70
69
  system_call: SystemCall.new,
71
- **other
70
+ **opts
72
71
  )
72
+ super(fs: fs, inflector: inflector, **opts)
73
73
  @bundler = bundler
74
74
  @generator = generator
75
75
  @system_call = system_call
76
- super(fs: fs, inflector: inflector, **other)
77
76
  end
78
77
 
79
78
  # rubocop:enable Metrics/ParameterLists
@@ -98,12 +97,17 @@ module Hanami
98
97
  bundler.install!
99
98
 
100
99
  unless skip_assets
101
- out.puts "Running npm install..."
102
- system_call.call("npm", ["install"])
100
+ out.puts "Running NPM install..."
101
+ system_call.call("npm", ["install"]).tap do |result|
102
+ unless result.successful?
103
+ puts "NPM ERROR:"
104
+ puts(result.err.lines.map { |line| line.prepend(" ") })
105
+ end
106
+ end
103
107
  end
104
108
 
105
109
  out.puts "Running Hanami install..."
106
- run_install_commmand!(head: head)
110
+ run_install_command!(head: head)
107
111
  end
108
112
  end
109
113
  end
@@ -116,10 +120,14 @@ module Hanami
116
120
  attr_reader :generator
117
121
  attr_reader :system_call
118
122
 
119
- def run_install_commmand!(head:)
123
+ def run_install_command!(head:)
120
124
  head_flag = head ? " --head" : ""
121
125
  bundler.exec("hanami install#{head_flag}").tap do |result|
122
- raise HanamiInstallError.new(result.err) unless result.successful?
126
+ if result.successful?
127
+ bundler.exec("check").successful? || bundler.exec("install")
128
+ else
129
+ raise HanamiInstallError.new(result.err)
130
+ end
123
131
  end
124
132
  end
125
133
  end
@@ -5,7 +5,7 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
6
  <title><%= humanized_app_name %> - <%= humanized_slice_name %></title>
7
7
  <%- if bundled_assets? -%>
8
- <%%= favicon %>
8
+ <%%= favicon_tag %>
9
9
  <%= stylesheet_erb_tag %>
10
10
  <%- end -%>
11
11
  </head>
@@ -35,6 +35,7 @@ module Hanami
35
35
  if context.bundled_assets?
36
36
  fs.write(fs.join(directory, "assets", "js", "app.js"), t("app_js.erb", context))
37
37
  fs.write(fs.join(directory, "assets", "css", "app.css"), t("app_css.erb", context))
38
+ fs.write(fs.join(directory, "assets", "images", "favicon.ico"), file("favicon.ico"))
38
39
  end
39
40
 
40
41
  # fs.write(fs.join(directory, "/entities.rb"), t("entities.erb", context))
@@ -64,6 +65,10 @@ module Hanami
64
65
  end
65
66
 
66
67
  alias_method :t, :template
68
+
69
+ def file(path)
70
+ File.read(File.join(__dir__, "slice", path))
71
+ end
67
72
  end
68
73
  end
69
74
  end
@@ -38,13 +38,13 @@ module Hanami
38
38
  # @since 2.1.0
39
39
  # @api private
40
40
  def stylesheet_erb_tag
41
- %(<%= css "#{slice}/app" %>)
41
+ %(<%= stylesheet_tag "app" %>)
42
42
  end
43
43
 
44
44
  # @since 2.1.0
45
45
  # @api private
46
46
  def javascript_erb_tag
47
- %(<%= js "#{slice}/app" %>)
47
+ %(<%= javascript_tag "app" %>)
48
48
  end
49
49
 
50
50
  private
@@ -53,7 +53,7 @@ module Hanami
53
53
  # @since 2.0.0
54
54
  # @api private
55
55
  def camelized_app_name
56
- inflector.camelize(app)
56
+ inflector.camelize(app).gsub(/[^\p{Alnum}]/, "")
57
57
  end
58
58
 
59
59
  # @since 2.0.0
@@ -5,14 +5,14 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
6
  <title><%= humanized_app_name %></title>
7
7
  <%- if generate_assets? -%>
8
- <%%= favicon %>
9
- <%%= css "app" %>
8
+ <%%= favicon_tag %>
9
+ <%%= stylesheet_tag "app" %>
10
10
  <%- end -%>
11
11
  </head>
12
12
  <body>
13
13
  <%%= yield %>
14
14
  <%- if generate_assets? -%>
15
- <%%= js "app" %>
15
+ <%%= javascript_tag "app" %>
16
16
  <%- end -%>
17
17
  </body>
18
18
  </html>
@@ -4,6 +4,8 @@ await assets.run();
4
4
 
5
5
  // To provide additional esbuild (https://esbuild.github.io) options, use the following:
6
6
  //
7
+ // Read more at: https://guides.hanamirb.org/assets/overview/
8
+ //
7
9
  // await assets.run({
8
10
  // esbuildOptionsFn: (args, esbuildOptions) => {
9
11
  // // Add to esbuildOptions here. Use `args.watch` as a condition for different options for
@@ -3,13 +3,13 @@
3
3
  source "https://rubygems.org"
4
4
 
5
5
  <%= hanami_gem("hanami") %>
6
- <%= hanami_gem("router") %>
7
- <%= hanami_gem("controller") %>
8
- <%= hanami_gem("validations") %>
9
- <%= hanami_gem("view") %>
10
6
  <%- if generate_assets? -%>
11
7
  <%= hanami_gem("assets") %>
12
8
  <%- end -%>
9
+ <%= hanami_gem("controller") %>
10
+ <%= hanami_gem("router") %>
11
+ <%= hanami_gem("validations") %>
12
+ <%= hanami_gem("view") %>
13
13
 
14
14
  gem "dry-types", "~> 1.0", ">= 1.6.1"
15
15
  gem "puma"
@@ -1,9 +1,7 @@
1
1
  {
2
2
  "name": "<%= underscored_app_name %>",
3
3
  "private": true,
4
- "scripts": {
5
- "assets": "node config/assets.mjs"
6
- },
4
+ "type": "module",
7
5
  "dependencies": {
8
6
  <%= hanami_assets_npm_package %>
9
7
  }
@@ -62,7 +62,7 @@ module Hanami
62
62
 
63
63
  if context.generate_assets?
64
64
  fs.write("package.json", t("package.json.erb", context))
65
- fs.write("config/assets.mjs", file("assets.mjs"))
65
+ fs.write("config/assets.js", file("assets.js"))
66
66
  fs.write("app/assets/js/app.js", t("app_js.erb", context))
67
67
  fs.write("app/assets/css/app.css", t("app_css.erb", context))
68
68
  fs.write("app/assets/images/favicon.ico", file("favicon.ico"))
@@ -9,7 +9,7 @@ module Hanami
9
9
  # @since 2.0.0
10
10
  # @api private
11
11
  def self.version
12
- return Hanami::VERSION if defined?(Hanami::VERSION)
12
+ return Hanami::VERSION if Hanami.const_defined?(:VERSION)
13
13
 
14
14
  Hanami::CLI::VERSION
15
15
  end
@@ -32,7 +32,7 @@ module Hanami
32
32
  if prerelease?
33
33
  result = result
34
34
  .sub(/\.(alpha|beta|rc)/, '-\1')
35
- .sub(/(alpha|beta|rc)(.+)\.(.+)$/, '\1.\2')
35
+ .sub(/(alpha|beta|rc)(\d+)(?:\.\d+)?\Z/, '\1.\2')
36
36
  end
37
37
 
38
38
  "^#{result}"
@@ -41,7 +41,7 @@ module Hanami
41
41
  # @since 2.0.0
42
42
  # @api private
43
43
  def self.prerelease?
44
- version =~ /alpha|beta|rc/
44
+ version.match?(/alpha|beta|rc/)
45
45
  end
46
46
 
47
47
  # @example
@@ -9,15 +9,16 @@ module Hanami
9
9
  class InteractiveSystemCall
10
10
  # @api private
11
11
  # @since 2.1.0
12
- def initialize(out: $stdout, err: $stderr)
12
+ def initialize(out: $stdout, err: $stderr, exit_after: true)
13
13
  @out = out
14
14
  @err = err
15
+ @exit_after = exit_after
15
16
  super()
16
17
  end
17
18
 
18
19
  # @api private
19
20
  # @since 2.1.0
20
- def call(cmd, *args, env: {})
21
+ def call(cmd, *args, env: {}, out_prefix: "")
21
22
  ::Bundler.with_unbundled_env do
22
23
  threads = []
23
24
  exit_status = 0
@@ -26,14 +27,14 @@ module Hanami
26
27
  Open3.popen3(env, command(cmd, *args)) do |_stdin, stdout, stderr, wait_thr|
27
28
  threads << Thread.new do
28
29
  stdout.each_line do |line|
29
- out.puts(line)
30
+ out.puts("#{out_prefix}#{line}")
30
31
  end
31
32
  rescue IOError # FIXME: Check if this is legit
32
33
  end
33
34
 
34
35
  threads << Thread.new do
35
36
  stderr.each_line do |line|
36
- err.puts(line)
37
+ err.puts("#{out_prefix}#{line}")
37
38
  end
38
39
  rescue IOError # FIXME: Check if this is legit
39
40
  end
@@ -44,7 +45,7 @@ module Hanami
44
45
  end
45
46
  # rubocop:enable Lint/SuppressedException
46
47
 
47
- exit(exit_status.exitstatus)
48
+ exit(exit_status.exitstatus) if @exit_after
48
49
  end
49
50
  end
50
51
 
@@ -6,6 +6,6 @@ module Hanami
6
6
  #
7
7
  # @api public
8
8
  # @since 2.0.0
9
- VERSION = "2.1.0.rc1"
9
+ VERSION = "2.1.0.rc3"
10
10
  end
11
11
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hanami-cli
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.0.rc1
4
+ version: 2.1.0.rc3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Luca Guidi
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-11-01 00:00:00.000000000 Z
11
+ date: 2024-02-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -250,6 +250,7 @@ files:
250
250
  - lib/hanami/cli/generators/app/slice/app_js.erb
251
251
  - lib/hanami/cli/generators/app/slice/app_layout.erb
252
252
  - lib/hanami/cli/generators/app/slice/entities.erb
253
+ - lib/hanami/cli/generators/app/slice/favicon.ico
253
254
  - lib/hanami/cli/generators/app/slice/helpers.erb
254
255
  - lib/hanami/cli/generators/app/slice/keep.erb
255
256
  - lib/hanami/cli/generators/app/slice/repository.erb
@@ -272,7 +273,7 @@ files:
272
273
  - lib/hanami/cli/generators/gem/app/app_css.erb
273
274
  - lib/hanami/cli/generators/gem/app/app_js.erb
274
275
  - lib/hanami/cli/generators/gem/app/app_layout.erb
275
- - lib/hanami/cli/generators/gem/app/assets.mjs
276
+ - lib/hanami/cli/generators/gem/app/assets.js
276
277
  - lib/hanami/cli/generators/gem/app/config_ru.erb
277
278
  - lib/hanami/cli/generators/gem/app/dev
278
279
  - lib/hanami/cli/generators/gem/app/env.erb
@@ -326,11 +327,11 @@ required_ruby_version: !ruby/object:Gem::Requirement
326
327
  version: '3.0'
327
328
  required_rubygems_version: !ruby/object:Gem::Requirement
328
329
  requirements:
329
- - - ">"
330
+ - - ">="
330
331
  - !ruby/object:Gem::Version
331
- version: 1.3.1
332
+ version: '0'
332
333
  requirements: []
333
- rubygems_version: 3.4.13
334
+ rubygems_version: 3.5.6
334
335
  signing_key:
335
336
  specification_version: 4
336
337
  summary: Hanami CLI