shakapacker 9.0.0.beta.6 → 9.0.0.beta.8
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/.eslintrc.fast.js +40 -0
- data/.eslintrc.js +48 -0
- data/.github/workflows/generator.yml +6 -0
- data/.gitignore +1 -4
- data/.npmignore +56 -0
- data/CHANGELOG.md +64 -1
- data/CONTRIBUTING.md +75 -21
- data/Gemfile.lock +1 -1
- data/README.md +4 -0
- data/TODO.md +15 -16
- data/docs/transpiler-migration.md +191 -0
- data/docs/typescript-migration.md +378 -0
- data/lib/install/template.rb +54 -7
- data/lib/shakapacker/version.rb +1 -1
- data/package/.npmignore +4 -0
- data/package/babel/preset.ts +56 -0
- data/package/config.ts +23 -10
- data/package/env.ts +15 -2
- data/package/environments/{development.js → development.ts} +30 -8
- data/package/environments/{production.js → production.ts} +18 -4
- data/package/environments/test.ts +53 -0
- data/package/environments/types.ts +90 -0
- data/package/esbuild/index.ts +42 -0
- data/package/optimization/rspack.ts +36 -0
- data/package/optimization/{webpack.js → webpack.ts} +12 -4
- data/package/plugins/{rspack.js → rspack.ts} +20 -5
- data/package/plugins/{webpack.js → webpack.ts} +2 -2
- data/package/rspack/{index.js → index.ts} +17 -10
- data/package/rules/{babel.js → babel.ts} +1 -1
- data/package/rules/{coffee.js → coffee.ts} +1 -1
- data/package/rules/{css.js → css.ts} +1 -1
- data/package/rules/{erb.js → erb.ts} +1 -1
- data/package/rules/{esbuild.js → esbuild.ts} +2 -2
- data/package/rules/{file.js → file.ts} +11 -6
- data/package/rules/{jscommon.js → jscommon.ts} +4 -4
- data/package/rules/{less.js → less.ts} +3 -3
- data/package/rules/raw.ts +25 -0
- data/package/rules/{rspack.js → rspack.ts} +21 -11
- data/package/rules/{sass.js → sass.ts} +1 -1
- data/package/rules/{stylus.js → stylus.ts} +3 -7
- data/package/rules/{swc.js → swc.ts} +2 -2
- data/package/rules/{webpack.js → webpack.ts} +1 -1
- data/package/swc/index.ts +54 -0
- data/package/types/README.md +87 -0
- data/package/types/index.ts +60 -0
- data/package/utils/errorCodes.ts +219 -0
- data/package/utils/errorHelpers.ts +68 -2
- data/package/utils/pathValidation.ts +139 -0
- data/package/utils/typeGuards.ts +161 -47
- data/package.json +26 -4
- data/scripts/remove-use-strict.js +45 -0
- data/scripts/type-check-no-emit.js +27 -0
- data/test/package/rules/raw.test.js +40 -7
- data/test/package/rules/webpack.test.js +21 -2
- data/test/package/transpiler-defaults.test.js +127 -0
- data/test/scripts/remove-use-strict.test.js +125 -0
- data/test/typescript/build.test.js +3 -2
- data/test/typescript/environments.test.js +107 -0
- data/test/typescript/pathValidation.test.js +142 -0
- data/test/typescript/securityValidation.test.js +182 -0
- data/tsconfig.eslint.json +16 -0
- data/tsconfig.json +9 -10
- data/yarn.lock +415 -6
- metadata +50 -28
- data/package/babel/preset.js +0 -48
- data/package/environments/base.js +0 -103
- data/package/environments/test.js +0 -19
- data/package/esbuild/index.js +0 -40
- data/package/optimization/rspack.js +0 -29
- data/package/rules/raw.js +0 -15
- data/package/swc/index.js +0 -50
@@ -0,0 +1,191 @@
|
|
1
|
+
# JavaScript Transpiler Configuration
|
2
|
+
|
3
|
+
## Default Transpilers
|
4
|
+
|
5
|
+
Shakapacker uses different default JavaScript transpilers based on the bundler:
|
6
|
+
|
7
|
+
- **Webpack**: `babel` (default) - Maintains backward compatibility
|
8
|
+
- **Rspack**: `swc` (default) - Modern, faster transpiler for new bundler
|
9
|
+
|
10
|
+
## Available Transpilers
|
11
|
+
|
12
|
+
- `babel` - Traditional JavaScript transpiler with wide ecosystem support
|
13
|
+
- `swc` - Rust-based transpiler, 20-70x faster than Babel
|
14
|
+
- `esbuild` - Go-based transpiler, extremely fast
|
15
|
+
- `none` - No transpilation (use native JavaScript)
|
16
|
+
|
17
|
+
## Configuration
|
18
|
+
|
19
|
+
Set the transpiler in your `config/shakapacker.yml`:
|
20
|
+
|
21
|
+
```yaml
|
22
|
+
default: &default
|
23
|
+
# For webpack users (babel is default, no change needed)
|
24
|
+
javascript_transpiler: babel
|
25
|
+
|
26
|
+
# To opt-in to SWC for better performance
|
27
|
+
javascript_transpiler: swc
|
28
|
+
|
29
|
+
# For rspack users (swc is default, no change needed)
|
30
|
+
assets_bundler: rspack
|
31
|
+
javascript_transpiler: swc
|
32
|
+
```
|
33
|
+
|
34
|
+
## Migration Guide
|
35
|
+
|
36
|
+
### Migrating from Babel to SWC
|
37
|
+
|
38
|
+
SWC offers significant performance improvements while maintaining high compatibility with Babel.
|
39
|
+
|
40
|
+
#### 1. Install SWC dependencies
|
41
|
+
|
42
|
+
```bash
|
43
|
+
yarn add --dev @swc/core swc-loader
|
44
|
+
```
|
45
|
+
|
46
|
+
#### 2. Update your configuration
|
47
|
+
|
48
|
+
```yaml
|
49
|
+
# config/shakapacker.yml
|
50
|
+
default: &default
|
51
|
+
javascript_transpiler: swc
|
52
|
+
```
|
53
|
+
|
54
|
+
#### 3. Create SWC configuration (optional)
|
55
|
+
|
56
|
+
If you need custom transpilation settings, create `.swcrc`:
|
57
|
+
|
58
|
+
```json
|
59
|
+
{
|
60
|
+
"$schema": "https://json.schemastore.org/swcrc",
|
61
|
+
"jsc": {
|
62
|
+
"parser": {
|
63
|
+
"syntax": "ecmascript",
|
64
|
+
"jsx": true,
|
65
|
+
"dynamicImport": true
|
66
|
+
},
|
67
|
+
"transform": {
|
68
|
+
"react": {
|
69
|
+
"runtime": "automatic"
|
70
|
+
}
|
71
|
+
},
|
72
|
+
"target": "es2015"
|
73
|
+
},
|
74
|
+
"module": {
|
75
|
+
"type": "es6"
|
76
|
+
}
|
77
|
+
}
|
78
|
+
```
|
79
|
+
|
80
|
+
#### 4. Update React configuration (if using React)
|
81
|
+
|
82
|
+
For React projects, ensure you have the correct refresh plugin:
|
83
|
+
|
84
|
+
```bash
|
85
|
+
# For webpack
|
86
|
+
yarn add --dev @pmmmwh/react-refresh-webpack-plugin
|
87
|
+
|
88
|
+
# For rspack
|
89
|
+
yarn add --dev @rspack/plugin-react-refresh
|
90
|
+
```
|
91
|
+
|
92
|
+
### Performance Comparison
|
93
|
+
|
94
|
+
Typical build time improvements when migrating from Babel to SWC:
|
95
|
+
|
96
|
+
| Project Size | Babel | SWC | Improvement |
|
97
|
+
|-------------|-------|-----|-------------|
|
98
|
+
| Small (<100 files) | 5s | 1s | 5x faster |
|
99
|
+
| Medium (100-500 files) | 20s | 3s | 6.7x faster |
|
100
|
+
| Large (500+ files) | 60s | 8s | 7.5x faster |
|
101
|
+
|
102
|
+
### Compatibility Notes
|
103
|
+
|
104
|
+
#### Babel Features Not Yet in SWC
|
105
|
+
|
106
|
+
- Some experimental/stage-0 proposals
|
107
|
+
- Custom Babel plugins (need SWC equivalents)
|
108
|
+
- Babel macros
|
109
|
+
|
110
|
+
#### Migration Checklist
|
111
|
+
|
112
|
+
- [ ] Back up your current configuration
|
113
|
+
- [ ] Install SWC dependencies
|
114
|
+
- [ ] Update `shakapacker.yml`
|
115
|
+
- [ ] Test your build locally
|
116
|
+
- [ ] Run your test suite
|
117
|
+
- [ ] Check browser compatibility
|
118
|
+
- [ ] Deploy to staging environment
|
119
|
+
- [ ] Monitor for any runtime issues
|
120
|
+
|
121
|
+
### Rollback Plan
|
122
|
+
|
123
|
+
If you encounter issues, rolling back is simple:
|
124
|
+
|
125
|
+
```yaml
|
126
|
+
# config/shakapacker.yml
|
127
|
+
default: &default
|
128
|
+
javascript_transpiler: babel # Revert to babel
|
129
|
+
```
|
130
|
+
|
131
|
+
Then rebuild your application:
|
132
|
+
|
133
|
+
```bash
|
134
|
+
bin/shakapacker clobber
|
135
|
+
bin/shakapacker compile
|
136
|
+
```
|
137
|
+
|
138
|
+
## Environment Variables
|
139
|
+
|
140
|
+
You can also control the transpiler via environment variables:
|
141
|
+
|
142
|
+
```bash
|
143
|
+
# Override config file setting
|
144
|
+
SHAKAPACKER_JAVASCRIPT_TRANSPILER=swc bin/shakapacker compile
|
145
|
+
|
146
|
+
# For debugging
|
147
|
+
SHAKAPACKER_DEBUG_CACHE=true bin/shakapacker compile
|
148
|
+
```
|
149
|
+
|
150
|
+
## Troubleshooting
|
151
|
+
|
152
|
+
### Issue: Build fails after switching to SWC
|
153
|
+
|
154
|
+
**Solution**: Ensure all SWC dependencies are installed:
|
155
|
+
|
156
|
+
```bash
|
157
|
+
yarn add --dev @swc/core swc-loader
|
158
|
+
```
|
159
|
+
|
160
|
+
### Issue: React Fast Refresh not working
|
161
|
+
|
162
|
+
**Solution**: Install the correct refresh plugin for your bundler:
|
163
|
+
|
164
|
+
```bash
|
165
|
+
# Webpack
|
166
|
+
yarn add --dev @pmmmwh/react-refresh-webpack-plugin
|
167
|
+
|
168
|
+
# Rspack
|
169
|
+
yarn add --dev @rspack/plugin-react-refresh
|
170
|
+
```
|
171
|
+
|
172
|
+
### Issue: Decorators not working
|
173
|
+
|
174
|
+
**Solution**: Enable decorator support in `.swcrc`:
|
175
|
+
|
176
|
+
```json
|
177
|
+
{
|
178
|
+
"jsc": {
|
179
|
+
"parser": {
|
180
|
+
"decorators": true,
|
181
|
+
"decoratorsBeforeExport": true
|
182
|
+
}
|
183
|
+
}
|
184
|
+
}
|
185
|
+
```
|
186
|
+
|
187
|
+
## Further Reading
|
188
|
+
|
189
|
+
- [SWC Documentation](https://swc.rs/docs/getting-started)
|
190
|
+
- [Babel to SWC Migration Guide](https://swc.rs/docs/migrating-from-babel)
|
191
|
+
- [Rspack Configuration](https://www.rspack.dev/config/index)
|
@@ -0,0 +1,378 @@
|
|
1
|
+
# TypeScript Migration Guide for Shakapacker
|
2
|
+
|
3
|
+
This guide helps you adopt TypeScript types in your Shakapacker configuration files for better type safety and IDE support.
|
4
|
+
|
5
|
+
## Table of Contents
|
6
|
+
- [Benefits](#benefits)
|
7
|
+
- [Quick Start](#quick-start)
|
8
|
+
- [Migration Steps](#migration-steps)
|
9
|
+
- [Common Patterns](#common-patterns)
|
10
|
+
- [Troubleshooting](#troubleshooting)
|
11
|
+
|
12
|
+
## Benefits
|
13
|
+
|
14
|
+
Using TypeScript with Shakapacker provides:
|
15
|
+
- **Type Safety**: Catch configuration errors at compile time
|
16
|
+
- **IDE Support**: Get autocompletion and inline documentation
|
17
|
+
- **Better Refactoring**: Safely rename and restructure configurations
|
18
|
+
- **Self-documenting**: Types serve as inline documentation
|
19
|
+
|
20
|
+
## Quick Start
|
21
|
+
|
22
|
+
### 1. Install TypeScript Dependencies
|
23
|
+
|
24
|
+
```bash
|
25
|
+
yarn add --dev typescript @types/node @types/webpack
|
26
|
+
# or
|
27
|
+
npm install --save-dev typescript @types/node @types/webpack
|
28
|
+
```
|
29
|
+
|
30
|
+
### 2. Create a tsconfig.json
|
31
|
+
|
32
|
+
```json
|
33
|
+
{
|
34
|
+
"compilerOptions": {
|
35
|
+
"target": "ES2020",
|
36
|
+
"module": "commonjs",
|
37
|
+
"lib": ["ES2020"],
|
38
|
+
"strict": true,
|
39
|
+
"esModuleInterop": true,
|
40
|
+
"skipLibCheck": true,
|
41
|
+
"forceConsistentCasingInFileNames": true,
|
42
|
+
"resolveJsonModule": true,
|
43
|
+
"moduleResolution": "node",
|
44
|
+
"allowJs": true,
|
45
|
+
"checkJs": false,
|
46
|
+
"noEmit": true
|
47
|
+
},
|
48
|
+
"include": ["config/webpack/**/*"],
|
49
|
+
"exclude": ["node_modules"]
|
50
|
+
}
|
51
|
+
```
|
52
|
+
|
53
|
+
### 3. Convert Your Webpack Config to TypeScript
|
54
|
+
|
55
|
+
Rename `config/webpack/webpack.config.js` to `config/webpack/webpack.config.ts`:
|
56
|
+
|
57
|
+
```typescript
|
58
|
+
// config/webpack/webpack.config.ts
|
59
|
+
import { generateWebpackConfig, merge } from 'shakapacker'
|
60
|
+
import type { WebpackConfigWithDevServer } from 'shakapacker/types'
|
61
|
+
import type { Configuration } from 'webpack'
|
62
|
+
|
63
|
+
const customConfig: Configuration = {
|
64
|
+
resolve: {
|
65
|
+
extensions: ['.css', '.ts', '.tsx']
|
66
|
+
}
|
67
|
+
}
|
68
|
+
|
69
|
+
const config: Configuration = generateWebpackConfig(customConfig)
|
70
|
+
|
71
|
+
export default config
|
72
|
+
```
|
73
|
+
|
74
|
+
## Migration Steps
|
75
|
+
|
76
|
+
### Step 1: Import Types
|
77
|
+
|
78
|
+
Start by importing the types you need:
|
79
|
+
|
80
|
+
```typescript
|
81
|
+
import type {
|
82
|
+
Config,
|
83
|
+
WebpackConfigWithDevServer,
|
84
|
+
RspackConfigWithDevServer,
|
85
|
+
CompressionPluginOptions
|
86
|
+
} from 'shakapacker/types'
|
87
|
+
```
|
88
|
+
|
89
|
+
### Step 2: Type Your Configuration Objects
|
90
|
+
|
91
|
+
Add type annotations to your configuration objects:
|
92
|
+
|
93
|
+
```typescript
|
94
|
+
// Before (JavaScript)
|
95
|
+
const customConfig = {
|
96
|
+
resolve: {
|
97
|
+
extensions: ['.css', '.ts', '.tsx']
|
98
|
+
}
|
99
|
+
}
|
100
|
+
|
101
|
+
// After (TypeScript)
|
102
|
+
import type { Configuration } from 'webpack'
|
103
|
+
|
104
|
+
const customConfig: Configuration = {
|
105
|
+
resolve: {
|
106
|
+
extensions: ['.css', '.ts', '.tsx']
|
107
|
+
}
|
108
|
+
}
|
109
|
+
```
|
110
|
+
|
111
|
+
### Step 3: Type Your Custom Functions
|
112
|
+
|
113
|
+
If you have custom configuration functions, add type annotations:
|
114
|
+
|
115
|
+
```typescript
|
116
|
+
// Before (JavaScript)
|
117
|
+
function modifyConfig(config) {
|
118
|
+
config.plugins.push(new MyPlugin())
|
119
|
+
return config
|
120
|
+
}
|
121
|
+
|
122
|
+
// After (TypeScript)
|
123
|
+
import type { Configuration } from 'webpack'
|
124
|
+
import type { WebpackPluginInstance } from 'shakapacker/types'
|
125
|
+
|
126
|
+
function modifyConfig(config: Configuration): Configuration {
|
127
|
+
const plugins = config.plugins as WebpackPluginInstance[]
|
128
|
+
plugins.push(new MyPlugin())
|
129
|
+
return config
|
130
|
+
}
|
131
|
+
```
|
132
|
+
|
133
|
+
### Step 4: Handle Environment-Specific Configurations
|
134
|
+
|
135
|
+
```typescript
|
136
|
+
// config/webpack/development.ts
|
137
|
+
import { generateWebpackConfig } from 'shakapacker'
|
138
|
+
import type { WebpackConfigWithDevServer } from 'shakapacker/types'
|
139
|
+
|
140
|
+
const developmentConfig: WebpackConfigWithDevServer = generateWebpackConfig({
|
141
|
+
devtool: 'eval-cheap-module-source-map',
|
142
|
+
devServer: {
|
143
|
+
hot: true,
|
144
|
+
port: 3035
|
145
|
+
}
|
146
|
+
})
|
147
|
+
|
148
|
+
export default developmentConfig
|
149
|
+
```
|
150
|
+
|
151
|
+
## Common Patterns
|
152
|
+
|
153
|
+
### Pattern 1: Custom Loaders
|
154
|
+
|
155
|
+
```typescript
|
156
|
+
import type { RuleSetRule } from 'webpack'
|
157
|
+
|
158
|
+
const customLoader: RuleSetRule = {
|
159
|
+
test: /\.svg$/,
|
160
|
+
use: ['@svgr/webpack']
|
161
|
+
}
|
162
|
+
|
163
|
+
const config: Configuration = generateWebpackConfig({
|
164
|
+
module: {
|
165
|
+
rules: [customLoader]
|
166
|
+
}
|
167
|
+
})
|
168
|
+
```
|
169
|
+
|
170
|
+
### Pattern 2: Plugin Configuration
|
171
|
+
|
172
|
+
```typescript
|
173
|
+
import CompressionPlugin from 'compression-webpack-plugin'
|
174
|
+
import type { CompressionPluginOptions } from 'shakapacker/types'
|
175
|
+
|
176
|
+
const compressionOptions: CompressionPluginOptions = {
|
177
|
+
filename: '[path][base].gz',
|
178
|
+
algorithm: 'gzip',
|
179
|
+
test: /\.(js|css|html|json|ico|svg|eot|otf|ttf)$/
|
180
|
+
}
|
181
|
+
|
182
|
+
const config: Configuration = generateWebpackConfig({
|
183
|
+
plugins: [new CompressionPlugin(compressionOptions)]
|
184
|
+
})
|
185
|
+
```
|
186
|
+
|
187
|
+
### Pattern 3: Conditional Configuration
|
188
|
+
|
189
|
+
```typescript
|
190
|
+
import type { Configuration } from 'webpack'
|
191
|
+
import { env } from 'shakapacker'
|
192
|
+
|
193
|
+
const config: Configuration = generateWebpackConfig()
|
194
|
+
|
195
|
+
if (env.isProduction) {
|
196
|
+
// TypeScript knows config.optimization exists
|
197
|
+
config.optimization = {
|
198
|
+
...config.optimization,
|
199
|
+
minimize: true,
|
200
|
+
sideEffects: false
|
201
|
+
}
|
202
|
+
}
|
203
|
+
|
204
|
+
export default config
|
205
|
+
```
|
206
|
+
|
207
|
+
### Pattern 4: Rspack Configuration
|
208
|
+
|
209
|
+
```typescript
|
210
|
+
// config/rspack/rspack.config.ts
|
211
|
+
import type { RspackConfigWithDevServer } from 'shakapacker/types'
|
212
|
+
import { generateWebpackConfig } from 'shakapacker'
|
213
|
+
|
214
|
+
const rspackConfig: RspackConfigWithDevServer = generateWebpackConfig({
|
215
|
+
mode: 'development',
|
216
|
+
devServer: {
|
217
|
+
hot: true,
|
218
|
+
port: 3036
|
219
|
+
}
|
220
|
+
})
|
221
|
+
|
222
|
+
export default rspackConfig
|
223
|
+
```
|
224
|
+
|
225
|
+
## Type-checking Your Configuration
|
226
|
+
|
227
|
+
Add a script to your package.json to type-check your configuration:
|
228
|
+
|
229
|
+
```json
|
230
|
+
{
|
231
|
+
"scripts": {
|
232
|
+
"type-check": "tsc --noEmit",
|
233
|
+
"webpack:type-check": "tsc --noEmit config/webpack/*.ts"
|
234
|
+
}
|
235
|
+
}
|
236
|
+
```
|
237
|
+
|
238
|
+
Run type checking:
|
239
|
+
|
240
|
+
```bash
|
241
|
+
yarn type-check
|
242
|
+
# or
|
243
|
+
npm run type-check
|
244
|
+
```
|
245
|
+
|
246
|
+
## Available Types Reference
|
247
|
+
|
248
|
+
### Core Types
|
249
|
+
- `Config` - Shakapacker configuration from shakapacker.yml
|
250
|
+
- `Env` - Environment variables and helpers
|
251
|
+
- `DevServerConfig` - Development server configuration
|
252
|
+
|
253
|
+
### Webpack/Rspack Types
|
254
|
+
- `WebpackConfigWithDevServer` - Webpack configuration with dev server
|
255
|
+
- `RspackConfigWithDevServer` - Rspack configuration with dev server
|
256
|
+
- `WebpackPluginInstance` - Webpack plugin instance type
|
257
|
+
- `RspackPlugin` - Rspack plugin interface
|
258
|
+
|
259
|
+
### Helper Types
|
260
|
+
- `CompressionPluginOptions` - Compression plugin configuration
|
261
|
+
- `ReactRefreshWebpackPlugin` - React refresh for Webpack
|
262
|
+
- `ReactRefreshRspackPlugin` - React refresh for Rspack
|
263
|
+
|
264
|
+
## Troubleshooting
|
265
|
+
|
266
|
+
### Issue: "Cannot find module 'shakapacker/types'"
|
267
|
+
|
268
|
+
**Solution**: Make sure you're using Shakapacker v9.0.0 or later:
|
269
|
+
|
270
|
+
```bash
|
271
|
+
yarn upgrade shakapacker
|
272
|
+
```
|
273
|
+
|
274
|
+
### Issue: Type errors with plugins
|
275
|
+
|
276
|
+
**Solution**: Cast plugin arrays when needed:
|
277
|
+
|
278
|
+
```typescript
|
279
|
+
const plugins = (config.plugins || []) as WebpackPluginInstance[]
|
280
|
+
plugins.push(new MyPlugin())
|
281
|
+
```
|
282
|
+
|
283
|
+
### Issue: Missing types for custom loaders
|
284
|
+
|
285
|
+
**Solution**: Install type definitions or declare them:
|
286
|
+
|
287
|
+
```typescript
|
288
|
+
// If types aren't available, declare them
|
289
|
+
declare module 'my-custom-loader' {
|
290
|
+
const loader: any
|
291
|
+
export default loader
|
292
|
+
}
|
293
|
+
```
|
294
|
+
|
295
|
+
### Issue: Conflicting types between webpack versions
|
296
|
+
|
297
|
+
**Solution**: Ensure your webpack types match your webpack version:
|
298
|
+
|
299
|
+
```bash
|
300
|
+
yarn add --dev @types/webpack@^5
|
301
|
+
```
|
302
|
+
|
303
|
+
## Gradual Migration
|
304
|
+
|
305
|
+
You don't need to convert everything at once. Start with:
|
306
|
+
|
307
|
+
1. Convert your main webpack.config.js to TypeScript
|
308
|
+
2. Add types to the most complex configurations
|
309
|
+
3. Gradually type other configuration files
|
310
|
+
4. Add type checking to your CI pipeline
|
311
|
+
|
312
|
+
## Example: Full Configuration
|
313
|
+
|
314
|
+
Here's a complete example of a typed webpack configuration:
|
315
|
+
|
316
|
+
```typescript
|
317
|
+
// config/webpack/webpack.config.ts
|
318
|
+
import { generateWebpackConfig, merge, config as shakapackerConfig } from 'shakapacker'
|
319
|
+
import type { Configuration } from 'webpack'
|
320
|
+
import type { WebpackConfigWithDevServer } from 'shakapacker/types'
|
321
|
+
import CompressionPlugin from 'compression-webpack-plugin'
|
322
|
+
import { resolve } from 'path'
|
323
|
+
|
324
|
+
// Type-safe custom configuration
|
325
|
+
const customConfig: Configuration = {
|
326
|
+
resolve: {
|
327
|
+
extensions: ['.css', '.ts', '.tsx'],
|
328
|
+
alias: {
|
329
|
+
'@': resolve(__dirname, '../../app/javascript'),
|
330
|
+
'components': resolve(__dirname, '../../app/javascript/components'),
|
331
|
+
'utils': resolve(__dirname, '../../app/javascript/utils')
|
332
|
+
}
|
333
|
+
},
|
334
|
+
module: {
|
335
|
+
rules: [
|
336
|
+
{
|
337
|
+
test: /\.svg$/,
|
338
|
+
use: ['@svgr/webpack'],
|
339
|
+
issuer: /\.(tsx?|jsx?)$/
|
340
|
+
}
|
341
|
+
]
|
342
|
+
}
|
343
|
+
}
|
344
|
+
|
345
|
+
// Generate the final configuration
|
346
|
+
const webpackConfig: Configuration = generateWebpackConfig(customConfig)
|
347
|
+
|
348
|
+
// Type-safe modifications based on environment
|
349
|
+
if (shakapackerConfig.env === 'production') {
|
350
|
+
const plugins = (webpackConfig.plugins || []) as WebpackPluginInstance[]
|
351
|
+
|
352
|
+
plugins.push(
|
353
|
+
new CompressionPlugin({
|
354
|
+
filename: '[path][base].br',
|
355
|
+
algorithm: 'brotliCompress',
|
356
|
+
test: /\.(js|css|html|json|ico|svg|eot|otf|ttf)$/
|
357
|
+
})
|
358
|
+
)
|
359
|
+
}
|
360
|
+
|
361
|
+
export default webpackConfig
|
362
|
+
```
|
363
|
+
|
364
|
+
## Next Steps
|
365
|
+
|
366
|
+
After migrating to TypeScript:
|
367
|
+
|
368
|
+
1. **Enable strict checks**: Gradually enable stricter TypeScript options
|
369
|
+
2. **Add custom types**: Create type definitions for your application-specific configurations
|
370
|
+
3. **Share types**: Export reusable configuration types for your team
|
371
|
+
4. **Document with types**: Use JSDoc comments with your types for better documentation
|
372
|
+
|
373
|
+
## Resources
|
374
|
+
|
375
|
+
- [TypeScript Handbook](https://www.typescriptlang.org/docs/handbook/intro.html)
|
376
|
+
- [Webpack TypeScript Configuration](https://webpack.js.org/configuration/configuration-languages/#typescript)
|
377
|
+
- [Shakapacker Types Documentation](./types/README.md)
|
378
|
+
- [Migration Examples](https://github.com/shakacode/shakapacker/tree/main/examples/typescript-config)
|
data/lib/install/template.rb
CHANGED
@@ -125,13 +125,60 @@ if (setup_path = Rails.root.join("bin/setup")).exist?
|
|
125
125
|
end
|
126
126
|
|
127
127
|
Dir.chdir(Rails.root) do
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
128
|
+
# In CI, use the pre-packed tarball if available
|
129
|
+
if ENV["SHAKAPACKER_NPM_PACKAGE"]
|
130
|
+
package_path = ENV["SHAKAPACKER_NPM_PACKAGE"]
|
131
|
+
|
132
|
+
# Validate package path to prevent directory traversal and invalid file types
|
133
|
+
begin
|
134
|
+
# Resolve to absolute path
|
135
|
+
absolute_path = File.expand_path(package_path)
|
136
|
+
|
137
|
+
# Reject paths containing directory traversal
|
138
|
+
if package_path.include?("..") || absolute_path.include?("..")
|
139
|
+
say "❌ Security Error: Package path contains directory traversal: #{package_path}", :red
|
140
|
+
exit 1
|
141
|
+
end
|
142
|
+
|
143
|
+
# Ensure filename ends with .tgz or .tar.gz
|
144
|
+
unless absolute_path.end_with?(".tgz", ".tar.gz")
|
145
|
+
say "❌ Security Error: Package must be a .tgz or .tar.gz file: #{package_path}", :red
|
146
|
+
exit 1
|
147
|
+
end
|
148
|
+
|
149
|
+
# Check existence only after validation
|
150
|
+
if File.exist?(absolute_path)
|
151
|
+
say "📦 Installing shakapacker from local package: #{absolute_path}", :cyan
|
152
|
+
begin
|
153
|
+
@package_json.manager.add!([absolute_path], type: :production)
|
154
|
+
rescue PackageJson::Error
|
155
|
+
say "Shakapacker installation failed 😭 See above for details.", :red
|
156
|
+
exit 1
|
157
|
+
end
|
158
|
+
else
|
159
|
+
say "⚠️ SHAKAPACKER_NPM_PACKAGE set but file not found: #{absolute_path}", :yellow
|
160
|
+
say "Falling back to npm registry...", :yellow
|
161
|
+
npm_version = Shakapacker::Utils::VersionSyntaxConverter.new.rubygem_to_npm(Shakapacker::VERSION)
|
162
|
+
begin
|
163
|
+
@package_json.manager.add!(["shakapacker@#{npm_version}"], type: :production)
|
164
|
+
rescue PackageJson::Error
|
165
|
+
say "Shakapacker installation failed 😭 See above for details.", :red
|
166
|
+
exit 1
|
167
|
+
end
|
168
|
+
end
|
169
|
+
rescue => e
|
170
|
+
say "❌ Error validating package path: #{e.message}", :red
|
171
|
+
exit 1
|
172
|
+
end
|
173
|
+
else
|
174
|
+
npm_version = Shakapacker::Utils::VersionSyntaxConverter.new.rubygem_to_npm(Shakapacker::VERSION)
|
175
|
+
say "Installing shakapacker@#{npm_version}"
|
176
|
+
begin
|
177
|
+
@package_json.manager.add!(["shakapacker@#{npm_version}"], type: :production)
|
178
|
+
rescue PackageJson::Error
|
179
|
+
say "Shakapacker installation failed 😭 See above for details.", :red
|
180
|
+
exit 1
|
181
|
+
end
|
135
182
|
end
|
136
183
|
|
137
184
|
@package_json.merge! do |pj|
|
data/lib/shakapacker/version.rb
CHANGED
data/package/.npmignore
ADDED
@@ -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
|
+
}
|