shakapacker 8.0.2 → 9.2.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 (198) hide show
  1. checksums.yaml +4 -4
  2. data/.eslintignore +1 -0
  3. data/.eslintrc.fast.js +40 -0
  4. data/.eslintrc.js +48 -0
  5. data/.github/STATUS.md +1 -0
  6. data/.github/workflows/claude-code-review.yml +54 -0
  7. data/.github/workflows/claude.yml +50 -0
  8. data/.github/workflows/dummy.yml +9 -4
  9. data/.github/workflows/generator.yml +32 -10
  10. data/.github/workflows/node.yml +23 -1
  11. data/.github/workflows/ruby.yml +33 -2
  12. data/.github/workflows/test-bundlers.yml +170 -0
  13. data/.gitignore +20 -0
  14. data/.husky/pre-commit +2 -0
  15. data/.npmignore +56 -0
  16. data/.prettierignore +3 -0
  17. data/.rubocop.yml +1 -0
  18. data/.yalcignore +26 -0
  19. data/CHANGELOG.md +302 -16
  20. data/CLAUDE.md +29 -0
  21. data/CONTRIBUTING.md +138 -20
  22. data/Gemfile.lock +83 -89
  23. data/README.md +343 -105
  24. data/Rakefile +39 -4
  25. data/TODO.md +50 -0
  26. data/TODO_v9.md +87 -0
  27. data/bin/export-bundler-config +11 -0
  28. data/conductor-setup.sh +70 -0
  29. data/conductor.json +7 -0
  30. data/docs/cdn_setup.md +379 -0
  31. data/docs/common-upgrades.md +615 -0
  32. data/docs/css-modules-export-mode.md +512 -0
  33. data/docs/deployment.md +62 -9
  34. data/docs/optional-peer-dependencies.md +198 -0
  35. data/docs/peer-dependencies.md +60 -0
  36. data/docs/react.md +6 -14
  37. data/docs/releasing.md +197 -0
  38. data/docs/rspack.md +190 -0
  39. data/docs/rspack_migration_guide.md +305 -0
  40. data/docs/subresource_integrity.md +54 -0
  41. data/docs/transpiler-migration.md +209 -0
  42. data/docs/transpiler-performance.md +179 -0
  43. data/docs/troubleshooting.md +157 -22
  44. data/docs/typescript-migration.md +379 -0
  45. data/docs/typescript.md +99 -0
  46. data/docs/using_esbuild_loader.md +3 -3
  47. data/docs/using_swc_loader.md +112 -10
  48. data/docs/v6_upgrade.md +10 -0
  49. data/docs/v8_upgrade.md +3 -5
  50. data/docs/v9_upgrade.md +458 -0
  51. data/gemfiles/Gemfile-rails.6.0.x +2 -1
  52. data/gemfiles/Gemfile-rails.6.1.x +1 -1
  53. data/gemfiles/Gemfile-rails.7.0.x +2 -2
  54. data/gemfiles/Gemfile-rails.7.1.x +1 -2
  55. data/gemfiles/Gemfile-rails.7.2.x +11 -0
  56. data/gemfiles/Gemfile-rails.8.0.x +11 -0
  57. data/lib/install/bin/export-bundler-config +11 -0
  58. data/lib/install/bin/shakapacker +4 -6
  59. data/lib/install/bin/shakapacker-dev-server +1 -1
  60. data/lib/install/config/rspack/rspack.config.js +6 -0
  61. data/lib/install/config/rspack/rspack.config.ts +7 -0
  62. data/lib/install/config/shakapacker.yml +25 -5
  63. data/lib/install/config/webpack/webpack.config.ts +7 -0
  64. data/lib/install/package.json +38 -0
  65. data/lib/install/template.rb +194 -44
  66. data/lib/shakapacker/bundler_switcher.rb +329 -0
  67. data/lib/shakapacker/compiler.rb +2 -1
  68. data/lib/shakapacker/compiler_strategy.rb +2 -2
  69. data/lib/shakapacker/configuration.rb +173 -2
  70. data/lib/shakapacker/dev_server_runner.rb +29 -8
  71. data/lib/shakapacker/digest_strategy.rb +2 -1
  72. data/lib/shakapacker/doctor.rb +905 -0
  73. data/lib/shakapacker/helper.rb +64 -16
  74. data/lib/shakapacker/manifest.rb +10 -3
  75. data/lib/shakapacker/mtime_strategy.rb +1 -1
  76. data/lib/shakapacker/railtie.rb +4 -4
  77. data/lib/shakapacker/rspack_runner.rb +19 -0
  78. data/lib/shakapacker/runner.rb +159 -10
  79. data/lib/shakapacker/swc_migrator.rb +384 -0
  80. data/lib/shakapacker/utils/manager.rb +15 -2
  81. data/lib/shakapacker/version.rb +1 -1
  82. data/lib/shakapacker/version_checker.rb +2 -2
  83. data/lib/shakapacker/webpack_runner.rb +6 -43
  84. data/lib/shakapacker.rb +22 -11
  85. data/lib/tasks/shakapacker/doctor.rake +8 -0
  86. data/lib/tasks/shakapacker/export_bundler_config.rake +72 -0
  87. data/lib/tasks/shakapacker/install.rake +12 -2
  88. data/lib/tasks/shakapacker/migrate_to_swc.rake +13 -0
  89. data/lib/tasks/shakapacker/switch_bundler.rake +82 -0
  90. data/lib/tasks/shakapacker.rake +2 -0
  91. data/package/.npmignore +4 -0
  92. data/package/babel/preset.ts +56 -0
  93. data/package/config.ts +175 -0
  94. data/package/configExporter/cli.ts +683 -0
  95. data/package/configExporter/configDocs.ts +102 -0
  96. data/package/configExporter/fileWriter.ts +92 -0
  97. data/package/configExporter/index.ts +5 -0
  98. data/package/configExporter/types.ts +36 -0
  99. data/package/configExporter/yamlSerializer.ts +266 -0
  100. data/package/{dev_server.js → dev_server.ts} +8 -5
  101. data/package/env.ts +92 -0
  102. data/package/environments/__type-tests__/rspack-plugin-compatibility.ts +30 -0
  103. data/package/environments/{base.js → base.ts} +56 -60
  104. data/package/environments/development.ts +90 -0
  105. data/package/environments/production.ts +80 -0
  106. data/package/environments/test.ts +53 -0
  107. data/package/environments/types.ts +98 -0
  108. data/package/esbuild/index.ts +42 -0
  109. data/package/index.d.ts +3 -60
  110. data/package/index.ts +55 -0
  111. data/package/loaders.d.ts +28 -0
  112. data/package/optimization/rspack.ts +36 -0
  113. data/package/optimization/webpack.ts +57 -0
  114. data/package/plugins/rspack.ts +103 -0
  115. data/package/plugins/webpack.ts +62 -0
  116. data/package/rspack/index.ts +64 -0
  117. data/package/rules/{babel.js → babel.ts} +2 -2
  118. data/package/rules/{coffee.js → coffee.ts} +1 -1
  119. data/package/rules/css.ts +3 -0
  120. data/package/rules/{erb.js → erb.ts} +1 -1
  121. data/package/rules/esbuild.ts +10 -0
  122. data/package/rules/file.ts +40 -0
  123. data/package/rules/{jscommon.js → jscommon.ts} +4 -4
  124. data/package/rules/{less.js → less.ts} +4 -4
  125. data/package/rules/raw.ts +25 -0
  126. data/package/rules/rspack.ts +176 -0
  127. data/package/rules/{sass.js → sass.ts} +7 -3
  128. data/package/rules/{stylus.js → stylus.ts} +4 -8
  129. data/package/rules/swc.ts +10 -0
  130. data/package/rules/webpack.ts +16 -0
  131. data/package/swc/index.ts +56 -0
  132. data/package/types/README.md +88 -0
  133. data/package/types/index.ts +61 -0
  134. data/package/types.ts +108 -0
  135. data/package/utils/configPath.ts +6 -0
  136. data/package/utils/debug.ts +49 -0
  137. data/package/utils/defaultConfigPath.ts +4 -0
  138. data/package/utils/errorCodes.ts +219 -0
  139. data/package/utils/errorHelpers.ts +143 -0
  140. data/package/utils/getStyleRule.ts +64 -0
  141. data/package/utils/helpers.ts +85 -0
  142. data/package/utils/{inliningCss.js → inliningCss.ts} +3 -3
  143. data/package/utils/pathValidation.ts +139 -0
  144. data/package/utils/requireOrError.ts +15 -0
  145. data/package/utils/snakeToCamelCase.ts +5 -0
  146. data/package/utils/typeGuards.ts +342 -0
  147. data/package/utils/validateDependencies.ts +61 -0
  148. data/package/webpack-types.d.ts +33 -0
  149. data/package/webpackDevServerConfig.ts +117 -0
  150. data/package-lock.json +13047 -0
  151. data/package.json +154 -18
  152. data/scripts/remove-use-strict.js +45 -0
  153. data/scripts/type-check-no-emit.js +27 -0
  154. data/test/helpers.js +1 -1
  155. data/test/package/config.test.js +43 -0
  156. data/test/package/env.test.js +42 -7
  157. data/test/package/environments/base.test.js +5 -1
  158. data/test/package/rules/babel.test.js +16 -0
  159. data/test/package/rules/esbuild.test.js +1 -1
  160. data/test/package/rules/raw.test.js +40 -7
  161. data/test/package/rules/swc.test.js +1 -1
  162. data/test/package/rules/webpack.test.js +35 -0
  163. data/test/package/staging.test.js +4 -3
  164. data/test/package/transpiler-defaults.test.js +127 -0
  165. data/test/peer-dependencies.sh +85 -0
  166. data/test/scripts/remove-use-strict.test.js +125 -0
  167. data/test/typescript/build.test.js +118 -0
  168. data/test/typescript/environments.test.js +107 -0
  169. data/test/typescript/pathValidation.test.js +142 -0
  170. data/test/typescript/securityValidation.test.js +182 -0
  171. data/tools/README.md +124 -0
  172. data/tools/css-modules-v9-codemod.js +179 -0
  173. data/tsconfig.eslint.json +16 -0
  174. data/tsconfig.json +38 -0
  175. data/yarn.lock +4165 -2706
  176. metadata +129 -41
  177. data/package/babel/preset.js +0 -37
  178. data/package/config.js +0 -54
  179. data/package/env.js +0 -48
  180. data/package/environments/development.js +0 -13
  181. data/package/environments/production.js +0 -88
  182. data/package/environments/test.js +0 -3
  183. data/package/esbuild/index.js +0 -40
  184. data/package/index.js +0 -40
  185. data/package/rules/css.js +0 -3
  186. data/package/rules/esbuild.js +0 -10
  187. data/package/rules/file.js +0 -29
  188. data/package/rules/index.js +0 -20
  189. data/package/rules/raw.js +0 -5
  190. data/package/rules/swc.js +0 -10
  191. data/package/swc/index.js +0 -50
  192. data/package/utils/configPath.js +0 -4
  193. data/package/utils/defaultConfigPath.js +0 -2
  194. data/package/utils/getStyleRule.js +0 -40
  195. data/package/utils/helpers.js +0 -58
  196. data/package/utils/snakeToCamelCase.js +0 -5
  197. data/package/webpackDevServerConfig.js +0 -71
  198. data/test/package/rules/index.test.js +0 -16
