hanami-cli 2.0.0 → 2.1.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 (83) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +2 -0
  3. data/{.action_hero.yml → .repobot.yml} +1 -3
  4. data/.rubocop.yml +6 -0
  5. data/CHANGELOG.md +115 -0
  6. data/Gemfile +7 -2
  7. data/LICENSE.md +1 -1
  8. data/README.md +1 -1
  9. data/hanami-cli.gemspec +1 -1
  10. data/lib/hanami/cli/bundler.rb +26 -9
  11. data/lib/hanami/cli/command.rb +28 -6
  12. data/lib/hanami/cli/commands/app/assets/command.rb +155 -0
  13. data/lib/hanami/cli/commands/app/assets/compile.rb +35 -0
  14. data/lib/hanami/cli/commands/app/assets/watch.rb +29 -0
  15. data/lib/hanami/cli/commands/app/assets.rb +16 -0
  16. data/lib/hanami/cli/commands/app/command.rb +5 -3
  17. data/lib/hanami/cli/commands/app/db/rollback.rb +30 -6
  18. data/lib/hanami/cli/commands/app/db/utils/database.rb +5 -1
  19. data/lib/hanami/cli/commands/app/dev.rb +50 -0
  20. data/lib/hanami/cli/commands/app/generate/action.rb +38 -9
  21. data/lib/hanami/cli/commands/app/generate/part.rb +57 -0
  22. data/lib/hanami/cli/commands/app/generate/slice.rb +6 -3
  23. data/lib/hanami/cli/commands/app/generate/view.rb +56 -0
  24. data/lib/hanami/cli/commands/app/generate.rb +2 -2
  25. data/lib/hanami/cli/commands/app/install.rb +16 -1
  26. data/lib/hanami/cli/commands/app/server.rb +6 -10
  27. data/lib/hanami/cli/commands/app.rb +10 -0
  28. data/lib/hanami/cli/commands/gem/new.rb +70 -12
  29. data/lib/hanami/cli/errors.rb +30 -0
  30. data/lib/hanami/cli/files.rb +8 -2
  31. data/lib/hanami/cli/generators/app/action/action.erb +5 -1
  32. data/lib/hanami/cli/generators/app/action/slice_action.erb +5 -1
  33. data/lib/hanami/cli/generators/app/action/slice_template.html.erb +1 -0
  34. data/lib/hanami/cli/generators/app/action/slice_view.erb +10 -0
  35. data/lib/hanami/cli/generators/app/action/template.html.erb +1 -2
  36. data/lib/hanami/cli/generators/app/action/view.erb +2 -5
  37. data/lib/hanami/cli/generators/app/action.rb +74 -22
  38. data/lib/hanami/cli/generators/app/action_context.rb +1 -1
  39. data/lib/hanami/cli/generators/app/part/app_base_part.erb +9 -0
  40. data/lib/hanami/cli/generators/app/part/app_part.erb +13 -0
  41. data/lib/hanami/cli/generators/app/part/slice_base_part.erb +9 -0
  42. data/lib/hanami/cli/generators/app/part/slice_part.erb +13 -0
  43. data/lib/hanami/cli/generators/app/part.rb +101 -0
  44. data/lib/hanami/cli/generators/app/part_context.rb +98 -0
  45. data/lib/hanami/cli/generators/app/slice/app_css.erb +5 -0
  46. data/lib/hanami/cli/generators/app/slice/app_js.erb +1 -0
  47. data/lib/hanami/cli/generators/app/slice/app_layout.erb +18 -0
  48. data/lib/hanami/cli/generators/app/slice/favicon.ico +0 -0
  49. data/lib/hanami/cli/generators/app/slice/helpers.erb +10 -0
  50. data/lib/hanami/cli/generators/app/slice/view.erb +0 -2
  51. data/lib/hanami/cli/generators/app/slice.rb +20 -6
  52. data/lib/hanami/cli/generators/app/slice_context.rb +18 -0
  53. data/lib/hanami/cli/generators/app/view/app_template.html.erb +1 -0
  54. data/lib/hanami/cli/generators/app/view/app_view.erb +10 -0
  55. data/lib/hanami/cli/generators/app/view/slice_template.html.erb +1 -0
  56. data/lib/hanami/cli/generators/app/view/slice_view.erb +10 -0
  57. data/lib/hanami/cli/generators/app/view.rb +89 -0
  58. data/lib/hanami/cli/generators/app/view_context.rb +100 -0
  59. data/lib/hanami/cli/generators/context.rb +72 -5
  60. data/lib/hanami/cli/generators/gem/app/404.html +82 -0
  61. data/lib/hanami/cli/generators/gem/app/500.html +82 -0
  62. data/lib/hanami/cli/generators/gem/app/app_css.erb +5 -0
  63. data/lib/hanami/cli/generators/gem/app/app_js.erb +1 -0
  64. data/lib/hanami/cli/generators/gem/app/app_layout.erb +18 -0
  65. data/lib/hanami/cli/generators/gem/app/assets.js +16 -0
  66. data/lib/hanami/cli/generators/gem/app/dev +8 -0
  67. data/lib/hanami/cli/generators/gem/app/favicon.ico +0 -0
  68. data/lib/hanami/cli/generators/gem/app/gemfile.erb +14 -6
  69. data/lib/hanami/cli/generators/gem/app/gitignore.erb +6 -0
  70. data/lib/hanami/cli/generators/gem/app/helpers.erb +10 -0
  71. data/lib/hanami/cli/generators/gem/app/package.json.erb +8 -0
  72. data/lib/hanami/cli/generators/gem/app/procfile.erb +4 -0
  73. data/lib/hanami/cli/generators/gem/app/puma.erb +39 -7
  74. data/lib/hanami/cli/generators/gem/app/routes.erb +1 -1
  75. data/lib/hanami/cli/generators/gem/app/view.erb +9 -0
  76. data/lib/hanami/cli/generators/gem/app.rb +25 -1
  77. data/lib/hanami/cli/generators/version.rb +14 -2
  78. data/lib/hanami/cli/interactive_system_call.rb +65 -0
  79. data/lib/hanami/cli/naming.rb +25 -0
  80. data/lib/hanami/cli/system_call.rb +8 -2
  81. data/lib/hanami/cli/url.rb +1 -1
  82. data/lib/hanami/cli/version.rb +1 -1
  83. metadata +47 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a1351e33232b164c6e82ac6148e95eac2562e43dcd36f1a1d11aad61307f32da
