shakapacker 9.0.0.beta.2 → 9.0.0.beta.4
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/.github/workflows/dummy.yml +3 -3
- data/.github/workflows/test-bundlers.yml +152 -0
- data/CHANGELOG.md +31 -2
- data/CLAUDE.md +29 -0
- data/Gemfile.lock +1 -1
- data/README.md +42 -1
- data/Rakefile +25 -7
- data/TODO.md +51 -0
- data/TODO_v9.md +84 -0
- data/conductor-setup.sh +58 -0
- data/conductor.json +7 -0
- data/docs/cdn_setup.md +379 -0
- data/docs/css-modules-export-mode.md +216 -86
- data/docs/deployment.md +10 -1
- data/docs/peer-dependencies.md +23 -3
- data/docs/transpiler-performance.md +179 -0
- data/docs/v6_upgrade.md +10 -0
- data/docs/v9_upgrade.md +228 -0
- data/lib/install/config/shakapacker.yml +8 -2
- data/lib/install/package.json +8 -0
- data/lib/install/template.rb +121 -51
- data/lib/shakapacker/configuration.rb +39 -0
- data/lib/shakapacker/version.rb +1 -1
- data/package/config.js +2 -2
- data/package/index.d.ts +12 -1
- data/package/rules/rspack.js +4 -0
- data/package/utils/getStyleRule.js +16 -7
- data/package/utils/helpers.js +66 -1
- data/package/utils/validateCssModulesConfig.js +91 -0
- data/package.json +9 -2
- data/test/package/rules/babel.test.js +16 -0
- data/tools/README.md +124 -0
- data/tools/css-modules-v9-codemod.js +179 -0
- data/yarn.lock +203 -85
- metadata +14 -2
data/docs/v9_upgrade.md
ADDED
@@ -0,0 +1,228 @@
|
|
1
|
+
# Shakapacker v9 Upgrade Guide
|
2
|
+
|
3
|
+
This guide outlines breaking changes and migration steps for upgrading from Shakapacker v8 to v9.
|
4
|
+
|
5
|
+
## Breaking Changes
|
6
|
+
|
7
|
+
### 1. CSS Modules Configuration Changed to Named Exports
|
8
|
+
|
9
|
+
**What changed:** CSS Modules are now configured with `namedExport: true` and `exportLocalsConvention: 'camelCase'` by default, aligning with Next.js and modern tooling standards.
|
10
|
+
|
11
|
+
**JavaScript Projects:**
|
12
|
+
```js
|
13
|
+
// Before (v8)
|
14
|
+
import styles from './Component.module.css';
|
15
|
+
<button className={styles.button} />
|
16
|
+
|
17
|
+
// After (v9)
|
18
|
+
import { button } from './Component.module.css';
|
19
|
+
<button className={button} />
|
20
|
+
```
|
21
|
+
|
22
|
+
**TypeScript Projects:**
|
23
|
+
```typescript
|
24
|
+
// Before (v8)
|
25
|
+
import styles from './Component.module.css';
|
26
|
+
<button className={styles.button} />
|
27
|
+
|
28
|
+
// After (v9) - namespace import due to TypeScript limitations
|
29
|
+
import * as styles from './Component.module.css';
|
30
|
+
<button className={styles.button} />
|
31
|
+
```
|
32
|
+
|
33
|
+
**Migration Options:**
|
34
|
+
|
35
|
+
1. **Update your code** (Recommended):
|
36
|
+
- JavaScript: Change to named imports (`import { className }`)
|
37
|
+
- TypeScript: Change to namespace imports (`import * as styles`)
|
38
|
+
- Kebab-case class names are automatically converted to camelCase
|
39
|
+
|
40
|
+
2. **Keep v8 behavior** temporarily:
|
41
|
+
- Override the css-loader configuration as shown in [CSS Modules Export Mode documentation](./css-modules-export-mode.md)
|
42
|
+
- This gives you time to migrate gradually
|
43
|
+
|
44
|
+
**Benefits of the change:**
|
45
|
+
- Eliminates webpack/TypeScript warnings
|
46
|
+
- Better tree-shaking of unused CSS classes
|
47
|
+
- More explicit about which classes are used
|
48
|
+
- Aligns with modern JavaScript standards
|
49
|
+
|
50
|
+
### 2. Configuration Option Renamed: `webpack_loader` → `javascript_transpiler`
|
51
|
+
|
52
|
+
**What changed:** The configuration option has been renamed to better reflect its purpose.
|
53
|
+
|
54
|
+
**Before (v8):**
|
55
|
+
```yml
|
56
|
+
# config/shakapacker.yml
|
57
|
+
webpack_loader: 'babel'
|
58
|
+
```
|
59
|
+
|
60
|
+
**After (v9):**
|
61
|
+
```yml
|
62
|
+
# config/shakapacker.yml
|
63
|
+
javascript_transpiler: 'babel'
|
64
|
+
```
|
65
|
+
|
66
|
+
**Note:** The old `webpack_loader` option is deprecated but still supported with a warning.
|
67
|
+
|
68
|
+
### 3. SWC is Now the Default JavaScript Transpiler
|
69
|
+
|
70
|
+
**What changed:** SWC replaces Babel as the default JavaScript transpiler. Babel is no longer included in peer dependencies.
|
71
|
+
|
72
|
+
**Why:** SWC is 20x faster than Babel while maintaining compatibility with most JavaScript and TypeScript code.
|
73
|
+
|
74
|
+
**Impact on existing projects:**
|
75
|
+
- Your project will continue using Babel if you already have babel packages in package.json
|
76
|
+
- To switch to SWC for better performance, see migration options below
|
77
|
+
|
78
|
+
**Impact on new projects:**
|
79
|
+
- New installations will use SWC by default
|
80
|
+
- Babel dependencies won't be installed unless explicitly configured
|
81
|
+
|
82
|
+
### Migration Options
|
83
|
+
|
84
|
+
#### Option 1 (Recommended): Switch to SWC
|
85
|
+
```yml
|
86
|
+
# config/shakapacker.yml
|
87
|
+
javascript_transpiler: 'swc'
|
88
|
+
```
|
89
|
+
Then install SWC:
|
90
|
+
```bash
|
91
|
+
npm install @swc/core swc-loader
|
92
|
+
```
|
93
|
+
|
94
|
+
#### Option 2: Keep using Babel
|
95
|
+
```yml
|
96
|
+
# config/shakapacker.yml
|
97
|
+
javascript_transpiler: 'babel'
|
98
|
+
```
|
99
|
+
No other changes needed - your existing babel packages will continue to work.
|
100
|
+
|
101
|
+
#### Option 3: Use esbuild
|
102
|
+
```yml
|
103
|
+
# config/shakapacker.yml
|
104
|
+
javascript_transpiler: 'esbuild'
|
105
|
+
```
|
106
|
+
Then install esbuild:
|
107
|
+
```bash
|
108
|
+
npm install esbuild esbuild-loader
|
109
|
+
```
|
110
|
+
|
111
|
+
### 4. Rspack Support Added
|
112
|
+
|
113
|
+
**New feature:** Shakapacker v9 adds support for Rspack as an alternative bundler to webpack.
|
114
|
+
|
115
|
+
```yml
|
116
|
+
# config/shakapacker.yml
|
117
|
+
assets_bundler: 'rspack' # or 'webpack' (default)
|
118
|
+
```
|
119
|
+
|
120
|
+
## Migration Steps
|
121
|
+
|
122
|
+
### Step 1: Update Dependencies
|
123
|
+
|
124
|
+
```bash
|
125
|
+
npm update shakapacker@^9.0.0
|
126
|
+
# or
|
127
|
+
yarn upgrade shakapacker@^9.0.0
|
128
|
+
```
|
129
|
+
|
130
|
+
### Step 2: Update CSS Module Imports
|
131
|
+
|
132
|
+
#### For each CSS module import:
|
133
|
+
|
134
|
+
```js
|
135
|
+
// Find imports like this:
|
136
|
+
import styles from './styles.module.css';
|
137
|
+
|
138
|
+
// Replace with named imports:
|
139
|
+
import { className1, className2 } from './styles.module.css';
|
140
|
+
```
|
141
|
+
|
142
|
+
#### Update TypeScript definitions:
|
143
|
+
|
144
|
+
```typescript
|
145
|
+
// Update your CSS module type definitions
|
146
|
+
declare module '*.module.css' {
|
147
|
+
// With namedExport: true, css-loader generates individual named exports
|
148
|
+
// TypeScript can't know the exact names at compile time, so we declare
|
149
|
+
// a module with any number of string exports
|
150
|
+
const classes: { readonly [key: string]: string };
|
151
|
+
export = classes;
|
152
|
+
// Note: This allows 'import * as styles' but not 'import styles from'
|
153
|
+
// because css-loader with namedExport: true doesn't generate a default export
|
154
|
+
}
|
155
|
+
```
|
156
|
+
|
157
|
+
### Step 3: Handle Kebab-Case Class Names
|
158
|
+
|
159
|
+
v9 automatically converts kebab-case to camelCase:
|
160
|
+
|
161
|
+
```css
|
162
|
+
/* styles.module.css */
|
163
|
+
.my-button { }
|
164
|
+
.primary-color { }
|
165
|
+
```
|
166
|
+
|
167
|
+
```js
|
168
|
+
// v9 imports
|
169
|
+
import { myButton, primaryColor } from './styles.module.css';
|
170
|
+
```
|
171
|
+
|
172
|
+
### Step 4: Update Configuration Files
|
173
|
+
|
174
|
+
If you have `webpack_loader` in your configuration:
|
175
|
+
|
176
|
+
```yml
|
177
|
+
# config/shakapacker.yml
|
178
|
+
# OLD:
|
179
|
+
# webpack_loader: 'babel'
|
180
|
+
|
181
|
+
# NEW:
|
182
|
+
javascript_transpiler: 'babel'
|
183
|
+
```
|
184
|
+
|
185
|
+
### Step 5: Run Tests
|
186
|
+
|
187
|
+
```bash
|
188
|
+
# Run your test suite
|
189
|
+
npm test
|
190
|
+
|
191
|
+
# Build your application
|
192
|
+
bin/shakapacker
|
193
|
+
|
194
|
+
# Test in development
|
195
|
+
bin/shakapacker-dev-server
|
196
|
+
```
|
197
|
+
|
198
|
+
## Troubleshooting
|
199
|
+
|
200
|
+
### CSS Classes Not Applying
|
201
|
+
|
202
|
+
- Ensure you're using named imports: `import { className } from '...'`
|
203
|
+
- Check camelCase conversion for kebab-case names
|
204
|
+
- Clear cache: `rm -rf tmp/cache && bin/shakapacker`
|
205
|
+
|
206
|
+
### TypeScript Errors
|
207
|
+
|
208
|
+
Update your global type definitions as shown in Step 2.
|
209
|
+
|
210
|
+
### Build Warnings
|
211
|
+
|
212
|
+
If you see warnings about CSS module exports, ensure you've updated all imports to use named exports or have properly configured the override.
|
213
|
+
|
214
|
+
## Need Help?
|
215
|
+
|
216
|
+
- See [CSS Modules Export Mode documentation](./css-modules-export-mode.md) for detailed configuration options
|
217
|
+
- Check the [CHANGELOG](../CHANGELOG.md) for all changes
|
218
|
+
- File issues at [GitHub Issues](https://github.com/shakacode/shakapacker/issues)
|
219
|
+
|
220
|
+
## Gradual Migration Strategy
|
221
|
+
|
222
|
+
If you have a large codebase and need to migrate gradually:
|
223
|
+
|
224
|
+
1. Override the CSS configuration to keep v8 behavior (see [documentation](./css-modules-export-mode.md))
|
225
|
+
2. Migrate files incrementally
|
226
|
+
3. Remove the override once migration is complete
|
227
|
+
|
228
|
+
This allows you to upgrade to v9 immediately while taking time to update your CSS module imports.
|
@@ -29,6 +29,10 @@ default: &default
|
|
29
29
|
# Location for manifest.json, defaults to {public_output_path}/manifest.json if unset
|
30
30
|
# manifest_path: public/packs/manifest.json
|
31
31
|
|
32
|
+
# Location for private server-side bundles (e.g., for SSR)
|
33
|
+
# These bundles are not served publicly, unlike public_output_path
|
34
|
+
# private_output_path: ssr-generated
|
35
|
+
|
32
36
|
# Additional paths webpack should look up modules
|
33
37
|
# ['app/assets', 'engine/foo/app/assets']
|
34
38
|
additional_paths: []
|
@@ -36,8 +40,10 @@ default: &default
|
|
36
40
|
# Reload manifest.json on all requests so we reload latest compiled packs
|
37
41
|
cache_manifest: false
|
38
42
|
|
39
|
-
# Select JavaScript transpiler to use
|
40
|
-
|
43
|
+
# Select JavaScript transpiler to use
|
44
|
+
# Available options: 'swc' (default, 20x faster), 'babel', or 'esbuild'
|
45
|
+
# Note: When using rspack, swc is used automatically regardless of this setting
|
46
|
+
javascript_transpiler: 'swc'
|
41
47
|
|
42
48
|
# Select assets bundler to use
|
43
49
|
# Available options: 'webpack' (default) or 'rspack'
|
data/lib/install/package.json
CHANGED
@@ -26,5 +26,13 @@
|
|
26
26
|
"@babel/preset-env": "^7.16.11",
|
27
27
|
"@babel/runtime": "^7.17.9",
|
28
28
|
"babel-loader": "^8.2.4 || ^9.0.0 || ^10.0.0"
|
29
|
+
},
|
30
|
+
"swc": {
|
31
|
+
"@swc/core": "^1.3.0",
|
32
|
+
"swc-loader": "^0.2.0"
|
33
|
+
},
|
34
|
+
"esbuild": {
|
35
|
+
"esbuild": "^0.24.0",
|
36
|
+
"esbuild-loader": "^4.0.0"
|
29
37
|
}
|
30
38
|
}
|
data/lib/install/template.rb
CHANGED
@@ -2,25 +2,56 @@ require "shakapacker/utils/misc"
|
|
2
2
|
require "shakapacker/utils/manager"
|
3
3
|
require "shakapacker/utils/version_syntax_converter"
|
4
4
|
require "package_json"
|
5
|
+
require "yaml"
|
6
|
+
require "json"
|
5
7
|
|
6
8
|
# Install Shakapacker
|
7
9
|
|
8
10
|
force_option = ENV["FORCE"] ? { force: true } : {}
|
9
11
|
|
10
|
-
|
12
|
+
# Initialize variables for use throughout the template
|
13
|
+
# Using instance variable to avoid method definition issues in Rails templates
|
14
|
+
@package_json ||= PackageJson.new
|
15
|
+
install_dir = File.expand_path(File.dirname(__FILE__))
|
16
|
+
|
17
|
+
# Installation strategy:
|
18
|
+
# - USE_BABEL_PACKAGES installs both babel AND swc for compatibility
|
19
|
+
# - Otherwise install only the specified transpiler
|
20
|
+
if ENV["USE_BABEL_PACKAGES"] == "true" || ENV["USE_BABEL_PACKAGES"] == "1"
|
21
|
+
@transpiler_to_install = "babel"
|
22
|
+
say "📦 Installing Babel packages (USE_BABEL_PACKAGES is set)", :yellow
|
23
|
+
say "✨ Also installing SWC packages for default config compatibility", :green
|
24
|
+
elsif ENV["JAVASCRIPT_TRANSPILER"]
|
25
|
+
@transpiler_to_install = ENV["JAVASCRIPT_TRANSPILER"]
|
26
|
+
say "📦 Installing #{@transpiler_to_install} packages", :blue
|
27
|
+
else
|
28
|
+
# Default to swc (matches the default in shakapacker.yml)
|
29
|
+
@transpiler_to_install = "swc"
|
30
|
+
say "✨ Installing SWC packages (20x faster than Babel)", :green
|
31
|
+
end
|
32
|
+
|
33
|
+
# Copy config file
|
34
|
+
copy_file "#{install_dir}/config/shakapacker.yml", "config/shakapacker.yml", force_option
|
35
|
+
|
36
|
+
# Update config if USE_BABEL_PACKAGES is set to ensure babel is used at runtime
|
37
|
+
if @transpiler_to_install == "babel" && !ENV["JAVASCRIPT_TRANSPILER"]
|
38
|
+
# When USE_BABEL_PACKAGES is set, update the config to use babel
|
39
|
+
gsub_file "config/shakapacker.yml", "javascript_transpiler: 'swc'", "javascript_transpiler: 'babel'"
|
40
|
+
say " 📝 Updated config/shakapacker.yml to use Babel transpiler", :green
|
41
|
+
end
|
11
42
|
|
12
43
|
say "Copying webpack core config"
|
13
|
-
directory "#{
|
44
|
+
directory "#{install_dir}/config/webpack", "config/webpack", force_option
|
14
45
|
|
15
46
|
if Dir.exist?(Shakapacker.config.source_path)
|
16
47
|
say "The packs app source directory already exists"
|
17
48
|
else
|
18
49
|
say "Creating packs app source directory"
|
19
50
|
empty_directory "app/javascript/packs"
|
20
|
-
copy_file "#{
|
51
|
+
copy_file "#{install_dir}/application.js", "app/javascript/packs/application.js"
|
21
52
|
end
|
22
53
|
|
23
|
-
apply "#{
|
54
|
+
apply "#{install_dir}/binstubs.rb"
|
24
55
|
|
25
56
|
git_ignore_path = Rails.root.join(".gitignore")
|
26
57
|
if File.exist?(git_ignore_path)
|
@@ -43,17 +74,8 @@ else
|
|
43
74
|
say %( Add <%= javascript_pack_tag "application" %> within the <head> tag in your custom layout.)
|
44
75
|
end
|
45
76
|
|
46
|
-
def package_json
|
47
|
-
@package_json ||= PackageJson.new
|
48
|
-
end
|
49
|
-
|
50
77
|
# setup the package manager with default values
|
51
|
-
package_json.merge! do |pj|
|
52
|
-
babel = pj.fetch("babel", {})
|
53
|
-
|
54
|
-
babel["presets"] ||= []
|
55
|
-
babel["presets"].push("./node_modules/shakapacker/package/babel/preset.js")
|
56
|
-
|
78
|
+
@package_json.merge! do |pj|
|
57
79
|
package_manager = pj.fetch("packageManager") do
|
58
80
|
"#{Shakapacker::Utils::Manager.guess_binary}@#{Shakapacker::Utils::Manager.guess_version}"
|
59
81
|
end
|
@@ -62,7 +84,6 @@ package_json.merge! do |pj|
|
|
62
84
|
"name" => "app",
|
63
85
|
"private" => true,
|
64
86
|
"version" => "0.1.0",
|
65
|
-
"babel" => babel,
|
66
87
|
"browserslist" => [
|
67
88
|
"defaults"
|
68
89
|
],
|
@@ -74,7 +95,7 @@ Shakapacker::Utils::Manager.error_unless_package_manager_is_obvious!
|
|
74
95
|
|
75
96
|
# Ensure there is `system!("bin/yarn")` command in `./bin/setup` file
|
76
97
|
if (setup_path = Rails.root.join("bin/setup")).exist?
|
77
|
-
native_install_command = package_json.manager.native_install_command.join(" ")
|
98
|
+
native_install_command = @package_json.manager.native_install_command.join(" ")
|
78
99
|
|
79
100
|
say "Run #{native_install_command} during bin/setup"
|
80
101
|
|
@@ -103,42 +124,56 @@ if (setup_path = Rails.root.join("bin/setup")).exist?
|
|
103
124
|
end
|
104
125
|
end
|
105
126
|
|
106
|
-
def add_dependencies(dependencies, type)
|
107
|
-
package_json.manager.add!(dependencies, type: type)
|
108
|
-
rescue PackageJson::Error
|
109
|
-
say "Shakapacker installation failed 😭 See above for details.", :red
|
110
|
-
exit 1
|
111
|
-
end
|
112
|
-
|
113
|
-
def fetch_peer_dependencies
|
114
|
-
PackageJson.read("#{__dir__}").fetch(ENV["SHAKAPACKER_BUNDLER"] || "webpack")
|
115
|
-
end
|
116
|
-
|
117
|
-
def fetch_common_dependencies
|
118
|
-
ENV["SKIP_COMMON_LOADERS"] ? {} : PackageJson.read("#{__dir__}").fetch("common")
|
119
|
-
end
|
120
|
-
|
121
|
-
def fetch_babel_dependencies
|
122
|
-
ENV["USE_BABEL_PACKAGES"] ? PackageJson.read("#{__dir__}").fetch("babel") : {}
|
123
|
-
end
|
124
|
-
|
125
127
|
Dir.chdir(Rails.root) do
|
126
128
|
npm_version = Shakapacker::Utils::VersionSyntaxConverter.new.rubygem_to_npm(Shakapacker::VERSION)
|
127
129
|
say "Installing shakapacker@#{npm_version}"
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
# TODO: workaround for test suite - long-run need to actually account for diff pkg manager behaviour
|
134
|
-
"shakapacker" => pj["dependencies"]["shakapacker"].delete_prefix("^")
|
135
|
-
})
|
136
|
-
}
|
130
|
+
begin
|
131
|
+
@package_json.manager.add!(["shakapacker@#{npm_version}"], type: :production)
|
132
|
+
rescue PackageJson::Error
|
133
|
+
say "Shakapacker installation failed 😭 See above for details.", :red
|
134
|
+
exit 1
|
137
135
|
end
|
138
136
|
|
139
|
-
|
140
|
-
|
141
|
-
|
137
|
+
@package_json.merge! do |pj|
|
138
|
+
if pj["dependencies"] && pj["dependencies"]["shakapacker"]
|
139
|
+
{
|
140
|
+
"dependencies" => pj["dependencies"].merge({
|
141
|
+
# TODO: workaround for test suite - long-run need to actually account for diff pkg manager behaviour
|
142
|
+
"shakapacker" => pj["dependencies"]["shakapacker"].delete_prefix("^")
|
143
|
+
})
|
144
|
+
}
|
145
|
+
else
|
146
|
+
pj
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
# Inline fetch_peer_dependencies and fetch_common_dependencies
|
151
|
+
peers = PackageJson.read(install_dir).fetch(ENV["SHAKAPACKER_BUNDLER"] || "webpack")
|
152
|
+
common_deps = ENV["SKIP_COMMON_LOADERS"] ? {} : PackageJson.read(install_dir).fetch("common")
|
153
|
+
peers = peers.merge(common_deps)
|
154
|
+
|
155
|
+
# Add transpiler-specific dependencies based on detected/configured transpiler
|
156
|
+
# Inline the logic here since methods can't be called before they're defined in Rails templates
|
157
|
+
|
158
|
+
# Install transpiler-specific dependencies
|
159
|
+
# When USE_BABEL_PACKAGES is set, install both babel AND swc
|
160
|
+
# This ensures backward compatibility while supporting the default config
|
161
|
+
if @transpiler_to_install == "babel"
|
162
|
+
# Install babel packages
|
163
|
+
babel_deps = PackageJson.read(install_dir).fetch("babel")
|
164
|
+
peers = peers.merge(babel_deps)
|
165
|
+
|
166
|
+
# Also install SWC since that's what the default config uses
|
167
|
+
# This ensures the runtime works regardless of config
|
168
|
+
swc_deps = PackageJson.read(install_dir).fetch("swc")
|
169
|
+
peers = peers.merge(swc_deps)
|
170
|
+
elsif @transpiler_to_install == "swc"
|
171
|
+
swc_deps = PackageJson.read(install_dir).fetch("swc")
|
172
|
+
peers = peers.merge(swc_deps)
|
173
|
+
elsif @transpiler_to_install == "esbuild"
|
174
|
+
esbuild_deps = PackageJson.read(install_dir).fetch("esbuild")
|
175
|
+
peers = peers.merge(esbuild_deps)
|
176
|
+
end
|
142
177
|
|
143
178
|
dev_dependency_packages = ["webpack-dev-server"]
|
144
179
|
|
@@ -146,8 +181,15 @@ Dir.chdir(Rails.root) do
|
|
146
181
|
dev_dependencies_to_add = []
|
147
182
|
|
148
183
|
peers.each do |(package, version)|
|
149
|
-
|
150
|
-
|
184
|
+
# Handle versions like "^1.3.0" or ">= 4 || 5"
|
185
|
+
if version.start_with?("^", "~") || version.match?(/^\d+\.\d+/)
|
186
|
+
# Already has proper version format, use as-is
|
187
|
+
entry = "#{package}@#{version}"
|
188
|
+
else
|
189
|
+
# Extract major version from complex version strings like ">= 4 || 5"
|
190
|
+
major_version = version.split("||").last.match(/(\d+)/)[1]
|
191
|
+
entry = "#{package}@#{major_version}"
|
192
|
+
end
|
151
193
|
|
152
194
|
if dev_dependency_packages.include? package
|
153
195
|
dev_dependencies_to_add << entry
|
@@ -157,8 +199,36 @@ Dir.chdir(Rails.root) do
|
|
157
199
|
end
|
158
200
|
|
159
201
|
say "Adding shakapacker peerDependencies"
|
160
|
-
|
202
|
+
begin
|
203
|
+
@package_json.manager.add!(dependencies_to_add, type: :production)
|
204
|
+
rescue PackageJson::Error
|
205
|
+
say "Shakapacker installation failed 😭 See above for details.", :red
|
206
|
+
exit 1
|
207
|
+
end
|
161
208
|
|
162
209
|
say "Installing webpack-dev-server for live reloading as a development dependency"
|
163
|
-
|
210
|
+
begin
|
211
|
+
@package_json.manager.add!(dev_dependencies_to_add, type: :dev)
|
212
|
+
rescue PackageJson::Error
|
213
|
+
say "Shakapacker installation failed 😭 See above for details.", :red
|
214
|
+
exit 1
|
215
|
+
end
|
216
|
+
|
217
|
+
# Configure babel preset in package.json if babel is being used
|
218
|
+
if @transpiler_to_install == "babel"
|
219
|
+
@package_json.merge! do |pj|
|
220
|
+
babel = pj.fetch("babel", {})
|
221
|
+
babel["presets"] ||= []
|
222
|
+
unless babel["presets"].include?("./node_modules/shakapacker/package/babel/preset.js")
|
223
|
+
babel["presets"].push("./node_modules/shakapacker/package/babel/preset.js")
|
224
|
+
end
|
225
|
+
{ "babel" => babel }
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
# Helper methods defined at the end (Rails template convention)
|
231
|
+
|
232
|
+
def package_json
|
233
|
+
@package_json
|
164
234
|
end
|
@@ -68,6 +68,13 @@ class Shakapacker::Configuration
|
|
68
68
|
root_path.join(fetch(:public_root_path))
|
69
69
|
end
|
70
70
|
|
71
|
+
def private_output_path
|
72
|
+
private_path = fetch(:private_output_path)
|
73
|
+
return nil unless private_path
|
74
|
+
validate_output_paths!
|
75
|
+
root_path.join(private_path)
|
76
|
+
end
|
77
|
+
|
71
78
|
def public_output_path
|
72
79
|
public_path.join(fetch(:public_output_path))
|
73
80
|
end
|
@@ -149,6 +156,38 @@ class Shakapacker::Configuration
|
|
149
156
|
end
|
150
157
|
|
151
158
|
private
|
159
|
+
def validate_output_paths!
|
160
|
+
# Skip validation if already validated to avoid redundant checks
|
161
|
+
return if @validated_output_paths
|
162
|
+
@validated_output_paths = true
|
163
|
+
|
164
|
+
# Only validate when both paths are configured
|
165
|
+
return unless fetch(:private_output_path) && fetch(:public_output_path)
|
166
|
+
|
167
|
+
private_path_str, public_path_str = resolve_paths_for_comparison
|
168
|
+
|
169
|
+
if private_path_str == public_path_str
|
170
|
+
raise "Shakapacker configuration error: private_output_path and public_output_path must be different. " \
|
171
|
+
"Both paths resolve to '#{private_path_str}'. " \
|
172
|
+
"The private_output_path is for server-side bundles (e.g., SSR) that should not be served publicly."
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
def resolve_paths_for_comparison
|
177
|
+
private_full_path = root_path.join(fetch(:private_output_path))
|
178
|
+
public_full_path = root_path.join(fetch(:public_root_path), fetch(:public_output_path))
|
179
|
+
|
180
|
+
# Create directories if they don't exist (for testing)
|
181
|
+
private_full_path.mkpath unless private_full_path.exist?
|
182
|
+
public_full_path.mkpath unless public_full_path.exist?
|
183
|
+
|
184
|
+
# Use realpath to resolve symbolic links and relative paths
|
185
|
+
[private_full_path.realpath.to_s, public_full_path.realpath.to_s]
|
186
|
+
rescue Errno::ENOENT
|
187
|
+
# If paths don't exist yet, fall back to cleanpath for comparison
|
188
|
+
[private_full_path.cleanpath.to_s, public_full_path.cleanpath.to_s]
|
189
|
+
end
|
190
|
+
|
152
191
|
def data
|
153
192
|
@data ||= load
|
154
193
|
end
|
data/lib/shakapacker/version.rb
CHANGED
data/package/config.js
CHANGED
@@ -59,8 +59,8 @@ if (process.env.SHAKAPACKER_ASSETS_BUNDLER) {
|
|
59
59
|
}
|
60
60
|
|
61
61
|
// Define clear defaults
|
62
|
-
|
63
|
-
|
62
|
+
// SWC is now the default transpiler for both webpack and rspack
|
63
|
+
const DEFAULT_JAVASCRIPT_TRANSPILER = "swc"
|
64
64
|
|
65
65
|
// Backward compatibility: Add webpack_loader property that maps to javascript_transpiler
|
66
66
|
// Show deprecation warning if webpack_loader is used
|
data/package/index.d.ts
CHANGED
@@ -9,6 +9,7 @@ declare module "shakapacker" {
|
|
9
9
|
css_extract_ignore_order_warnings: boolean
|
10
10
|
public_root_path: string
|
11
11
|
public_output_path: string
|
12
|
+
private_output_path?: string
|
12
13
|
cache_path: string
|
13
14
|
webpack_compile_output: boolean
|
14
15
|
shakapacker_precompile: boolean
|
@@ -23,6 +24,14 @@ declare module "shakapacker" {
|
|
23
24
|
publicPath: string
|
24
25
|
publicPathWithoutCDN: string
|
25
26
|
manifestPath: string
|
27
|
+
manifest_path?: string
|
28
|
+
assets_bundler?: string
|
29
|
+
dev_server?: DevServerConfig
|
30
|
+
integrity?: {
|
31
|
+
enabled: boolean
|
32
|
+
cross_origin: string
|
33
|
+
hash_functions?: string[]
|
34
|
+
}
|
26
35
|
}
|
27
36
|
|
28
37
|
export interface Env {
|
@@ -60,6 +69,8 @@ declare module "shakapacker" {
|
|
60
69
|
ipc?: boolean | string
|
61
70
|
magic_html?: boolean
|
62
71
|
live_reload?: boolean
|
72
|
+
inline_css?: boolean
|
73
|
+
env_prefix?: string
|
63
74
|
open?:
|
64
75
|
| boolean
|
65
76
|
| string
|
@@ -136,4 +147,4 @@ declare module "shakapacker/package/babel/preset.js" {
|
|
136
147
|
) => TransformOptions & RequiredTransformOptions
|
137
148
|
|
138
149
|
export = defaultConfigFunc
|
139
|
-
}
|
150
|
+
}
|
data/package/rules/rspack.js
CHANGED
@@ -12,6 +12,8 @@ debug("Adding JavaScript rule with builtin:swc-loader")
|
|
12
12
|
rules.push({
|
13
13
|
test: /\.(js|jsx|mjs)$/,
|
14
14
|
exclude: /node_modules/,
|
15
|
+
// The 'type' field is required for Rspack to properly handle JavaScript modules
|
16
|
+
// when using builtin loaders. It ensures correct module parsing and transformation.
|
15
17
|
type: "javascript/auto",
|
16
18
|
use: [
|
17
19
|
{
|
@@ -38,6 +40,8 @@ debug("Adding TypeScript rule with builtin:swc-loader")
|
|
38
40
|
rules.push({
|
39
41
|
test: /\.(ts|tsx)$/,
|
40
42
|
exclude: /node_modules/,
|
43
|
+
// The 'type' field is required for Rspack to properly handle TypeScript modules
|
44
|
+
// when using builtin loaders. It ensures correct module parsing and transformation.
|
41
45
|
type: "javascript/auto",
|
42
46
|
use: [
|
43
47
|
{
|
@@ -3,6 +3,7 @@ const { canProcess, moduleExists } = require("./helpers")
|
|
3
3
|
const { requireOrError } = require("./requireOrError")
|
4
4
|
const config = require("../config")
|
5
5
|
const inliningCss = require("./inliningCss")
|
6
|
+
const { validateCssModulesConfig } = require("./validateCssModulesConfig")
|
6
7
|
|
7
8
|
const getStyleRule = (test, preprocessors = []) => {
|
8
9
|
if (moduleExists("css-loader")) {
|
@@ -19,17 +20,25 @@ const getStyleRule = (test, preprocessors = []) => {
|
|
19
20
|
? requireOrError("@rspack/core").CssExtractRspackPlugin.loader
|
20
21
|
: requireOrError("mini-css-extract-plugin").loader
|
21
22
|
|
23
|
+
const cssLoaderOptions = {
|
24
|
+
sourceMap: true,
|
25
|
+
importLoaders: 2,
|
26
|
+
modules: {
|
27
|
+
auto: true,
|
28
|
+
// v9 defaults: named exports with camelCase conversion
|
29
|
+
namedExport: true,
|
30
|
+
exportLocalsConvention: "camelCase"
|
31
|
+
}
|
32
|
+
}
|
33
|
+
|
34
|
+
// Validate CSS modules configuration
|
35
|
+
validateCssModulesConfig(cssLoaderOptions)
|
36
|
+
|
22
37
|
const use = [
|
23
38
|
inliningCss ? "style-loader" : extractionPlugin,
|
24
39
|
{
|
25
40
|
loader: require.resolve("css-loader"),
|
26
|
-
options:
|
27
|
-
sourceMap: true,
|
28
|
-
importLoaders: 2,
|
29
|
-
modules: {
|
30
|
-
auto: true
|
31
|
-
}
|
32
|
-
}
|
41
|
+
options: cssLoaderOptions
|
33
42
|
},
|
34
43
|
tryPostcss(),
|
35
44
|
...preprocessors
|