@@ -0,0 +1,82 @@
1
+ require "shakapacker/bundler_switcher"
2
+
3
+ namespace :shakapacker do
4
+ desc <<~DESC
5
+ Switch between webpack and rspack bundlers
6
+
7
+ Easily switch your Shakapacker configuration between webpack and rspack bundlers.
8
+ This task updates config/shakapacker.yml and optionally manages npm dependencies.
9
+
10
+ Usage:
11
+ rails shakapacker:switch_bundler [webpack|rspack] [OPTIONS]
12
+ rake shakapacker:switch_bundler [webpack|rspack] -- [OPTIONS]
13
+
14
+ Options:
15
+ --install-deps Automatically install/uninstall bundler dependencies
16
+ --no-uninstall Skip uninstalling old bundler packages (faster, keeps both bundlers)
17
+ --init-config Create custom dependencies configuration file
18
+ --help, -h Show detailed help message
19
+
20
+ Examples:
21
+ # Switch to rspack with automatic dependency management
22
+ rails shakapacker:switch_bundler rspack --install-deps
23
+ rake shakapacker:switch_bundler rspack -- --install-deps
24
+
25
+ # Fast switching without uninstalling (keeps both bundlers)
26
+ rails shakapacker:switch_bundler webpack --install-deps --no-uninstall
27
+ rake shakapacker:switch_bundler rspack -- --install-deps --no-uninstall
28
+
29
+ # Switch to rspack (manual dependency management)
30
+ rails shakapacker:switch_bundler rspack
31
+ rake shakapacker:switch_bundler rspack
32
+
33
+ # Switch back to webpack with dependency management
34
+ rails shakapacker:switch_bundler webpack --install-deps
35
+ rake shakapacker:switch_bundler webpack -- --install-deps
36
+
37
+ # Create custom dependencies config file
38
+ rails shakapacker:switch_bundler --init-config
39
+ rake shakapacker:switch_bundler -- --init-config
40
+
41
+ # Show current bundler and usage help
42
+ rails shakapacker:switch_bundler --help
43
+ rake shakapacker:switch_bundler -- --help
44
+
45
+ Note: When using 'rake', you must use '--' to separate rake options from task arguments.
46
+
47
+ What it does:
48
+ - Updates 'assets_bundler' in config/shakapacker.yml
49
+ - Preserves YAML comments and structure
50
+ - Updates 'javascript_transpiler' to 'swc' when switching to rspack
51
+ - With --install-deps: installs/uninstalls npm dependencies automatically
52
+ - Without --install-deps: shows manual installation commands
53
+
54
+ Custom Dependencies:
55
+ Create .shakapacker-switch-bundler-dependencies.yml to customize which
56
+ npm packages are installed/uninstalled during bundler switching.
57
+
58
+ See docs/rspack_migration_guide.md for more information.
59
+ DESC
60
+ task :switch_bundler do
61
+ switcher = Shakapacker::BundlerSwitcher.new
62
+
63
+ if ARGV.empty? || ARGV.include?("--help") || ARGV.include?("-h")
64
+ switcher.show_usage
65
+ elsif ARGV.include?("--init-config")
66
+ switcher.init_config
67
+ else
68
+ bundler = ARGV[1]
69
+ install_deps = ARGV.include?("--install-deps")
70
+ no_uninstall = ARGV.include?("--no-uninstall")
71
+
72
+ if bundler.nil? || bundler.start_with?("-")
73
+ switcher.show_usage
74
+ else
75
+ switcher.switch_to(bundler, install_deps: install_deps, no_uninstall: no_uninstall)
76
+ end
77
+ end
78
+
79
+ # Prevent rake from trying to execute arguments as tasks
80
+ ARGV.each { |arg| task arg.to_sym {} }
81
+ end
82
+ end
@@ -9,6 +9,8 @@ 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",
13
+ "shakapacker:switch_bundler" => "Switch between webpack and rspack bundlers"
12
14
  }.freeze