4
- data.tar.gz: 647213baac46e31addb957a6ce4d3f8b11e28e64ddb02d3cfe1569ce49015ed6
3
+ metadata.gz: f0885d4f1e10f7f8bd8691c04524be0f6353de0b0a7ce19c2ecf42ae31c8f8cd
4
+ data.tar.gz: d6c27d7571896e9f93d2ab41206e50f922fdfecc892ccf3d2328f9123c6d2348
5
5
  SHA512:
6
- metadata.gz: cb27cd6155cd93c1783402e32992104d32a41da98397ef57c0dc1b0b72edf0cb76c9203d48dc925c352f9742a16c274cf0679dc9e431c12d83ad69508478786d
7
- data.tar.gz: 04cc7a076b6a66d30729ebaa28b9108cb41bd7515cd538d5a81a350b22850ff3fbefd27a0a9044130ed50bfa8dc34562ab6e32e57834924cc7c3e51a13468708
6
+ metadata.gz: 74180e374ac3836550e622af10dc998bc5356d3a745c2b0731ef2f03e2bc06f639ab5c375d7d53e7dc67fe40672eb80c38b77bb5cc6111d52d9a20da415de222
7
+ data.tar.gz: 94596b8dd6f53f2dc6d53f37bfcec37b5002324ef85715c9791b94ebb0fa198884c8acf7d339752e9bb11b0da9f8fbddb4fa5f89ec62fb49944af8cc8732087d
@@ -24,6 +24,8 @@ jobs:
24
24
  fail-fast: false
25
25
  matrix:
26
26
  ruby:
27
+ - "3.3"
28
+ - "3.2"
27
29
  - "3.1"
28
30
  - "3.0"
29
31
  steps:
@@ -3,9 +3,7 @@
3
3
  sources:
4
4
  - repo: hanami/template-gem
5
5
  sync:
6
- - ".action_hero.yml.erb"
6
+ - ".repobot.yml.erb"
7
7
  - ".github/FUNDING.yml"
8
- - ".rspec"
9
- - ".rubocop.yml"
10
8
  - "CODE_OF_CONDUCT.md"
11
9
  - "LICENSE.md.erb"
data/.rubocop.yml CHANGED
@@ -8,9 +8,13 @@ Layout/LineLength:
8
8
  Exclude:
9
9
  - Gemfile
10
10
  - 'spec/**/*.rb'
