shakapacker 9.0.0.beta.4 → 9.0.0.beta.6
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/.eslintignore +1 -0
- data/.github/workflows/claude-code-review.yml +1 -1
- data/.github/workflows/dummy.yml +4 -0
- data/.github/workflows/generator.yml +7 -0
- data/.github/workflows/node.yml +22 -0
- data/.github/workflows/ruby.yml +11 -0
- data/.github/workflows/test-bundlers.yml +27 -9
- data/.gitignore +20 -0
- data/.yalcignore +26 -0
- data/CHANGELOG.md +58 -40
- data/CONTRIBUTING.md +64 -0
- data/Gemfile.lock +1 -1
- data/README.md +80 -1
- data/docs/optional-peer-dependencies.md +198 -0
- data/docs/typescript.md +99 -0
- data/docs/v9_upgrade.md +79 -2
- data/lib/install/template.rb +8 -1
- data/lib/shakapacker/configuration.rb +58 -1
- data/lib/shakapacker/doctor.rb +751 -0
- data/lib/shakapacker/swc_migrator.rb +292 -0
- data/lib/shakapacker/version.rb +1 -1
- data/lib/shakapacker.rb +1 -0
- data/lib/tasks/shakapacker/doctor.rake +8 -0
- data/lib/tasks/shakapacker/migrate_to_swc.rake +13 -0
- data/lib/tasks/shakapacker.rake +1 -0
- data/package/config.ts +162 -0
- data/package/{dev_server.js → dev_server.ts} +8 -5
- data/package/env.ts +67 -0
- data/package/environments/base.js +94 -117
- data/package/environments/base.ts +138 -0
- data/package/index.d.ts +3 -150
- data/package/{index.js → index.ts} +18 -8
- data/package/loaders.d.ts +28 -0
- data/package/types.ts +108 -0
- data/package/utils/configPath.ts +6 -0
- data/package/utils/{debug.js → debug.ts} +7 -7
- data/package/utils/defaultConfigPath.ts +4 -0
- data/package/utils/errorHelpers.ts +77 -0
- data/package/utils/{getStyleRule.js → getStyleRule.ts} +17 -20
- data/package/utils/helpers.ts +85 -0
- data/package/utils/{inliningCss.js → inliningCss.ts} +3 -3
- data/package/utils/{requireOrError.js → requireOrError.ts} +2 -2
- data/package/utils/snakeToCamelCase.ts +5 -0
- data/package/utils/typeGuards.ts +228 -0
- data/package/utils/{validateDependencies.js → validateDependencies.ts} +4 -4
- data/package/webpack-types.d.ts +33 -0
- data/package/webpackDevServerConfig.ts +117 -0
- data/package.json +112 -4
- data/test/peer-dependencies.sh +85 -0
- data/test/typescript/build.test.js +117 -0
- data/tsconfig.json +39 -0
- data/yarn.lock +1 -1
- metadata +34 -17
- data/package/config.js +0 -80
- data/package/env.js +0 -48
- data/package/utils/configPath.js +0 -4
- data/package/utils/defaultConfigPath.js +0 -2
- data/package/utils/helpers.js +0 -127
- data/package/utils/snakeToCamelCase.js +0 -5
- data/package/utils/validateCssModulesConfig.js +0 -91
- data/package/webpackDevServerConfig.js +0 -73
@@ -0,0 +1,292 @@
|
|
1
|
+
require "yaml"
|
2
|
+
require "json"
|
3
|
+
require "fileutils"
|
4
|
+
require "logger"
|
5
|
+
require "pathname"
|
6
|
+
|
7
|
+
module Shakapacker
|
8
|
+
class SwcMigrator
|
9
|
+
attr_reader :root_path, :logger
|
10
|
+
|
11
|
+
BABEL_PACKAGES = [
|
12
|
+
"@babel/core",
|
13
|
+
"@babel/plugin-proposal-class-properties",
|
14
|
+
"@babel/plugin-proposal-object-rest-spread",
|
15
|
+
"@babel/plugin-syntax-dynamic-import",
|
16
|
+
"@babel/plugin-transform-destructuring",
|
17
|
+
"@babel/plugin-transform-regenerator",
|
18
|
+
"@babel/plugin-transform-runtime",
|
19
|
+
"@babel/preset-env",
|
20
|
+
"@babel/preset-react",
|
21
|
+
"@babel/preset-typescript",
|
22
|
+
"@babel/runtime",
|
23
|
+
"babel-loader",
|
24
|
+
"babel-plugin-macros",
|
25
|
+
"babel-plugin-transform-react-remove-prop-types"
|
26
|
+
].freeze
|
27
|
+
|
28
|
+
SWC_PACKAGES = {
|
29
|
+
"@swc/core" => "^1.7.39",
|
30
|
+
"swc-loader" => "^0.2.6"
|
31
|
+
}.freeze
|
32
|
+
|
33
|
+
DEFAULT_SWCRC_CONFIG = {
|
34
|
+
"jsc" => {
|
35
|
+
"parser" => {
|
36
|
+
"syntax" => "ecmascript",
|
37
|
+
"jsx" => true,
|
38
|
+
"dynamicImport" => true
|
39
|
+
},
|
40
|
+
"transform" => {
|
41
|
+
"react" => {
|
42
|
+
"runtime" => "automatic"
|
43
|
+
}
|
44
|
+
},
|
45
|
+
"target" => "es2015"
|
46
|
+
},
|
47
|
+
"module" => {
|
48
|
+
"type" => "es6"
|
49
|
+
}
|
50
|
+
}.freeze
|
51
|
+
|
52
|
+
def initialize(root_path, logger: nil)
|
53
|
+
@root_path = Pathname.new(root_path)
|
54
|
+
@logger = logger || Logger.new($stdout)
|
55
|
+
end
|
56
|
+
|
57
|
+
def migrate_to_swc(run_installer: true)
|
58
|
+
logger.info "🔄 Starting migration from Babel to SWC..."
|
59
|
+
|
60
|
+
results = {
|
61
|
+
config_updated: update_shakapacker_config,
|
62
|
+
packages_installed: install_swc_packages,
|
63
|
+
swcrc_created: create_swcrc,
|
64
|
+
babel_packages_found: find_babel_packages
|
65
|
+
}
|
66
|
+
|
67
|
+
logger.info "🎉 Migration to SWC complete!"
|
68
|
+
logger.info " Note: SWC is approximately 20x faster than Babel for transpilation."
|
69
|
+
logger.info " Please test your application thoroughly after migration."
|
70
|
+
|
71
|
+
# Show cleanup recommendations if babel packages found
|
72
|
+
if results[:babel_packages_found].any?
|
73
|
+
logger.info "\n🧹 Cleanup Recommendations:"
|
74
|
+
logger.info " Found the following Babel packages in your package.json:"
|
75
|
+
results[:babel_packages_found].each do |package|
|
76
|
+
logger.info " - #{package}"
|
77
|
+
end
|
78
|
+
logger.info "\n To remove them, run:"
|
79
|
+
logger.info " rails shakapacker:clean_babel_packages"
|
80
|
+
end
|
81
|
+
|
82
|
+
# Suggest running doctor to verify configuration
|
83
|
+
logger.info "\n🩺 Run 'rails shakapacker:doctor' to verify your configuration"
|
84
|
+
|
85
|
+
# Run package manager install if packages were added
|
86
|
+
if run_installer && results[:packages_installed].any?
|
87
|
+
run_package_manager_install
|
88
|
+
end
|
89
|
+
|
90
|
+
results
|
91
|
+
end
|
92
|
+
|
93
|
+
def clean_babel_packages(run_installer: true)
|
94
|
+
logger.info "🧹 Removing Babel packages..."
|
95
|
+
|
96
|
+
package_json_path = root_path.join("package.json")
|
97
|
+
unless package_json_path.exist?
|
98
|
+
logger.error "❌ No package.json found"
|
99
|
+
return { removed_packages: [], config_files_deleted: [] }
|
100
|
+
end
|
101
|
+
|
102
|
+
removed_packages = remove_babel_from_package_json(package_json_path)
|
103
|
+
deleted_files = delete_babel_config_files
|
104
|
+
|
105
|
+
if removed_packages.any?
|
106
|
+
logger.info "✅ Babel packages removed successfully!"
|
107
|
+
run_package_manager_install if run_installer
|
108
|
+
else
|
109
|
+
logger.info "ℹ️ No Babel packages found to remove"
|
110
|
+
end
|
111
|
+
|
112
|
+
{ removed_packages: removed_packages, config_files_deleted: deleted_files }
|
113
|
+
end
|
114
|
+
|
115
|
+
def find_babel_packages
|
116
|
+
package_json_path = root_path.join("package.json")
|
117
|
+
return [] unless package_json_path.exist?
|
118
|
+
|
119
|
+
begin
|
120
|
+
package_json = JSON.parse(File.read(package_json_path))
|
121
|
+
dependencies = package_json["dependencies"] || {}
|
122
|
+
dev_dependencies = package_json["devDependencies"] || {}
|
123
|
+
all_deps = dependencies.merge(dev_dependencies)
|
124
|
+
|
125
|
+
found_packages = BABEL_PACKAGES.select { |pkg| all_deps.key?(pkg) }
|
126
|
+
found_packages
|
127
|
+
rescue JSON::ParserError => e
|
128
|
+
logger.error "Failed to parse package.json: #{e.message}"
|
129
|
+
[]
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
private
|
134
|
+
|
135
|
+
def update_shakapacker_config
|
136
|
+
config_path = root_path.join("config/shakapacker.yml")
|
137
|
+
return false unless config_path.exist?
|
138
|
+
|
139
|
+
logger.info "📝 Updating shakapacker.yml..."
|
140
|
+
config = begin
|
141
|
+
YAML.load_file(config_path, aliases: true)
|
142
|
+
rescue ArgumentError
|
143
|
+
YAML.load_file(config_path)
|
144
|
+
end
|
145
|
+
|
146
|
+
config.each do |env, settings|
|
147
|
+
next unless settings.is_a?(Hash)
|
148
|
+
|
149
|
+
if settings["babel"]
|
150
|
+
logger.info " - Removing babel config from #{env} environment"
|
151
|
+
settings.delete("babel")
|
152
|
+
end
|
153
|
+
|
154
|
+
settings["swc"] = true
|
155
|
+
logger.info " - Enabled SWC for #{env} environment"
|
156
|
+
end
|
157
|
+
|
158
|
+
File.write(config_path, config.to_yaml)
|
159
|
+
logger.info "✅ shakapacker.yml updated"
|
160
|
+
true
|
161
|
+
rescue StandardError => e
|
162
|
+
logger.error "Failed to update config: #{e.message}"
|
163
|
+
false
|
164
|
+
end
|
165
|
+
|
166
|
+
def install_swc_packages
|
167
|
+
package_json_path = root_path.join("package.json")
|
168
|
+
return {} unless package_json_path.exist?
|
169
|
+
|
170
|
+
logger.info "📦 Installing SWC dependencies..."
|
171
|
+
package_json = JSON.parse(File.read(package_json_path))
|
172
|
+
|
173
|
+
dependencies = package_json["dependencies"] || {}
|
174
|
+
dev_dependencies = package_json["devDependencies"] || {}
|
175
|
+
installed = {}
|
176
|
+
|
177
|
+
SWC_PACKAGES.each do |package, version|
|
178
|
+
unless dependencies[package] || dev_dependencies[package]
|
179
|
+
logger.info " - Adding #{package}@#{version}"
|
180
|
+
dev_dependencies[package] = version
|
181
|
+
installed[package] = version
|
182
|
+
else
|
183
|
+
logger.info " - #{package} already installed"
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
if installed.any?
|
188
|
+
package_json["devDependencies"] = dev_dependencies
|
189
|
+
File.write(package_json_path, JSON.pretty_generate(package_json) + "\n")
|
190
|
+
logger.info "✅ package.json updated with SWC dependencies"
|
191
|
+
end
|
192
|
+
|
193
|
+
installed
|
194
|
+
rescue StandardError => e
|
195
|
+
logger.error "Failed to install packages: #{e.message}"
|
196
|
+
{}
|
197
|
+
end
|
198
|
+
|
199
|
+
def create_swcrc
|
200
|
+
swcrc_path = root_path.join(".swcrc")
|
201
|
+
if swcrc_path.exist?
|
202
|
+
logger.info "ℹ️ .swcrc already exists"
|
203
|
+
return false
|
204
|
+
end
|
205
|
+
|
206
|
+
logger.info "📄 Creating .swcrc configuration..."
|
207
|
+
File.write(swcrc_path, JSON.pretty_generate(DEFAULT_SWCRC_CONFIG) + "\n")
|
208
|
+
logger.info "✅ .swcrc created"
|
209
|
+
true
|
210
|
+
rescue StandardError => e
|
211
|
+
logger.error "Failed to create .swcrc: #{e.message}"
|
212
|
+
false
|
213
|
+
end
|
214
|
+
|
215
|
+
def remove_babel_from_package_json(package_json_path)
|
216
|
+
package_json = JSON.parse(File.read(package_json_path))
|
217
|
+
dependencies = package_json["dependencies"] || {}
|
218
|
+
dev_dependencies = package_json["devDependencies"] || {}
|
219
|
+
removed_packages = []
|
220
|
+
|
221
|
+
BABEL_PACKAGES.each do |package|
|
222
|
+
if dependencies.delete(package)
|
223
|
+
removed_packages << package
|
224
|
+
logger.info " - Removed #{package} from dependencies"
|
225
|
+
end
|
226
|
+
if dev_dependencies.delete(package)
|
227
|
+
removed_packages << package
|
228
|
+
logger.info " - Removed #{package} from devDependencies"
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
if removed_packages.any?
|
233
|
+
package_json["dependencies"] = dependencies
|
234
|
+
package_json["devDependencies"] = dev_dependencies
|
235
|
+
File.write(package_json_path, JSON.pretty_generate(package_json) + "\n")
|
236
|
+
logger.info "✅ package.json updated"
|
237
|
+
end
|
238
|
+
|
239
|
+
removed_packages.uniq
|
240
|
+
rescue StandardError => e
|
241
|
+
logger.error "Failed to remove packages: #{e.message}"
|
242
|
+
[]
|
243
|
+
end
|
244
|
+
|
245
|
+
def delete_babel_config_files
|
246
|
+
deleted_files = []
|
247
|
+
babel_config_files = [".babelrc", "babel.config.js", ".babelrc.js", "babel.config.json"]
|
248
|
+
|
249
|
+
babel_config_files.each do |file|
|
250
|
+
file_path = root_path.join(file)
|
251
|
+
if file_path.exist?
|
252
|
+
logger.info " - Removing #{file}"
|
253
|
+
File.delete(file_path)
|
254
|
+
deleted_files << file
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
deleted_files
|
259
|
+
rescue StandardError => e
|
260
|
+
logger.error "Failed to delete config files: #{e.message}"
|
261
|
+
[]
|
262
|
+
end
|
263
|
+
|
264
|
+
def run_package_manager_install
|
265
|
+
logger.info "\n🔧 Running npm/yarn install..."
|
266
|
+
|
267
|
+
yarn_lock = root_path.join("yarn.lock")
|
268
|
+
pnpm_lock = root_path.join("pnpm-lock.yaml")
|
269
|
+
|
270
|
+
if yarn_lock.exist?
|
271
|
+
system("yarn install")
|
272
|
+
elsif pnpm_lock.exist?
|
273
|
+
system("pnpm install")
|
274
|
+
else
|
275
|
+
system("npm install")
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
def package_manager
|
280
|
+
yarn_lock = root_path.join("yarn.lock")
|
281
|
+
pnpm_lock = root_path.join("pnpm-lock.yaml")
|
282
|
+
|
283
|
+
if yarn_lock.exist?
|
284
|
+
"yarn"
|
285
|
+
elsif pnpm_lock.exist?
|
286
|
+
"pnpm"
|
287
|
+
else
|
288
|
+
"npm"
|
289
|
+
end
|
290
|
+
end
|
291
|
+
end
|
292
|
+
end
|
data/lib/shakapacker/version.rb
CHANGED
data/lib/shakapacker.rb
CHANGED
@@ -44,6 +44,7 @@ require_relative "shakapacker/manifest"
|
|
44
44
|
require_relative "shakapacker/compiler"
|
45
45
|
require_relative "shakapacker/commands"
|
46
46
|
require_relative "shakapacker/dev_server"
|
47
|
+
require_relative "shakapacker/doctor"
|
47
48
|
require_relative "shakapacker/deprecation_helper"
|
48
49
|
|
49
50
|
require_relative "shakapacker/railtie" if defined?(Rails)
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require "shakapacker/swc_migrator"
|
2
|
+
|
3
|
+
namespace :shakapacker do
|
4
|
+
desc "Migrate from Babel to SWC transpiler"
|
5
|
+
task :migrate_to_swc do
|
6
|
+
Shakapacker::SwcMigrator.new(Rails.root).migrate_to_swc
|
7
|
+
end
|
8
|
+
|
9
|
+
desc "Remove Babel packages after migrating to SWC"
|
10
|
+
task :clean_babel_packages do
|
11
|
+
Shakapacker::SwcMigrator.new(Rails.root).clean_babel_packages
|
12
|
+
end
|
13
|
+
end
|
data/lib/tasks/shakapacker.rake
CHANGED
@@ -9,6 +9,7 @@ tasks = {
|
|
9
9
|
"shakapacker:check_binstubs" => "Verifies that bin/shakapacker is present",
|
10
10
|
"shakapacker:binstubs" => "Installs Shakapacker binstubs in this application",
|
11
11
|
"shakapacker:verify_install" => "Verifies if Shakapacker is installed",
|
12
|
+
"shakapacker:doctor" => "Checks for configuration issues and missing dependencies"
|
12
13
|
}.freeze
|
13
14
|
|
14
15
|
desc "Lists all available tasks in Shakapacker"
|
data/package/config.ts
ADDED
@@ -0,0 +1,162 @@
|
|
1
|
+
import { resolve } from "path"
|
2
|
+
import { load } from "js-yaml"
|
3
|
+
import { existsSync, readFileSync } from "fs"
|
4
|
+
import { merge } from "webpack-merge"
|
5
|
+
const { ensureTrailingSlash } = require("./utils/helpers")
|
6
|
+
const { railsEnv } = require("./env")
|
7
|
+
const configPath = require("./utils/configPath")
|
8
|
+
const defaultConfigPath = require("./utils/defaultConfigPath")
|
9
|
+
import { Config, YamlConfig, LegacyConfig } from "./types"
|
10
|
+
const { isValidYamlConfig, createConfigValidationError, isPartialConfig, isValidConfig } = require("./utils/typeGuards")
|
11
|
+
const { isFileNotFoundError, createFileOperationError } = require("./utils/errorHelpers")
|
12
|
+
|
13
|
+
const loadAndValidateYaml = (path: string): YamlConfig => {
|
14
|
+
const fileContent = readFileSync(path, "utf8")
|
15
|
+
const yamlContent = load(fileContent)
|
16
|
+
|
17
|
+
if (!isValidYamlConfig(yamlContent)) {
|
18
|
+
throw createConfigValidationError(path, railsEnv, "Invalid YAML structure")
|
19
|
+
}
|
20
|
+
|
21
|
+
return yamlContent as YamlConfig
|
22
|
+
}
|
23
|
+
|
24
|
+
const getDefaultConfig = (): Partial<Config> => {
|
25
|
+
try {
|
26
|
+
const defaultConfig = loadAndValidateYaml(defaultConfigPath)
|
27
|
+
return defaultConfig[railsEnv] || defaultConfig.production || {}
|
28
|
+
} catch (error) {
|
29
|
+
if (isFileNotFoundError(error)) {
|
30
|
+
throw createFileOperationError(
|
31
|
+
'read',
|
32
|
+
defaultConfigPath,
|
33
|
+
`Default configuration not found at ${defaultConfigPath}. Please ensure Shakapacker is properly installed. You may need to run 'yarn add shakapacker' or 'npm install shakapacker'.`
|
34
|
+
)
|
35
|
+
}
|
36
|
+
throw error
|
37
|
+
}
|
38
|
+
}
|
39
|
+
|
40
|
+
const defaults = getDefaultConfig()
|
41
|
+
let config: Config
|
42
|
+
|
43
|
+
if (existsSync(configPath)) {
|
44
|
+
try {
|
45
|
+
const appYmlObject = loadAndValidateYaml(configPath)
|
46
|
+
|
47
|
+
const envAppConfig = appYmlObject[railsEnv]
|
48
|
+
|
49
|
+
if (!envAppConfig) {
|
50
|
+
/* eslint no-console:0 */
|
51
|
+
console.warn(
|
52
|
+
`[SHAKAPACKER WARNING] Environment '${railsEnv}' not found in ${configPath}\n` +
|
53
|
+
`Available environments: ${Object.keys(appYmlObject).join(', ')}\n` +
|
54
|
+
`Using 'production' configuration as fallback.\n\n` +
|
55
|
+
`To fix this, either:\n` +
|
56
|
+
` - Add a '${railsEnv}' section to your shakapacker.yml\n` +
|
57
|
+
` - Set RAILS_ENV to one of the available environments\n` +
|
58
|
+
` - Copy settings from another environment as a starting point`
|
59
|
+
)
|
60
|
+
}
|
61
|
+
|
62
|
+
// Merge returns the merged type
|
63
|
+
const mergedConfig = merge(defaults, envAppConfig || {})
|
64
|
+
|
65
|
+
// Validate merged config before type assertion
|
66
|
+
if (!isPartialConfig(mergedConfig)) {
|
67
|
+
throw createConfigValidationError(
|
68
|
+
configPath,
|
69
|
+
railsEnv,
|
70
|
+
`Invalid configuration structure in ${configPath}. Please check your shakapacker.yml syntax and ensure all required fields are properly defined.`
|
71
|
+
)
|
72
|
+
}
|
73
|
+
|
74
|
+
// After merging with defaults, config should be complete
|
75
|
+
// Use type assertion only after validation
|
76
|
+
config = mergedConfig as Config
|
77
|
+
} catch (error) {
|
78
|
+
if (isFileNotFoundError(error)) {
|
79
|
+
// File not found is OK, use defaults
|
80
|
+
if (!isPartialConfig(defaults)) {
|
81
|
+
throw createConfigValidationError(
|
82
|
+
defaultConfigPath,
|
83
|
+
railsEnv,
|
84
|
+
`Invalid default configuration. This may indicate a corrupted Shakapacker installation. Try reinstalling with 'yarn add shakapacker --force'.`
|
85
|
+
)
|
86
|
+
}
|
87
|
+
// Using defaults only, might be partial
|
88
|
+
config = defaults as Config
|
89
|
+
} else {
|
90
|
+
throw error
|
91
|
+
}
|
92
|
+
}
|
93
|
+
} else {
|
94
|
+
// No user config, use defaults
|
95
|
+
if (!isPartialConfig(defaults)) {
|
96
|
+
throw createConfigValidationError(
|
97
|
+
defaultConfigPath,
|
98
|
+
railsEnv,
|
99
|
+
`Invalid default configuration. This may indicate a corrupted Shakapacker installation. Try reinstalling with 'yarn add shakapacker --force'.`
|
100
|
+
)
|
101
|
+
}
|
102
|
+
// Using defaults only, might be partial
|
103
|
+
config = defaults as Config
|
104
|
+
}
|
105
|
+
|
106
|
+
config.outputPath = resolve(config.public_root_path, config.public_output_path)
|
107
|
+
|
108
|
+
// Ensure that the publicPath includes our asset host so dynamic imports
|
109
|
+
// (code-splitting chunks and static assets) load from the CDN instead of a relative path.
|
110
|
+
const getPublicPath = (): string => {
|
111
|
+
const rootUrl = ensureTrailingSlash(process.env.SHAKAPACKER_ASSET_HOST || "/")
|
112
|
+
return `${rootUrl}${config.public_output_path}/`
|
113
|
+
}
|
114
|
+
|
115
|
+
config.publicPath = getPublicPath()
|
116
|
+
config.publicPathWithoutCDN = `/${config.public_output_path}/`
|
117
|
+
|
118
|
+
if (config.manifest_path) {
|
119
|
+
config.manifestPath = resolve(config.manifest_path)
|
120
|
+
} else {
|
121
|
+
config.manifestPath = resolve(config.outputPath, "manifest.json")
|
122
|
+
}
|
123
|
+
// Ensure no duplicate hash functions exist in the returned config object
|
124
|
+
if (config.integrity?.hash_functions) {
|
125
|
+
config.integrity.hash_functions = [...new Set(config.integrity.hash_functions)]
|
126
|
+
}
|
127
|
+
|
128
|
+
// Allow ENV variable to override assets_bundler
|
129
|
+
if (process.env.SHAKAPACKER_ASSETS_BUNDLER) {
|
130
|
+
config.assets_bundler = process.env.SHAKAPACKER_ASSETS_BUNDLER
|
131
|
+
}
|
132
|
+
|
133
|
+
// Define clear defaults
|
134
|
+
const DEFAULT_JAVASCRIPT_TRANSPILER =
|
135
|
+
config.assets_bundler === "rspack" ? "swc" : "babel"
|
136
|
+
|
137
|
+
// Backward compatibility: Add webpack_loader property that maps to javascript_transpiler
|
138
|
+
// Show deprecation warning if webpack_loader is used
|
139
|
+
// Use type-safe property check
|
140
|
+
const configWithLegacy = config as Config & { webpack_loader?: string }
|
141
|
+
const webpackLoader = configWithLegacy.webpack_loader
|
142
|
+
|
143
|
+
if (webpackLoader && !config.javascript_transpiler) {
|
144
|
+
console.warn(
|
145
|
+
"[SHAKAPACKER DEPRECATION] The 'webpack_loader' configuration option is deprecated.\n" +
|
146
|
+
"Please use 'javascript_transpiler' instead as it better reflects its purpose of configuring JavaScript transpilation regardless of the bundler used."
|
147
|
+
)
|
148
|
+
config.javascript_transpiler = webpackLoader
|
149
|
+
} else if (!config.javascript_transpiler) {
|
150
|
+
config.javascript_transpiler = DEFAULT_JAVASCRIPT_TRANSPILER
|
151
|
+
}
|
152
|
+
|
153
|
+
// Ensure webpack_loader is always available for backward compatibility
|
154
|
+
// Use property assignment instead of type assertion
|
155
|
+
Object.defineProperty(config, 'webpack_loader', {
|
156
|
+
value: config.javascript_transpiler,
|
157
|
+
writable: true,
|
158
|
+
enumerable: true,
|
159
|
+
configurable: true
|
160
|
+
})
|
161
|
+
|
162
|
+
export = config
|
@@ -1,23 +1,26 @@
|
|
1
1
|
// These are the raw shakapacker dev server config settings from the YML file with ENV overrides applied.
|
2
2
|
const { isBoolean } = require("./utils/helpers")
|
3
3
|
const config = require("./config")
|
4
|
+
import { DevServerConfig } from "./types"
|
4
5
|
|
5
|
-
const envFetch = (key) => {
|
6
|
+
const envFetch = (key: string): string | boolean | undefined => {
|
6
7
|
const value = process.env[key]
|
8
|
+
if (!value) return undefined
|
7
9
|
return isBoolean(value) ? JSON.parse(value) : value
|
8
10
|
}
|
9
11
|
|
10
|
-
const devServerConfig = config.dev_server
|
12
|
+
const devServerConfig: DevServerConfig | undefined = config.dev_server
|
11
13
|
|
12
14
|
if (devServerConfig) {
|
13
|
-
const envPrefix =
|
15
|
+
const envPrefix = devServerConfig.env_prefix || "SHAKAPACKER_DEV_SERVER"
|
14
16
|
|
15
17
|
Object.keys(devServerConfig).forEach((key) => {
|
16
18
|
const envValue = envFetch(`${envPrefix}_${key.toUpperCase()}`)
|
17
19
|
if (envValue !== undefined) {
|
18
|
-
|
20
|
+
// Use bracket notation to avoid ASI issues
|
21
|
+
(devServerConfig as Record<string, unknown>)[key] = envValue
|
19
22
|
}
|
20
23
|
})
|
21
24
|
}
|
22
25
|
|
23
|
-
|
26
|
+
export = devServerConfig || {}
|
data/package/env.ts
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
import { load } from "js-yaml"
|
2
|
+
import { readFileSync } from "fs"
|
3
|
+
const defaultConfigPath = require("./utils/defaultConfigPath")
|
4
|
+
const configPath = require("./utils/configPath")
|
5
|
+
const { isFileNotFoundError } = require("./utils/errorHelpers")
|
6
|
+
|
7
|
+
const NODE_ENVIRONMENTS = ["development", "production", "test"] as const
|
8
|
+
const DEFAULT = "production"
|
9
|
+
|
10
|
+
const initialRailsEnv = process.env.RAILS_ENV
|
11
|
+
const rawNodeEnv = process.env.NODE_ENV
|
12
|
+
const nodeEnv =
|
13
|
+
rawNodeEnv && NODE_ENVIRONMENTS.includes(rawNodeEnv as typeof NODE_ENVIRONMENTS[number]) ? rawNodeEnv : DEFAULT
|
14
|
+
const isProduction = nodeEnv === "production"
|
15
|
+
const isDevelopment = nodeEnv === "development"
|
16
|
+
|
17
|
+
interface ConfigFile {
|
18
|
+
[environment: string]: Record<string, unknown>
|
19
|
+
}
|
20
|
+
|
21
|
+
let config: ConfigFile
|
22
|
+
try {
|
23
|
+
config = load(readFileSync(configPath, "utf8")) as ConfigFile
|
24
|
+
} catch (error: unknown) {
|
25
|
+
if (isFileNotFoundError(error)) {
|
26
|
+
// File not found, use default configuration
|
27
|
+
try {
|
28
|
+
config = load(readFileSync(defaultConfigPath, "utf8")) as ConfigFile
|
29
|
+
} catch (defaultError) {
|
30
|
+
throw new Error(
|
31
|
+
`Failed to load Shakapacker configuration.\n` +
|
32
|
+
`Neither user config (${configPath}) nor default config (${defaultConfigPath}) could be loaded.\n\n` +
|
33
|
+
`To fix this issue:\n` +
|
34
|
+
`1. Create a config/shakapacker.yml file in your project\n` +
|
35
|
+
`2. Or set the SHAKAPACKER_CONFIG environment variable to point to your config file\n` +
|
36
|
+
`3. Or reinstall Shakapacker to restore the default configuration:\n` +
|
37
|
+
` npm install shakapacker --force\n` +
|
38
|
+
` yarn add shakapacker --force`
|
39
|
+
)
|
40
|
+
}
|
41
|
+
} else {
|
42
|
+
throw error
|
43
|
+
}
|
44
|
+
}
|
45
|
+
|
46
|
+
const availableEnvironments = Object.keys(config).join("|")
|
47
|
+
const regex = new RegExp(`^(${availableEnvironments})$`, "g")
|
48
|
+
|
49
|
+
const runningWebpackDevServer = process.env.WEBPACK_SERVE === "true"
|
50
|
+
|
51
|
+
const validatedRailsEnv = initialRailsEnv && initialRailsEnv.match(regex) ? initialRailsEnv : DEFAULT
|
52
|
+
|
53
|
+
if (initialRailsEnv && validatedRailsEnv !== initialRailsEnv) {
|
54
|
+
/* eslint no-console:0 */
|
55
|
+
console.warn(
|
56
|
+
`[SHAKAPACKER WARNING] Environment '${initialRailsEnv}' not found in the configuration.\n` +
|
57
|
+
`Using '${DEFAULT}' configuration as a fallback.`
|
58
|
+
)
|
59
|
+
}
|
60
|
+
|
61
|
+
export = {
|
62
|
+
railsEnv: validatedRailsEnv,
|
63
|
+
nodeEnv,
|
64
|
+
isProduction,
|
65
|
+
isDevelopment,
|
66
|
+
runningWebpackDevServer
|
67
|
+
}
|