shakapacker 9.3.0.beta.6 → 9.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +46 -105
- data/ESLINT_TECHNICAL_DEBT.md +8 -2
- data/Gemfile.lock +1 -1
- data/README.md +53 -2
- data/docs/configuration.md +28 -0
- data/docs/rspack_migration_guide.md +238 -2
- data/docs/troubleshooting.md +21 -21
- data/eslint.config.fast.js +8 -0
- data/eslint.config.js +47 -10
- data/knip.ts +8 -1
- data/lib/install/config/shakapacker.yml +6 -6
- data/lib/shakapacker/configuration.rb +227 -4
- data/lib/shakapacker/dev_server.rb +88 -1
- data/lib/shakapacker/doctor.rb +129 -72
- data/lib/shakapacker/instance.rb +85 -1
- data/lib/shakapacker/manifest.rb +85 -11
- data/lib/shakapacker/runner.rb +12 -8
- data/lib/shakapacker/swc_migrator.rb +7 -7
- data/lib/shakapacker/version.rb +1 -1
- data/lib/shakapacker.rb +143 -3
- data/lib/tasks/shakapacker/doctor.rake +1 -1
- data/lib/tasks/shakapacker/export_bundler_config.rake +4 -4
- data/package/config.ts +0 -1
- data/package/configExporter/buildValidator.ts +53 -29
- data/package/configExporter/cli.ts +152 -118
- data/package/configExporter/configFile.ts +33 -26
- data/package/configExporter/fileWriter.ts +3 -3
- data/package/configExporter/types.ts +64 -0
- data/package/configExporter/yamlSerializer.ts +147 -36
- data/package/dev_server.ts +2 -1
- data/package/env.ts +1 -1
- data/package/environments/base.ts +4 -4
- data/package/environments/development.ts +7 -6
- data/package/environments/production.ts +6 -7
- data/package/environments/test.ts +2 -1
- data/package/index.ts +28 -4
- data/package/loaders.d.ts +2 -2
- data/package/optimization/webpack.ts +29 -31
- data/package/plugins/webpack.ts +2 -1
- data/package/rspack/index.ts +2 -1
- data/package/rules/file.ts +1 -0
- data/package/rules/jscommon.ts +1 -0
- data/package/utils/helpers.ts +0 -1
- data/package/utils/pathValidation.ts +68 -7
- data/package/utils/requireOrError.ts +10 -2
- data/package/utils/typeGuards.ts +43 -46
- data/package/webpack-types.d.ts +2 -2
- data/package/webpackDevServerConfig.ts +1 -0
- data/package.json +2 -3
- data/test/configExporter/integration.test.js +8 -8
- data/test/package/configExporter/cli.test.js +440 -0
- data/test/package/configExporter/types.test.js +163 -0
- data/test/package/configExporter.test.js +271 -7
- data/test/package/yamlSerializer.test.js +204 -0
- data/test/typescript/pathValidation.test.js +44 -0
- data/test/typescript/requireOrError.test.js +49 -0
- data/yarn.lock +0 -32
- metadata +11 -6
- data/.eslintrc.fast.js +0 -40
- data/.eslintrc.js +0 -84
- data/package-lock.json +0 -13047
data/lib/shakapacker/doctor.rb
CHANGED
|
@@ -391,7 +391,7 @@ module Shakapacker
|
|
|
391
391
|
expected_binstubs = {
|
|
392
392
|
"bin/shakapacker" => "Main Shakapacker binstub",
|
|
393
393
|
"bin/shakapacker-dev-server" => "Development server binstub",
|
|
394
|
-
"bin/
|
|
394
|
+
"bin/shakapacker-config" => "Config export binstub"
|
|
395
395
|
}
|
|
396
396
|
|
|
397
397
|
expected_binstubs.each do |path, description|
|
|
@@ -879,49 +879,81 @@ module Shakapacker
|
|
|
879
879
|
|
|
880
880
|
def print_verbose_checks
|
|
881
881
|
puts "\nVerbose diagnostics:"
|
|
882
|
+
print_environment_info
|
|
883
|
+
print_version_info
|
|
884
|
+
print_path_info
|
|
885
|
+
print_config_values
|
|
886
|
+
end
|
|
882
887
|
|
|
883
|
-
|
|
888
|
+
def print_environment_info
|
|
884
889
|
rails_env = defined?(Rails) ? Rails.env : ENV["RAILS_ENV"]
|
|
885
890
|
node_env = ENV["NODE_ENV"]
|
|
886
891
|
puts " • Rails environment: #{rails_env || 'not set'}"
|
|
887
892
|
puts " • Node environment: #{node_env || 'not set'}"
|
|
893
|
+
end
|
|
888
894
|
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
package_json = doctor.send(:read_package_json)
|
|
892
|
-
npm_version = package_json.dig("dependencies", "shakapacker") ||
|
|
893
|
-
package_json.dig("devDependencies", "shakapacker")
|
|
894
|
-
puts " • Shakapacker gem version: #{Shakapacker::VERSION}"
|
|
895
|
-
puts " • Shakapacker npm version: #{npm_version || 'not installed'}"
|
|
896
|
-
end
|
|
895
|
+
def print_version_info
|
|
896
|
+
return unless doctor.send(:package_json_exists?)
|
|
897
897
|
|
|
898
|
-
|
|
898
|
+
package_json = doctor.send(:read_package_json)
|
|
899
|
+
npm_version = package_json.dig("dependencies", "shakapacker") ||
|
|
900
|
+
package_json.dig("devDependencies", "shakapacker")
|
|
901
|
+
puts " • Shakapacker gem version: #{Shakapacker::VERSION}"
|
|
902
|
+
puts " • Shakapacker npm version: #{npm_version || 'not installed'}"
|
|
903
|
+
end
|
|
904
|
+
|
|
905
|
+
def print_path_info
|
|
899
906
|
puts " • Root path: #{doctor.root_path}"
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
907
|
+
return unless doctor.config.config_path.exist?
|
|
908
|
+
|
|
909
|
+
puts " • Cache path: #{doctor.config.cache_path}"
|
|
910
|
+
puts " • Manifest path: #{doctor.config.manifest_path}"
|
|
911
|
+
end
|
|
912
|
+
|
|
913
|
+
def print_config_values
|
|
914
|
+
return unless doctor.config.config_path.exist?
|
|
915
|
+
|
|
916
|
+
puts "\nConfiguration values for '#{doctor.config.env}' environment:"
|
|
917
|
+
config_data = doctor.config.send(:data)
|
|
918
|
+
|
|
919
|
+
if config_data.any?
|
|
920
|
+
print_config_data(config_data)
|
|
921
|
+
else
|
|
922
|
+
puts " (using bundled defaults - no environment-specific config found)"
|
|
903
923
|
end
|
|
924
|
+
end
|
|
904
925
|
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
926
|
+
def print_config_data(config_data)
|
|
927
|
+
config_data.each do |key, value|
|
|
928
|
+
formatted_value = format_config_value(value)
|
|
929
|
+
puts " • #{key}: #{formatted_value}"
|
|
930
|
+
end
|
|
931
|
+
end
|
|
932
|
+
|
|
933
|
+
def format_config_value(value)
|
|
934
|
+
case value
|
|
935
|
+
when Array
|
|
936
|
+
format_array_value(value)
|
|
937
|
+
when Hash
|
|
938
|
+
format_hash_value(value)
|
|
939
|
+
else
|
|
940
|
+
value.inspect
|
|
941
|
+
end
|
|
942
|
+
end
|
|
943
|
+
|
|
944
|
+
def format_array_value(array)
|
|
945
|
+
if array.length > 3
|
|
946
|
+
"#{array.first(3).inspect}... (#{array.length} items)"
|
|
947
|
+
else
|
|
948
|
+
array.inspect
|
|
949
|
+
end
|
|
950
|
+
end
|
|
951
|
+
|
|
952
|
+
def format_hash_value(hash)
|
|
953
|
+
if hash.length > 3
|
|
954
|
+
"{...} (#{hash.length} keys)"
|
|
955
|
+
else
|
|
956
|
+
hash.inspect
|
|
925
957
|
end
|
|
926
958
|
end
|
|
927
959
|
|
|
@@ -981,7 +1013,7 @@ module Shakapacker
|
|
|
981
1013
|
binstubs = [
|
|
982
1014
|
"bin/shakapacker",
|
|
983
1015
|
"bin/shakapacker-dev-server",
|
|
984
|
-
"bin/
|
|
1016
|
+
"bin/shakapacker-config"
|
|
985
1017
|
]
|
|
986
1018
|
|
|
987
1019
|
existing_binstubs = binstubs.select { |b| doctor.root_path.join(b).exist? }
|
|
@@ -1033,49 +1065,61 @@ module Shakapacker
|
|
|
1033
1065
|
|
|
1034
1066
|
item_number = 0
|
|
1035
1067
|
doctor.warnings.each do |warning|
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
when :info then "[INFO]"
|
|
1039
|
-
when :recommended then "[RECOMMENDED]"
|
|
1040
|
-
else ""
|
|
1041
|
-
end
|
|
1042
|
-
|
|
1043
|
-
# Sub-items start with whitespace (indented fix instructions)
|
|
1044
|
-
is_subitem = warning[:message].start_with?(" ")
|
|
1045
|
-
|
|
1046
|
-
if is_subitem
|
|
1047
|
-
# Fix instructions align at column 16 (length of "N. [RECOMMENDED] ")
|
|
1048
|
-
# This ensures all Fix lines align vertically regardless of category
|
|
1049
|
-
subitem_prefix = " " * 15
|
|
1050
|
-
wrapped = wrap_text(warning[:message], 100, subitem_prefix)
|
|
1051
|
-
puts wrapped
|
|
1068
|
+
if subitem?(warning[:message])
|
|
1069
|
+
print_subitem(warning[:message])
|
|
1052
1070
|
else
|
|
1053
1071
|
item_number += 1
|
|
1054
|
-
|
|
1055
|
-
prefix = "#{item_number}. #{category_prefix} "
|
|
1056
|
-
wrapped = wrap_text(warning[:message], 100, prefix)
|
|
1057
|
-
puts wrapped
|
|
1072
|
+
print_main_item(item_number, warning)
|
|
1058
1073
|
end
|
|
1059
1074
|
end
|
|
1060
1075
|
puts ""
|
|
1061
1076
|
end
|
|
1062
1077
|
|
|
1078
|
+
def subitem?(message)
|
|
1079
|
+
message.start_with?(" ")
|
|
1080
|
+
end
|
|
1081
|
+
|
|
1082
|
+
def print_subitem(message)
|
|
1083
|
+
# Fix instructions align at column 16 (length of "N. [RECOMMENDED] ")
|
|
1084
|
+
# This ensures all Fix lines align vertically regardless of category
|
|
1085
|
+
subitem_prefix = " " * 15
|
|
1086
|
+
wrapped = wrap_text(message, 100, subitem_prefix)
|
|
1087
|
+
puts wrapped
|
|
1088
|
+
end
|
|
1089
|
+
|
|
1090
|
+
def print_main_item(item_number, warning)
|
|
1091
|
+
category_prefix = case warning[:category]
|
|
1092
|
+
when :action_required then "[REQUIRED]"
|
|
1093
|
+
when :info then "[INFO]"
|
|
1094
|
+
when :recommended then "[RECOMMENDED]"
|
|
1095
|
+
else
|
|
1096
|
+
""
|
|
1097
|
+
end
|
|
1098
|
+
|
|
1099
|
+
# Format: N. [CATEGORY] Message
|
|
1100
|
+
prefix = "#{item_number}. #{category_prefix} "
|
|
1101
|
+
wrapped = wrap_text(warning[:message], 100, prefix)
|
|
1102
|
+
puts wrapped
|
|
1103
|
+
end
|
|
1104
|
+
|
|
1063
1105
|
def wrap_text(text, max_width, prefix)
|
|
1064
|
-
# Strip leading whitespace from sub-items
|
|
1065
1106
|
text = text.strip
|
|
1066
|
-
|
|
1067
|
-
# Calculate available width for text
|
|
1068
1107
|
available_width = max_width - prefix.length
|
|
1108
|
+
|
|
1069
1109
|
return prefix + text if text.length <= available_width
|
|
1070
1110
|
|
|
1071
|
-
|
|
1111
|
+
lines = build_wrapped_lines(text, available_width)
|
|
1112
|
+
format_wrapped_output(lines, prefix)
|
|
1113
|
+
end
|
|
1114
|
+
|
|
1115
|
+
def build_wrapped_lines(text, available_width)
|
|
1072
1116
|
words = text.split(" ")
|
|
1073
1117
|
lines = []
|
|
1074
1118
|
current_line = []
|
|
1075
1119
|
current_length = 0
|
|
1076
1120
|
|
|
1077
1121
|
words.each do |word|
|
|
1078
|
-
word_length = word.length + (current_line.empty? ? 0 : 1)
|
|
1122
|
+
word_length = word.length + (current_line.empty? ? 0 : 1)
|
|
1079
1123
|
|
|
1080
1124
|
if current_length + word_length <= available_width
|
|
1081
1125
|
current_line << word
|
|
@@ -1086,26 +1130,39 @@ module Shakapacker
|
|
|
1086
1130
|
current_length = word.length
|
|
1087
1131
|
end
|
|
1088
1132
|
end
|
|
1133
|
+
|
|
1089
1134
|
lines << current_line.join(" ") unless current_line.empty?
|
|
1135
|
+
lines
|
|
1136
|
+
end
|
|
1090
1137
|
|
|
1091
|
-
|
|
1138
|
+
def format_wrapped_output(lines, prefix)
|
|
1092
1139
|
result = prefix + lines[0]
|
|
1140
|
+
indent = " " * prefix.length
|
|
1141
|
+
|
|
1093
1142
|
lines[1..].each do |line|
|
|
1094
|
-
result += "\n"
|
|
1143
|
+
result += "\n#{indent}#{line}"
|
|
1095
1144
|
end
|
|
1145
|
+
|
|
1096
1146
|
result
|
|
1097
1147
|
end
|
|
1098
1148
|
|
|
1099
1149
|
def has_dependency_issues?
|
|
1100
|
-
# Check if any issues or warnings are about missing npm/package dependencies
|
|
1101
|
-
# Exclude optional dependencies - only show install instructions for required dependencies
|
|
1102
1150
|
all_messages = doctor.issues + doctor.warnings.map { |w| w[:message] }
|
|
1103
|
-
all_messages.any?
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1151
|
+
all_messages.any? { |msg| dependency_issue?(msg) }
|
|
1152
|
+
end
|
|
1153
|
+
|
|
1154
|
+
def dependency_issue?(message)
|
|
1155
|
+
return false if message.include?("Optional")
|
|
1156
|
+
|
|
1157
|
+
missing_dependency?(message) || not_installed?(message)
|
|
1158
|
+
end
|
|
1159
|
+
|
|
1160
|
+
def missing_dependency?(message)
|
|
1161
|
+
message.include?("Missing") && message.include?("dependency")
|
|
1162
|
+
end
|
|
1163
|
+
|
|
1164
|
+
def not_installed?(message)
|
|
1165
|
+
message.include?("not installed") || message.include?("is not installed")
|
|
1109
1166
|
end
|
|
1110
1167
|
|
|
1111
1168
|
def print_fix_instructions
|
|
@@ -1114,10 +1171,10 @@ module Shakapacker
|
|
|
1114
1171
|
puts " #{package_manager_install_command(package_manager)}"
|
|
1115
1172
|
puts ""
|
|
1116
1173
|
puts "For debugging configuration issues, export your webpack/rspack config:"
|
|
1117
|
-
puts " bin/
|
|
1174
|
+
puts " bin/shakapacker-config --doctor"
|
|
1118
1175
|
puts " (Exports annotated YAML configs for dev and production - best for troubleshooting)"
|
|
1119
1176
|
puts ""
|
|
1120
|
-
puts " See 'bin/
|
|
1177
|
+
puts " See 'bin/shakapacker-config --help' for more options"
|
|
1121
1178
|
end
|
|
1122
1179
|
|
|
1123
1180
|
def package_manager_install_command(manager)
|
data/lib/shakapacker/instance.rb
CHANGED
|
@@ -1,10 +1,43 @@
|
|
|
1
1
|
require "pathname"
|
|
2
2
|
|
|
3
|
+
# Represents a single instance of Shakapacker configuration and state
|
|
4
|
+
#
|
|
5
|
+
# An instance encapsulates all the configuration, compilation, and manifest
|
|
6
|
+
# lookup functionality for a specific Rails application. Most applications
|
|
7
|
+
# will use the shared instance accessible via {Shakapacker.instance}, but
|
|
8
|
+
# multiple instances can be created for testing or advanced scenarios.
|
|
9
|
+
#
|
|
10
|
+
# @example Using the default instance
|
|
11
|
+
# instance = Shakapacker::Instance.new
|
|
12
|
+
# instance.config.source_path
|
|
13
|
+
# instance.manifest.lookup("application.js")
|
|
14
|
+
#
|
|
15
|
+
# @example Creating an instance with custom paths
|
|
16
|
+
# instance = Shakapacker::Instance.new(
|
|
17
|
+
# root_path: "/path/to/app",
|
|
18
|
+
# config_path: "/custom/config.yml"
|
|
19
|
+
# )
|
|
3
20
|
class Shakapacker::Instance
|
|
21
|
+
# The shared logger used by all Shakapacker instances
|
|
22
|
+
# @return [ActiveSupport::TaggedLogging] the logger
|
|
4
23
|
cattr_accessor(:logger) { ActiveSupport::TaggedLogging.new(ActiveSupport::Logger.new(STDOUT)) }
|
|
5
24
|
|
|
6
|
-
|
|
25
|
+
# The root path of the application
|
|
26
|
+
# @return [Pathname] the application root path
|
|
27
|
+
attr_reader :root_path
|
|
7
28
|
|
|
29
|
+
# The path to the Shakapacker configuration file
|
|
30
|
+
# @return [Pathname] the config file path
|
|
31
|
+
attr_reader :config_path
|
|
32
|
+
|
|
33
|
+
# Creates a new Shakapacker instance
|
|
34
|
+
#
|
|
35
|
+
# @param root_path [String, Pathname, nil] the application root path.
|
|
36
|
+
# Defaults to Rails.root if Rails is defined, otherwise uses current directory
|
|
37
|
+
# @param config_path [String, Pathname, nil] the path to shakapacker.yml.
|
|
38
|
+
# Can also be set via SHAKAPACKER_CONFIG environment variable.
|
|
39
|
+
# Defaults to +config/shakapacker.yml+ within the root_path
|
|
40
|
+
# @return [Shakapacker::Instance] the new instance
|
|
8
41
|
def initialize(root_path: nil, config_path: nil)
|
|
9
42
|
# Use Rails.root if Rails is defined and no root_path is provided
|
|
10
43
|
@root_path = root_path || (defined?(Rails) && Rails&.root) || Pathname.new(Dir.pwd)
|
|
@@ -15,10 +48,23 @@ class Shakapacker::Instance
|
|
|
15
48
|
@config_path = Pathname.new(ENV["SHAKAPACKER_CONFIG"] || config_path || default_config_path)
|
|
16
49
|
end
|
|
17
50
|
|
|
51
|
+
# Returns the current Rails environment as a StringInquirer
|
|
52
|
+
#
|
|
53
|
+
# This allows for convenient environment checking:
|
|
54
|
+
# env.development? # => true/false
|
|
55
|
+
# env.production? # => true/false
|
|
56
|
+
#
|
|
57
|
+
# @return [ActiveSupport::StringInquirer] the environment
|
|
18
58
|
def env
|
|
19
59
|
@env ||= Shakapacker::Env.inquire self
|
|
20
60
|
end
|
|
21
61
|
|
|
62
|
+
# Returns the configuration object for this instance
|
|
63
|
+
#
|
|
64
|
+
# The configuration is loaded from the shakapacker.yml file and provides
|
|
65
|
+
# access to all settings like source paths, output paths, and compilation options.
|
|
66
|
+
#
|
|
67
|
+
# @return [Shakapacker::Configuration] the configuration
|
|
22
68
|
def config
|
|
23
69
|
@config ||= Shakapacker::Configuration.new(
|
|
24
70
|
root_path: root_path,
|
|
@@ -27,26 +73,64 @@ class Shakapacker::Instance
|
|
|
27
73
|
)
|
|
28
74
|
end
|
|
29
75
|
|
|
76
|
+
# Returns the compiler strategy for determining staleness
|
|
77
|
+
#
|
|
78
|
+
# The strategy (mtime or digest) determines how Shakapacker decides whether
|
|
79
|
+
# assets need recompilation.
|
|
80
|
+
#
|
|
81
|
+
# @return [Shakapacker::CompilerStrategy] the compiler strategy
|
|
82
|
+
# @api private
|
|
30
83
|
def strategy
|
|
31
84
|
@strategy ||= Shakapacker::CompilerStrategy.from_config
|
|
32
85
|
end
|
|
33
86
|
|
|
87
|
+
# Returns the compiler for this instance
|
|
88
|
+
#
|
|
89
|
+
# The compiler is responsible for executing webpack/rspack to compile assets.
|
|
90
|
+
#
|
|
91
|
+
# @return [Shakapacker::Compiler] the compiler
|
|
34
92
|
def compiler
|
|
35
93
|
@compiler ||= Shakapacker::Compiler.new self
|
|
36
94
|
end
|
|
37
95
|
|
|
96
|
+
# Returns the development server instance
|
|
97
|
+
#
|
|
98
|
+
# The dev server instance can query the status of the webpack-dev-server,
|
|
99
|
+
# including whether it's running, its host/port, and configuration.
|
|
100
|
+
#
|
|
101
|
+
# @return [Shakapacker::DevServer] the dev server
|
|
38
102
|
def dev_server
|
|
39
103
|
@dev_server ||= Shakapacker::DevServer.new config
|
|
40
104
|
end
|
|
41
105
|
|
|
106
|
+
# Returns the manifest for looking up compiled assets
|
|
107
|
+
#
|
|
108
|
+
# The manifest reads the manifest.json file produced by webpack/rspack
|
|
109
|
+
# and provides methods to look up the compiled paths for source files.
|
|
110
|
+
#
|
|
111
|
+
# @return [Shakapacker::Manifest] the manifest
|
|
42
112
|
def manifest
|
|
43
113
|
@manifest ||= Shakapacker::Manifest.new self
|
|
44
114
|
end
|
|
45
115
|
|
|
116
|
+
# Returns the commands instance for build operations
|
|
117
|
+
#
|
|
118
|
+
# The commands object provides methods for bootstrapping, cleaning,
|
|
119
|
+
# clobbering, and compiling assets.
|
|
120
|
+
#
|
|
121
|
+
# @return [Shakapacker::Commands] the commands
|
|
46
122
|
def commands
|
|
47
123
|
@commands ||= Shakapacker::Commands.new self
|
|
48
124
|
end
|
|
49
125
|
|
|
126
|
+
# Returns whether CSS should be inlined by the dev server
|
|
127
|
+
#
|
|
128
|
+
# CSS inlining is enabled when:
|
|
129
|
+
# - The dev server has inline_css enabled
|
|
130
|
+
# - Hot Module Replacement (HMR) is enabled
|
|
131
|
+
# - The dev server is currently running
|
|
132
|
+
#
|
|
133
|
+
# @return [Boolean] true if CSS should be inlined
|
|
50
134
|
def inlining_css?
|
|
51
135
|
dev_server.inline_css? && dev_server.hmr? && dev_server.running?
|
|
52
136
|
end
|
data/lib/shakapacker/manifest.rb
CHANGED
|
@@ -1,23 +1,70 @@
|
|
|
1
|
-
#
|
|
2
|
-
# This allows javascript_pack_tag, stylesheet_pack_tag, asset_pack_path to take a reference to,
|
|
3
|
-
# say, "calendar.js" or "calendar.css" and turn it into "/packs/calendar-1016838bab065ae1e314.js" or
|
|
4
|
-
# "/packs/calendar-1016838bab065ae1e314.css".
|
|
1
|
+
# Manifest for looking up compiled asset paths
|
|
5
2
|
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
3
|
+
# The manifest reads the +manifest.json+ file produced by webpack/rspack during
|
|
4
|
+
# compilation and provides methods to look up the compiled (digested) paths for
|
|
5
|
+
# source files.
|
|
6
|
+
#
|
|
7
|
+
# This allows view helpers like +javascript_pack_tag+, +stylesheet_pack_tag+, and
|
|
8
|
+
# +asset_pack_path+ to take a reference to a source file (e.g., "calendar.js")
|
|
9
|
+
# and turn it into the compiled path with digest (e.g., "/packs/calendar-1016838bab065ae1e314.js").
|
|
10
|
+
#
|
|
11
|
+
# == Automatic Compilation
|
|
12
|
+
#
|
|
13
|
+
# When the configuration has +compile: true+ in shakapacker.yml, any lookups will
|
|
14
|
+
# automatically trigger compilation if the assets are stale. This is typically
|
|
15
|
+
# enabled in development and disabled in production.
|
|
16
|
+
#
|
|
17
|
+
# == Caching
|
|
18
|
+
#
|
|
19
|
+
# The manifest can cache the loaded data in memory when +cache_manifest: true+ is
|
|
20
|
+
# set in the configuration. This improves performance in production by avoiding
|
|
21
|
+
# repeated file reads.
|
|
22
|
+
#
|
|
23
|
+
# @example Looking up assets
|
|
24
|
+
# manifest = Shakapacker.manifest
|
|
25
|
+
# manifest.lookup("application.js")
|
|
26
|
+
# #=> "/packs/application-abc123.js"
|
|
27
|
+
#
|
|
28
|
+
# manifest.lookup!("missing.js")
|
|
29
|
+
# #=> raises Shakapacker::Manifest::MissingEntryError
|
|
30
|
+
#
|
|
31
|
+
# @see Shakapacker::Helper
|
|
8
32
|
class Shakapacker::Manifest
|
|
33
|
+
# Raised when an asset cannot be found in the manifest
|
|
9
34
|
class MissingEntryError < StandardError; end
|
|
10
35
|
|
|
11
36
|
delegate :config, :compiler, :dev_server, to: :@instance
|
|
12
37
|
|
|
38
|
+
# Creates a new manifest instance
|
|
39
|
+
#
|
|
40
|
+
# @param instance [Shakapacker::Instance] the Shakapacker instance
|
|
41
|
+
# @return [Shakapacker::Manifest] the new manifest
|
|
13
42
|
def initialize(instance)
|
|
14
43
|
@instance = instance
|
|
15
44
|
end
|
|
16
45
|
|
|
46
|
+
# Reloads the manifest data from disk
|
|
47
|
+
#
|
|
48
|
+
# Forces a fresh read of the manifest.json file, bypassing any cache.
|
|
49
|
+
# This is useful when you know the manifest has been updated.
|
|
50
|
+
#
|
|
51
|
+
# @return [Hash] the loaded manifest data
|
|
17
52
|
def refresh
|
|
18
53
|
@data = load
|
|
19
54
|
end
|
|
20
55
|
|
|
56
|
+
# Looks up an entry point with all its chunks (split code)
|
|
57
|
+
#
|
|
58
|
+
# This method is used when you need to load all chunks for a pack that has
|
|
59
|
+
# been split via code splitting. It returns an array of asset paths for the
|
|
60
|
+
# main entry and all its dynamic imports.
|
|
61
|
+
#
|
|
62
|
+
# @param name [String] the entry point name (e.g., "application")
|
|
63
|
+
# @param pack_type [Hash] options hash with :type key (:javascript, :stylesheet, etc.)
|
|
64
|
+
# @return [Array<String>, nil] array of asset paths, or nil if not found
|
|
65
|
+
# @example
|
|
66
|
+
# manifest.lookup_pack_with_chunks("application", type: :javascript)
|
|
67
|
+
# #=> ["/packs/runtime-abc123.js", "/packs/application-def456.js"]
|
|
21
68
|
def lookup_pack_with_chunks(name, pack_type = {})
|
|
22
69
|
compile if compiling?
|
|
23
70
|
|
|
@@ -28,23 +75,50 @@ class Shakapacker::Manifest
|
|
|
28
75
|
nil
|
|
29
76
|
end
|
|
30
77
|
|
|
78
|
+
# Like {#lookup_pack_with_chunks}, but raises an error if not found
|
|
79
|
+
#
|
|
80
|
+
# @param name [String] the entry point name
|
|
81
|
+
# @param pack_type [Hash] options hash with :type key
|
|
82
|
+
# @return [Array<String>] array of asset paths
|
|
83
|
+
# @raise [MissingEntryError] if the entry point is not found in the manifest
|
|
31
84
|
def lookup_pack_with_chunks!(name, pack_type = {})
|
|
32
85
|
lookup_pack_with_chunks(name, pack_type) || handle_missing_entry(name, pack_type)
|
|
33
86
|
end
|
|
34
87
|
|
|
35
|
-
#
|
|
36
|
-
#
|
|
88
|
+
# Looks up the compiled path for a given asset
|
|
89
|
+
#
|
|
90
|
+
# Computes the relative path for a Shakapacker asset using the manifest.json file.
|
|
91
|
+
# If automatic compilation is enabled and the assets are stale, triggers a
|
|
92
|
+
# compilation before looking up the path.
|
|
37
93
|
#
|
|
38
|
-
#
|
|
94
|
+
# @param name [String] the source file name (e.g., "calendar.js" or "calendar")
|
|
95
|
+
# @param pack_type [Hash] options hash with :type key (:javascript, :stylesheet, etc.).
|
|
96
|
+
# If not specified, the extension from the name is used.
|
|
97
|
+
# @return [String, nil] the compiled asset path, or nil if not found
|
|
98
|
+
# @example
|
|
99
|
+
# Shakapacker.manifest.lookup('calendar.js')
|
|
100
|
+
# #=> "/packs/calendar-1016838bab065ae1e122.js"
|
|
39
101
|
#
|
|
40
|
-
# Shakapacker.manifest.lookup('calendar
|
|
102
|
+
# Shakapacker.manifest.lookup('calendar', type: :javascript)
|
|
103
|
+
# #=> "/packs/calendar-1016838bab065ae1e122.js"
|
|
41
104
|
def lookup(name, pack_type = {})
|
|
42
105
|
compile if compiling?
|
|
43
106
|
|
|
44
107
|
find(full_pack_name(name, pack_type[:type]))
|
|
45
108
|
end
|
|
46
109
|
|
|
47
|
-
# Like lookup,
|
|
110
|
+
# Like {#lookup}, but raises an error if the asset is not found
|
|
111
|
+
#
|
|
112
|
+
# @param name [String] the source file name
|
|
113
|
+
# @param pack_type [Hash] options hash with :type key
|
|
114
|
+
# @return [String] the compiled asset path
|
|
115
|
+
# @raise [MissingEntryError] if the asset is not found in the manifest
|
|
116
|
+
# @example
|
|
117
|
+
# Shakapacker.manifest.lookup!('calendar.js')
|
|
118
|
+
# #=> "/packs/calendar-1016838bab065ae1e122.js"
|
|
119
|
+
#
|
|
120
|
+
# Shakapacker.manifest.lookup!('missing.js')
|
|
121
|
+
# #=> raises MissingEntryError
|
|
48
122
|
def lookup!(name, pack_type = {})
|
|
49
123
|
lookup(name, pack_type) || handle_missing_entry(name, pack_type)
|
|
50
124
|
end
|
data/lib/shakapacker/runner.rb
CHANGED
|
@@ -29,8 +29,10 @@ module Shakapacker
|
|
|
29
29
|
$stdout.sync = true
|
|
30
30
|
|
|
31
31
|
# Show Shakapacker help and exit (don't call bundler)
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
# Support --help, -h, and --help=verbose formats
|
|
33
|
+
help_verbose = argv.any? { |arg| arg == "--help=verbose" }
|
|
34
|
+
if argv.include?("--help") || argv.include?("-h") || help_verbose
|
|
35
|
+
print_help(verbose: help_verbose)
|
|
34
36
|
exit(0)
|
|
35
37
|
elsif argv.include?("--version") || argv.include?("-v")
|
|
36
38
|
print_version
|
|
@@ -310,7 +312,7 @@ module Shakapacker
|
|
|
310
312
|
$stderr.puts "Current configured path: #{config_dir}"
|
|
311
313
|
end
|
|
312
314
|
|
|
313
|
-
def self.print_help
|
|
315
|
+
def self.print_help(verbose: false)
|
|
314
316
|
puts <<~HELP
|
|
315
317
|
================================================================================
|
|
316
318
|
SHAKAPACKER - Rails Webpack/Rspack Integration
|
|
@@ -320,6 +322,7 @@ module Shakapacker
|
|
|
320
322
|
|
|
321
323
|
Shakapacker-specific options:
|
|
322
324
|
-h, --help Show this help message
|
|
325
|
+
--help=verbose Show verbose help including all bundler options
|
|
323
326
|
-v, --version Show Shakapacker version
|
|
324
327
|
--debug-shakapacker Enable Node.js debugging (--inspect-brk)
|
|
325
328
|
--trace-deprecation Show stack traces for deprecations
|
|
@@ -347,7 +350,7 @@ module Shakapacker
|
|
|
347
350
|
|
|
348
351
|
HELP
|
|
349
352
|
|
|
350
|
-
print_bundler_help
|
|
353
|
+
print_bundler_help(verbose: verbose)
|
|
351
354
|
|
|
352
355
|
puts <<~HELP
|
|
353
356
|
|
|
@@ -366,8 +369,9 @@ module Shakapacker
|
|
|
366
369
|
HELP
|
|
367
370
|
end
|
|
368
371
|
|
|
369
|
-
def self.print_bundler_help
|
|
370
|
-
|
|
372
|
+
def self.print_bundler_help(verbose: false)
|
|
373
|
+
help_flag = verbose ? "--help=verbose" : "--help"
|
|
374
|
+
bundler_type, bundler_help = get_bundler_help(help_flag)
|
|
371
375
|
|
|
372
376
|
if bundler_help
|
|
373
377
|
bundler_name = bundler_type == :rspack ? "RSPACK" : "WEBPACK"
|
|
@@ -390,8 +394,8 @@ module Shakapacker
|
|
|
390
394
|
end
|
|
391
395
|
end
|
|
392
396
|
|
|
393
|
-
def self.get_bundler_help
|
|
394
|
-
execute_bundler_command(
|
|
397
|
+
def self.get_bundler_help(help_flag = "--help")
|
|
398
|
+
execute_bundler_command(help_flag) { |stdout| stdout }
|
|
395
399
|
end
|
|
396
400
|
|
|
397
401
|
# Filter bundler help output to remove Shakapacker-managed options
|
|
@@ -61,13 +61,13 @@ module Shakapacker
|
|
|
61
61
|
keepClassNames: true,
|
|
62
62
|
transform: {
|
|
63
63
|
react: {
|
|
64
|
-
runtime:
|
|
65
|
-
refresh: env.isDevelopment && env.runningWebpackDevServer
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
}
|
|
64
|
+
runtime: 'automatic',
|
|
65
|
+
refresh: env.isDevelopment && env.runningWebpackDevServer,
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
71
|
JS
|
|
72
72
|
|
|
73
73
|
def initialize(root_path, logger: nil)
|
data/lib/shakapacker/version.rb
CHANGED