11
+ Lint/DuplicateBranch:
12
+ Enabled: false
11
13
  Lint/EmptyFile:
12
14
  Exclude:
13
15
  - 'spec/fixtures/**/*.rb'
16
+ Lint/NonLocalExitFromIterator:
17
+ Enabled: false
14
18
  Naming/HeredocDelimiterNaming:
15
19
  Enabled: false
16
20
  Naming/MethodParameterName:
@@ -26,6 +30,8 @@ Style/CommentedKeyword:
26
30
  Enabled: false
27
31
  Style/EmptyHeredoc:
28
32
  Enabled: false
33
+ Style/IdenticalConditionalBranches:
34
+ Enabled: false
29
35
  Style/LambdaCall:
30
36
  Enabled: false
31
37
  Style/TrailingCommaInArguments:
data/CHANGELOG.md CHANGED
@@ -2,6 +2,121 @@
2
2
 
3
3
  Hanami Command Line Interface
4
4
 
5
+ ## v2.1.0 - 2024-02-27
6
+
7
+ ### Changed
8
+
9
+ - [Tim Riley] Underscore slice names in `public/assets/` to avoid naming conflicts with nested asset entry points. In this arrangement, an "admin" slice will have its assets compiled into `public/assets/_admin/`.
10
+
11
+ ## v2.1.0.rc3 - 2024-02-16
12
+
13
+ ### Changed
14
+
15
+ - [Tim Riley] For `hanami assets` commands, run a separate assets compilation process per slice (in parallel).
16
+ - [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.
17
+ - [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.
18
+ - [Tim Riley] Do not generate `"scripts": {"assets": "..."}` section in new app's `package.json`.
19
+ - [Tim Riley] Subclasses of `Hanami::CLI::Command` receive default args to their `#initialize` methods, and do not need to re-declare default args themselves.
20
+ - [Philip Arndt] Alphabetically sort hanami gems in the new app `Gemfile`.
21
+
22
+ ### Fixed
23
+
24
+ - [Nishiki (錦華)] Strip invalid characters from module name when generating new app.
25
+
26
+ ## v2.1.0.rc2 - 2023-11-08
27
+
28
+ ### Added
29
+
30
+ - [Tim Riley] Add `--skip-tests` for `hanami generate` commands. This CLI option will skip tests generation.
31
+
32
+ ### Changed
33
+
34
+ - [Tim Riley] Set `"type": "module"` in package.json, enabling ES modules by default
35
+ - [Tim Riley] Rename `config/assets.mjs` to `config/assets.js` (use a plain `.js` file extension)
36
+
37
+ ### Fixed
38
+
39
+ - [Tim Riley] Use correct helper names in generated app layout
40
+ - [Luca Guidi] Ensure to generate apps with correct pre-release version of `hanami-assets` NPM package
41
+ - [Sean Collins] Print to stderr NPM installation errors when running `hanami install`
42
+ - [Sean Collins] Ensure to install missing gems after `hanami install` is ran
43
+
44
+ ## v2.1.0.rc1 - 2023-11-01
45
+
46
+ ### Added
47
+
48
+ - [Tim Riley] `hanami new` to generate `bin/dev` as configuration for `hanami dev`
49
+ - [Luca Guidi] Introducing `hanami generate part` to generate view parts
50
+
51
+ ### Fixed
52
+
53
+ - [Luca Guidi] `hanami new` generates a fully documented Puma configuration in `config/puma.rb`
54
+
55
+ ### Changed
56
+
57
+ - [Tim Riley] `hanami new` generates a `config/assets.mjs` as Assets configuration
58
+ - [Tim Riley] `hanami new` generates a leaner `package.json`
59
+ - [Tim Riley] `hanami new` doesn't generate a default root route anymore
60
+ - [Aaron Moodie & Tim Riley] `hanami new` to generate a redesigned 404 and 500 error pages
61
+ - [Luca Guidi] When generating a RESTful action, skip `create`, if `new` is present, and `update`, if `edit` is present
62
+
63
+ ## v2.1.0.beta2 - 2023-10-04
64
+
65
+ ### Added
66
+
67
+ - [Luca Guidi] `hanami new` generates `Procfile.dev`
68
+ - [Luca Guidi] `hanami new` generates basic app assets, if `hanami-assets` is bundled by the app
69
+ - [Luca Guidi] `hanami new` accepts `--head` to generate the app using Hanami HEAD version from GitHub
70
+ - [Luca Guidi] `hanami generate slice` generates basic slice assets, if `hanami-assets` is bundled by the app
71
+ - [Ryan Bigg] `hanami generate action` generates corresponding view, if `hanami-view` is bundled by the app
72
+ - [Luca Guidi] `hanami assets compile` to compile assets at the deploy time
73
+ - [Luca Guidi] `hanami assets watch` to watch and compile assets at the development time
74
+ - [Luca Guidi] `hanami dev` to start the processes in `Procfile.dev`
75
+
76
+ ### Fixed
77
+
78
+ - [Luca Guidi] `hanami new` generates a `Gemfile` with `hanami-webconsole` in `:development` group
79
+ - [Luca Guidi] `hanami new` generates a `Gemfile` with versioned `hanami-webconsole`, `hanami-rspec`, and `hanami-reloader`
80
+
81
+ ## v2.1.0.beta1 - 2023-06-29
82
+
83
+ ### Added
84
+
85
+ - [Tim Riley] `hanami new` to generate default views, templates, and helpers
86
+ - [Tim Riley] `hanami generate slice` to generate default views, templates, and helpers
87
+ - [Tim Riley] `hanami generate action` to generate associated view and template
88
+ - [Tim Riley] Introduced `hanami generate view`
89
+ - [Tim Riley] `hanami new` to generate `Gemfile` with `hanami-view` and `hanami-webconsole` gems
90
+ - [Tim Riley] `hanami new` to generate default error pages for `404` and `500` HTTP errors
91
+
92
+ ### Fixed
93
+
94
+ - [Philip Arndt] `hanami server` to start only one Puma worker by default
95
+
96
+ ## v2.0.3 - 2023-02-01
97
+
98
+ ### Added
99
+
100
+ - [Luca Guidi] Generate a default `.gitignore` when using `hanami new`
101
+
102
+ ### Fixed
103
+
104
+ - [dsisnero] Ensure to run automatically bundle gems when using `hanami new` on Windows
105
+ - [Luca Guidi] Ensure to generate the correct action identifier in routes when using `hanami generate action` with deeply nested action name
106
+
107
+ ## v2.0.2 - 2022-12-25
108
+
109
+ ### Added
110
+
111
+ - [Luca Guidi] Official support for Ruby 3.2
112
+
113
+ ## v2.0.1 - 2022-12-06
114
+
115
+ ### Fixed
116
+
117
+ - [Luca Guidi] Ensure to load `.env` files during CLI commands execution
118
+ - [Luca Guidi] Ensure `hanami server` to respect HTTP port used in `.env` or the value given as CLI argument (`--port`)
119
+
5
120
  ## v2.0.0 - 2022-11-22
6
121
 
7
122
  ### Added
data/Gemfile CHANGED
@@ -10,12 +10,17 @@ unless ENV["CI"]
10
10
  end
11
11
 
12
12
  gem "hanami", github: "hanami/hanami", branch: "main"
13
- gem "hanami-utils", github: "hanami/utils", branch: "main"
14
- gem "hanami-router", github: "hanami/router", branch: "main"
13
+ gem "hanami-assets", github: "hanami/assets", branch: "main"
15
14
  gem "hanami-controller", github: "hanami/controller", branch: "main"
15
+ gem "hanami-router", github: "hanami/router", branch: "main"
16
+ gem "hanami-utils", github: "hanami/utils", branch: "main"
17
+
18
+ gem "dry-files", github: "dry-rb/dry-files", branch: "main"
16
19
 
17
20
  gem "rack"
18
21
 
22
+ gem "hanami-devtools", github: "hanami/devtools", branch: "main"
23
+
19
24
  group :test do
20
25
  gem "pry"
21
26
  end
data/LICENSE.md CHANGED
@@ -1,4 +1,4 @@
1
- Copyright © 2014-2022 Luca Guidi
1
+ Copyright © 2014 Hanami Team
2
2
 
3
3
  MIT License
4
4
 
data/README.md CHANGED
@@ -44,4 +44,4 @@ Everyone interacting in the `Hanami::CLI` project's codebases, issue trackers, c
44
44
 
45
45
  ## Copyright
46
46
 
47
- Copyright © 2014-2022 Hanami Team – Released under MIT License
47
+ Copyright © 2014 Hanami Team – Released under MIT License
data/hanami-cli.gemspec CHANGED
@@ -32,7 +32,7 @@ Gem::Specification.new do |spec|
32
32
 
33
33
  spec.add_dependency "bundler", "~> 2.1"
34
34
  spec.add_dependency "dry-cli", "~> 1.0", "< 2"
35
- spec.add_dependency "dry-files", "~> 1.0", ">= 1.0.1", "< 2"
35
+ spec.add_dependency "dry-files", "~> 1.0", ">= 1.0.2", "< 2"
36
36
  spec.add_dependency "dry-inflector", "~> 1.0", "< 2"
37
37
  spec.add_dependency "rake", "~> 13.0"
38
38
  spec.add_dependency "zeitwerk", "~> 2.6"
@@ -3,9 +3,9 @@
3
3
  require "bundler"
4
4
  require "open3"
5
5
  require "etc"
6
- require "dry/files"
7
- require_relative "./system_call"
8
- require_relative "./errors"
6
+ require_relative "files"
7
+ require_relative "system_call"
8
+ require_relative "errors"
9
9
 
10
10
  module Hanami
11
11
  module CLI
@@ -45,12 +45,12 @@ module Hanami
45
45
 
46
46
  # Returns a new bundler.
47
47
  #
48
- # @param fs [Dry::Files] the filesystem interaction object
48
+ # @param fs [Hanami::CLI::Files] the filesystem interaction object
49
49
  # @param system_call [SystemCall] convenience object for making system calls
50
50
  #
51
51
  # @since 2.0.0
52
52
  # @api public
53
- def initialize(fs: Dry::Files.new, system_call: SystemCall.new)
53
+ def initialize(fs: Hanami::CLI::Files.new, system_call: SystemCall.new)
54
54
  @fs = fs
55
55
  @system_call = system_call
56
56
  end
@@ -71,7 +71,7 @@ module Hanami
71
71
  #
72
72
  # @return [SystemCall::Result] the result of the `bundle` command execution
73
73
  #
74
- # @raise [StandardError] if the `bundle` command does not execute successfully
74
+ # @raise [Hanami::CLI::BundleInstallError] if the `bundle` command does not execute successfully
75
75
  #
76
76
  # @since 2.0.0
77
77
  # @api public
@@ -81,6 +81,20 @@ module Hanami
81
81
  end
82
82
  end
83
83
 
84
+ # Runs the given Hanami CLI command via `bundle exec hanami`
85
+ #
86
+ # @return [SystemCall::Result] the result of the command execution
87
+ #
88
+ # @raise [Hanami::CLI::HanamiExecError] if the does not execute successfully
89
+ #
90
+ # @since 2.1.0
91
+ # @api public
92
+ def hanami_exec(cmd, env: nil, &blk)
93
+ exec("hanami #{cmd}", env: env, &blk).tap do |result|
94
+ raise HanamiExecError.new(cmd, result.err) unless result.successful?
95
+ end
96
+ end
97
+
84
98
  # Executes the given command prefixed by `bundle exec`.
85
99
  #
86
100
  # @return [SystemCall::Result] the result of the command execution
@@ -127,7 +141,7 @@ module Hanami
127
141
 
128
142
  private
129
143
 
130
- # @return [Dry::Files]
144
+ # @return [Hanami::CLI::Files]
131
145
  #
132
146
  # @since 2.0.0
133
147
  # @api public
@@ -146,10 +160,13 @@ module Hanami
146
160
  # @since 2.0.0
147
161
  # @api private
148
162
  def which(cmd)
163
+ exts = ENV["PATHEXT"] ? ENV["PATHEXT"].split(";") : [""]
149
164
  # Adapted from https://stackoverflow.com/a/5471032/498386
150
165
  ENV["PATH"].split(File::PATH_SEPARATOR).each do |path|
151
- exe = fs.join(path, cmd)
152
- return exe if fs.executable?(exe) && !fs.directory?(exe)
166
+ exts.each do |ext|
167
+ exe = fs.join(path, "#{cmd}#{ext}")
168
+ return exe if fs.executable?(exe) && !fs.directory?(exe)
169
+ end
153
170
  end
154
171
 
155
172
  nil
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "dry/cli"
4
- require "dry/files"
5
4
  require "dry/inflector"
5
+ require_relative "files"
6
6
 
7
7
  module Hanami
8
8
  module CLI
@@ -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
- # @param fs [Dry::Files] object for managing file system interactions
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: Dry::Files.new, inflector: Dry::Inflector.new)
49
+ def initialize(out:, err:, fs:, inflector:)
28
50
  super()
29
51
  @out = out
30
52
  @err = err
@@ -52,7 +74,7 @@ module Hanami
52
74
 
53
75
  # Returns the object for managing file system interactions.
54
76
  #
55
- # @return [Dry::Files]
77
+ # @return [Hanami::CLI::Files]
56
78
  #
57
79
  # @since 2.0.0
58
80
  # @api public
@@ -0,0 +1,155 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "shellwords"
4
+ require_relative "../command"
5
+ require_relative "../../../interactive_system_call"
6
+
7
+ module Hanami
8
+ module CLI
9
+ module Commands
10
+ module App
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
+ #
27
+ # @since 2.1.0
28
+ # @api private
29
+ class Command < App::Command
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
+
40
+ @config = config
41
+ @system_call = system_call
42
+ end
43
+
44
+ # @since 2.1.0
45
+ # @api private
46
+ def call(**)
47
+ slices = slices_with_assets
48
+
49
+ if slices.empty?
50
+ out.puts "No assets found."
51
+ return
52
+ end
53
+
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
70
+ end
71
+
72
+ private
73
+
74
+ # @since 2.1.0
75
+ # @api private
76
+ attr_reader :config
77
+
78
+ # @since 2.1.0
79
+ # @api private
80
+ attr_reader :system_call
81
+
82
+ # @since 2.1.0
83
+ # @api private
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
92
+ end
93
+
94
+ # @since 2.1.0
95
+ # @api private
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/#{Hanami::Assets.public_assets_dir(slice)}"
105
+ end
106
+
107
+ cmd
108
+ end
109
+
110
+ # @since 2.1.0
111
+ # @api private
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?
143
+ end
144
+
145
+ # @since 2.1.0
146
+ # @api private
147
+ def escape(str)
148
+ Shellwords.shellescape(str)
149
+ end
150
+ end
151
+ end
152
+ end
153
+ end
154
+ end
155
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "command"
4
+
5
+ module Hanami
6
+ module CLI
7
+ module Commands
8
+ module App
9
+ module Assets
10
+ # Compiles assets for each slice.
11
+ #
12
+ # @since 2.1.0
13
+ # @api private
14
+ class Compile < Assets::Command
15
+ desc "Compile assets for deployments"
16
+
17
+ private
18
+
19
+ # @since 2.1.0
20
+ # @api private
21
+ def assets_command(slice)
22
+ cmd = super
23
+
24
+ if config.subresource_integrity.any?
25
+ cmd << "--sri=#{escape(config.subresource_integrity.join(','))}"
26
+ end
27
+
28
+ cmd
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "command"
4
+
5
+ module Hanami
6
+ module CLI
7
+ module Commands
8
+ module App
9
+ module Assets
10
+ # Watches for asset changes within each slice.
11
+ #
12
+ # @since 2.1.0
13
+ # @api private
14
+ class Watch < Assets::Command
15
+ desc "Start assets watch mode"
16
+
17
+ private
18
+
19
+ # @since 2.1.0
20
+ # @api private
21
+ def assets_command(slice)
22
+ super + ["--watch"]
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hanami
4
+ module CLI
5
+ module Commands
6
+ module App
7
+ # @since 2.1.0
8
+ # @api private
9
+ module Assets
10
+ require_relative "assets/compile"
11
+ require_relative "assets/watch"
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -1,7 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "dry/files"
3
+ require "hanami/env"
4
4
  require_relative "db/utils/database"
5
+ require_relative "../../files"
5
6
 
6
7
  module Hanami
7
8
  module CLI
@@ -34,6 +35,7 @@ module Hanami
34
35
  hanami_env = env ? env.to_s : ENV.fetch("HANAMI_ENV", "development")
35
36
 
36
37
  ENV["HANAMI_ENV"] = hanami_env
38
+ Hanami::Env.load
37
39
 
38
40
  super(*args, **opts)
39
41
  end
@@ -73,7 +75,7 @@ module Hanami
73
75
  klass.new(
74
76
  out: out,
75
77
  inflector: app.inflector,
76
- fs: Dry::Files
78
+ fs: Hanami::CLI::Files,
77
79
  ).call(*args)
78
80
  end
79
81
 
@@ -114,7 +116,7 @@ module Hanami
114
116
  #
115
117
  # @api private
116
118
  def database
117
- @database ||= Commands::DB::Utils::Database[app]
119
+ @database ||= Commands::App::DB::Utils::Database[app]
118
120
  end
119
121
 
120
122
  # This is NOT AVAILABLE as of the 2.0.0 release.