13
15
 
14
16
  desc "Lists all available tasks in Shakapacker"
@@ -0,0 +1,4 @@
1
+ # Exclude TypeScript source files from the package directory
2
+ *.ts
3
+ # But keep the TypeScript declaration files
4
+ !*.d.ts
@@ -0,0 +1,56 @@
1
+ import { moduleExists, packageFullVersion } from "../utils/helpers"
2
+ import type { ConfigAPI, PluginItem } from "@babel/core"
3
+
4
+ const CORE_JS_VERSION_REGEX = /^\d+\.\d+/
5
+
6
+ const coreJsVersion = (): string => {
7
+ try {
8
+ const version = packageFullVersion("core-js").match(CORE_JS_VERSION_REGEX)
9
+ return version?.[0] ?? "3.8"
10
+ } catch (e) {
11
+ const error = e as NodeJS.ErrnoException
12
+ if (error.code !== "MODULE_NOT_FOUND") {
13
+ throw e
14
+ }
15
+
16
+ return "3.8"
17
+ }
18
+ }
19
+
20
+ export = function config(api: ConfigAPI): { presets: PluginItem[]; plugins: PluginItem[] } {
21
+ const validEnv = ["development", "test", "production"]
22
+ const currentEnv = api.env()
23
+ const isDevelopmentEnv = api.env("development")
24
+ const isProductionEnv = api.env("production")
25
+ const isTestEnv = api.env("test")
26
+
27
+ if (!validEnv.includes(currentEnv)) {
28
+ throw new Error(
29
+ `Please specify a valid NODE_ENV or BABEL_ENV environment variable. Valid values are "development", "test", and "production". Instead, received: "${currentEnv}".`
30
+ )
31
+ }
32
+
33
+ const presets: PluginItem[] = [
34
+ isTestEnv && ["@babel/preset-env", { targets: { node: "current" } }],
35
+ (isProductionEnv || isDevelopmentEnv) && [
36
+ "@babel/preset-env",
37
+ {
38
+ useBuiltIns: "entry",
39
+ corejs: coreJsVersion(),
40
+ modules: "auto",
41
+ bugfixes: true,
42
+ exclude: ["transform-typeof-symbol"]
43
+ }
44
+ ],
45
+ moduleExists("@babel/preset-typescript") && "@babel/preset-typescript"
46
+ ].filter(Boolean) as PluginItem[]
47
+
48
+ const plugins: PluginItem[] = [["@babel/plugin-transform-runtime", { helpers: false }]].filter(
49
+ Boolean
50
+ ) as PluginItem[]
51
+
52
+ return {
53
+ presets,
54
+ plugins
55
+ }
56
+ }
data/package/config.ts ADDED
@@ -0,0 +1,175 @@
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
+ // Ensure assets_bundler has a default value
129
+ if (!config.assets_bundler) {
130
+ config.assets_bundler = "webpack"
131
+ }
132
+
133
+ // Allow ENV variable to override assets_bundler
134
+ if (process.env.SHAKAPACKER_ASSETS_BUNDLER) {
135
+ config.assets_bundler = process.env.SHAKAPACKER_ASSETS_BUNDLER
136
+ }
137
+
138
+ // Define clear defaults
139
+ // Keep Babel as default for webpack to maintain backward compatibility
140
+ // Use SWC for rspack as it's a newer bundler where we can set modern defaults
141
+ const DEFAULT_JAVASCRIPT_TRANSPILER =
142
+ config.assets_bundler === "rspack" ? "swc" : "babel"
143
+
144
+ // Backward compatibility: Check for webpack_loader using proper type guard
145
+ function hasWebpackLoader(obj: unknown): obj is Config & { webpack_loader: string } {
146
+ return (
147
+ typeof obj === 'object' &&
148
+ obj !== null &&
149
+ 'webpack_loader' in obj &&
150
+ typeof (obj as Record<string, unknown>).webpack_loader === 'string'
151
+ )
152
+ }
153
+
154
+ // Allow environment variable to override javascript_transpiler
155
+ if (process.env.SHAKAPACKER_JAVASCRIPT_TRANSPILER) {
156
+ config.javascript_transpiler = process.env.SHAKAPACKER_JAVASCRIPT_TRANSPILER
157
+ } else if (hasWebpackLoader(config) && !config.javascript_transpiler) {
158
+ console.warn(
159
+ "[SHAKAPACKER DEPRECATION] The 'webpack_loader' configuration option is deprecated.\n" +
160
+ "Please use 'javascript_transpiler' instead as it better reflects its purpose of configuring JavaScript transpilation regardless of the bundler used."
161
+ )
162
+ config.javascript_transpiler = config.webpack_loader
163
+ } else if (!config.javascript_transpiler) {
164
+ config.javascript_transpiler = DEFAULT_JAVASCRIPT_TRANSPILER
165
+ }
166
+
167
+ // Ensure webpack_loader is always available for backward compatibility
168
+ Object.defineProperty(config, 'webpack_loader', {
169
+ value: config.javascript_transpiler,
170
+ writable: true,
171
+ enumerable: true,
172
+ configurable: true
173
+ })
174
+
175
+ export = config