shakapacker 9.0.0.beta.8 ā 9.0.0.beta.10
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/.husky/pre-commit +2 -0
- data/CHANGELOG.md +119 -97
- data/Gemfile.lock +1 -1
- data/README.md +3 -2
- data/TODO_v9.md +5 -2
- data/conductor-setup.sh +12 -0
- data/docs/css-modules-export-mode.md +102 -8
- data/docs/v9_upgrade.md +131 -23
- data/lib/shakapacker/doctor.rb +94 -0
- data/lib/shakapacker/swc_migrator.rb +60 -2
- data/lib/shakapacker/version.rb +1 -1
- data/lib/shakapacker.rb +1 -1
- data/package/env.ts +25 -13
- data/package/utils/getStyleRule.ts +7 -2
- data/package.json +1 -1
- data/test/package/config.test.js +3 -0
- data/test/package/env.test.js +42 -7
- data/test/package/environments/base.test.js +4 -0
- data/test/package/staging.test.js +4 -3
- data/tools/README.md +2 -2
- metadata +3 -2
|
@@ -30,6 +30,15 @@ module Shakapacker
|
|
|
30
30
|
"swc-loader" => "^0.2.6"
|
|
31
31
|
}.freeze
|
|
32
32
|
|
|
33
|
+
ESLINT_CONFIG_FILES = %w[
|
|
34
|
+
.eslintrc
|
|
35
|
+
.eslintrc.js
|
|
36
|
+
.eslintrc.cjs
|
|
37
|
+
.eslintrc.yaml
|
|
38
|
+
.eslintrc.yml
|
|
39
|
+
.eslintrc.json
|
|
40
|
+
].freeze
|
|
41
|
+
|
|
33
42
|
DEFAULT_SWCRC_CONFIG = {
|
|
34
43
|
"jsc" => {
|
|
35
44
|
"parser" => {
|
|
@@ -41,8 +50,7 @@ module Shakapacker
|
|
|
41
50
|
"react" => {
|
|
42
51
|
"runtime" => "automatic"
|
|
43
52
|
}
|
|
44
|
-
}
|
|
45
|
-
"target" => "es2015"
|
|
53
|
+
}
|
|
46
54
|
},
|
|
47
55
|
"module" => {
|
|
48
56
|
"type" => "es6"
|
|
@@ -67,6 +75,10 @@ module Shakapacker
|
|
|
67
75
|
logger.info "š Migration to SWC complete!"
|
|
68
76
|
logger.info " Note: SWC is approximately 20x faster than Babel for transpilation."
|
|
69
77
|
logger.info " Please test your application thoroughly after migration."
|
|
78
|
+
logger.info "\nš Configuration Info:"
|
|
79
|
+
logger.info " - .swcrc provides base configuration for all environments"
|
|
80
|
+
logger.info " - The SWC loader adds automatic environment targeting (via 'env' setting)"
|
|
81
|
+
logger.info " - You can customize .swcrc, but avoid setting 'jsc.target' as it conflicts with 'env'"
|
|
70
82
|
|
|
71
83
|
# Show cleanup recommendations if babel packages found
|
|
72
84
|
if results[:babel_packages_found].any?
|
|
@@ -99,6 +111,16 @@ module Shakapacker
|
|
|
99
111
|
return { removed_packages: [], config_files_deleted: [] }
|
|
100
112
|
end
|
|
101
113
|
|
|
114
|
+
# Check if ESLint uses Babel parser
|
|
115
|
+
if eslint_uses_babel?
|
|
116
|
+
logger.info "\nā ļø WARNING: ESLint configuration detected that may use Babel"
|
|
117
|
+
logger.info " If you use @babel/eslint-parser or babel-eslint, you may need to:"
|
|
118
|
+
logger.info " 1. Keep @babel/core and related Babel packages for ESLint"
|
|
119
|
+
logger.info " 2. Or switch to @typescript-eslint/parser for TypeScript files"
|
|
120
|
+
logger.info " 3. Or use espree (ESLint's default parser) for JavaScript files"
|
|
121
|
+
logger.info "\n Proceeding with Babel package removal. Check your ESLint config after."
|
|
122
|
+
end
|
|
123
|
+
|
|
102
124
|
removed_packages = remove_babel_from_package_json(package_json_path)
|
|
103
125
|
deleted_files = delete_babel_config_files
|
|
104
126
|
|
|
@@ -132,6 +154,42 @@ module Shakapacker
|
|
|
132
154
|
|
|
133
155
|
private
|
|
134
156
|
|
|
157
|
+
def eslint_uses_babel?
|
|
158
|
+
# Check for ESLint config files
|
|
159
|
+
# Note: This is a heuristic check that may have false positives (e.g., in comments),
|
|
160
|
+
# but false positives only result in an extra warning, which is safer than silently
|
|
161
|
+
# breaking ESLint configurations.
|
|
162
|
+
ESLINT_CONFIG_FILES.each do |config_file|
|
|
163
|
+
config_path = root_path.join(config_file)
|
|
164
|
+
next unless config_path.exist?
|
|
165
|
+
|
|
166
|
+
content = File.read(config_path)
|
|
167
|
+
# Check for Babel parser references
|
|
168
|
+
return true if content.match?(/@babel\/eslint-parser|babel-eslint/)
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
# Check package.json for eslintConfig
|
|
172
|
+
package_json_path = root_path.join("package.json")
|
|
173
|
+
if package_json_path.exist?
|
|
174
|
+
begin
|
|
175
|
+
package_json = JSON.parse(File.read(package_json_path))
|
|
176
|
+
if package_json["eslintConfig"]
|
|
177
|
+
return true if package_json["eslintConfig"].to_json.match?(/@babel\/eslint-parser|babel-eslint/)
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
# Check if Babel ESLint packages are installed
|
|
181
|
+
dependencies = package_json["dependencies"] || {}
|
|
182
|
+
dev_dependencies = package_json["devDependencies"] || {}
|
|
183
|
+
all_deps = dependencies.merge(dev_dependencies)
|
|
184
|
+
return true if all_deps.key?("@babel/eslint-parser") || all_deps.key?("babel-eslint")
|
|
185
|
+
rescue JSON::ParserError => e
|
|
186
|
+
logger.debug "Could not parse package.json for ESLint detection: #{e.message}"
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
false
|
|
191
|
+
end
|
|
192
|
+
|
|
135
193
|
def update_shakapacker_config
|
|
136
194
|
config_path = root_path.join("config/shakapacker.yml")
|
|
137
195
|
return false unless config_path.exist?
|
data/lib/shakapacker/version.rb
CHANGED
data/lib/shakapacker.rb
CHANGED
data/package/env.ts
CHANGED
|
@@ -6,21 +6,32 @@ const { isFileNotFoundError } = require("./utils/errorHelpers")
|
|
|
6
6
|
const { sanitizeEnvValue } = require("./utils/pathValidation")
|
|
7
7
|
|
|
8
8
|
const NODE_ENVIRONMENTS = ["development", "production", "test"] as const
|
|
9
|
-
const DEFAULT = "production"
|
|
10
9
|
|
|
11
10
|
// Sanitize environment variables to prevent injection
|
|
12
11
|
const initialRailsEnv = sanitizeEnvValue(process.env.RAILS_ENV)
|
|
13
12
|
const rawNodeEnv = sanitizeEnvValue(process.env.NODE_ENV)
|
|
14
13
|
|
|
14
|
+
// Default NODE_ENV based on RAILS_ENV to match bin/shakapacker behavior (see lib/shakapacker/runner.rb:27)
|
|
15
|
+
// - RAILS_ENV=production ā DEFAULT="production" (safe for production builds)
|
|
16
|
+
// - RAILS_ENV=development, test, staging, or unset ā DEFAULT="development" (good DX for dev server)
|
|
17
|
+
// This ensures the dev server works out of the box without requiring NODE_ENV to be set explicitly
|
|
18
|
+
const DEFAULT = initialRailsEnv === "production" ? "production" : "development"
|
|
19
|
+
|
|
15
20
|
// Validate NODE_ENV strictly
|
|
16
21
|
const nodeEnv =
|
|
17
|
-
rawNodeEnv &&
|
|
22
|
+
rawNodeEnv &&
|
|
23
|
+
NODE_ENVIRONMENTS.includes(rawNodeEnv as (typeof NODE_ENVIRONMENTS)[number])
|
|
24
|
+
? rawNodeEnv
|
|
25
|
+
: DEFAULT
|
|
18
26
|
|
|
19
27
|
// Log warning if NODE_ENV was invalid
|
|
20
|
-
if (
|
|
28
|
+
if (
|
|
29
|
+
rawNodeEnv &&
|
|
30
|
+
!NODE_ENVIRONMENTS.includes(rawNodeEnv as (typeof NODE_ENVIRONMENTS)[number])
|
|
31
|
+
) {
|
|
21
32
|
console.warn(
|
|
22
33
|
`[SHAKAPACKER WARNING] Invalid NODE_ENV value: ${rawNodeEnv}. ` +
|
|
23
|
-
|
|
34
|
+
`Valid values are: ${NODE_ENVIRONMENTS.join(", ")}. Using default: ${DEFAULT}`
|
|
24
35
|
)
|
|
25
36
|
}
|
|
26
37
|
|
|
@@ -42,13 +53,13 @@ try {
|
|
|
42
53
|
} catch (defaultError) {
|
|
43
54
|
throw new Error(
|
|
44
55
|
`Failed to load Shakapacker configuration.\n` +
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
56
|
+
`Neither user config (${configPath}) nor default config (${defaultConfigPath}) could be loaded.\n\n` +
|
|
57
|
+
`To fix this issue:\n` +
|
|
58
|
+
`1. Create a config/shakapacker.yml file in your project\n` +
|
|
59
|
+
`2. Or set the SHAKAPACKER_CONFIG environment variable to point to your config file\n` +
|
|
60
|
+
`3. Or reinstall Shakapacker to restore the default configuration:\n` +
|
|
61
|
+
` npm install shakapacker --force\n` +
|
|
62
|
+
` yarn add shakapacker --force`
|
|
52
63
|
)
|
|
53
64
|
}
|
|
54
65
|
} else {
|
|
@@ -61,13 +72,14 @@ const regex = new RegExp(`^(${availableEnvironments})$`, "g")
|
|
|
61
72
|
|
|
62
73
|
const runningWebpackDevServer = process.env.WEBPACK_SERVE === "true"
|
|
63
74
|
|
|
64
|
-
const validatedRailsEnv =
|
|
75
|
+
const validatedRailsEnv =
|
|
76
|
+
initialRailsEnv && initialRailsEnv.match(regex) ? initialRailsEnv : DEFAULT
|
|
65
77
|
|
|
66
78
|
if (initialRailsEnv && validatedRailsEnv !== initialRailsEnv) {
|
|
67
79
|
/* eslint no-console:0 */
|
|
68
80
|
console.warn(
|
|
69
81
|
`[SHAKAPACKER WARNING] Environment '${initialRailsEnv}' not found in the configuration.\n` +
|
|
70
|
-
|
|
82
|
+
`Using '${DEFAULT}' configuration as a fallback.`
|
|
71
83
|
)
|
|
72
84
|
}
|
|
73
85
|
|
|
@@ -33,7 +33,12 @@ const getStyleRule = (test: RegExp, preprocessors: any[] = []): StyleRule | null
|
|
|
33
33
|
sourceMap: true,
|
|
34
34
|
importLoaders: 2,
|
|
35
35
|
modules: {
|
|
36
|
-
auto: true
|
|
36
|
+
auto: true,
|
|
37
|
+
// v9 defaults: Use named exports with camelCase conversion
|
|
38
|
+
// Note: css-loader requires 'camelCaseOnly' or 'dashesOnly' when namedExport is true
|
|
39
|
+
// Using 'camelCase' with namedExport: true causes a build error
|
|
40
|
+
namedExport: true,
|
|
41
|
+
exportLocalsConvention: 'camelCaseOnly'
|
|
37
42
|
}
|
|
38
43
|
}
|
|
39
44
|
},
|
|
@@ -56,4 +61,4 @@ const getStyleRule = (test: RegExp, preprocessors: any[] = []): StyleRule | null
|
|
|
56
61
|
return null
|
|
57
62
|
}
|
|
58
63
|
|
|
59
|
-
export = { getStyleRule }
|
|
64
|
+
export = { getStyleRule }
|
data/package.json
CHANGED
data/test/package/config.test.js
CHANGED
|
@@ -71,6 +71,7 @@ describe("Config", () => {
|
|
|
71
71
|
})
|
|
72
72
|
|
|
73
73
|
test("should allow enabling integrity", () => {
|
|
74
|
+
process.env.RAILS_ENV = "production"
|
|
74
75
|
process.env.SHAKAPACKER_CONFIG = "config/shakapacker_integrity.yml"
|
|
75
76
|
const config = require("../../package/config")
|
|
76
77
|
|
|
@@ -78,6 +79,7 @@ describe("Config", () => {
|
|
|
78
79
|
})
|
|
79
80
|
|
|
80
81
|
test("should allow configuring hash functions", () => {
|
|
82
|
+
process.env.RAILS_ENV = "production"
|
|
81
83
|
process.env.SHAKAPACKER_CONFIG = "config/shakapacker_integrity.yml"
|
|
82
84
|
const config = require("../../package/config")
|
|
83
85
|
|
|
@@ -89,6 +91,7 @@ describe("Config", () => {
|
|
|
89
91
|
})
|
|
90
92
|
|
|
91
93
|
test("should allow configuring crossorigin", () => {
|
|
94
|
+
process.env.RAILS_ENV = "production"
|
|
92
95
|
process.env.SHAKAPACKER_CONFIG = "config/shakapacker_integrity.yml"
|
|
93
96
|
const config = require("../../package/config")
|
|
94
97
|
|
data/test/package/env.test.js
CHANGED
|
@@ -24,6 +24,18 @@ describe("Env", () => {
|
|
|
24
24
|
delete process.env.NODE_ENV
|
|
25
25
|
expect(require("../../package/env")).toStrictEqual({
|
|
26
26
|
railsEnv: "development",
|
|
27
|
+
nodeEnv: "development",
|
|
28
|
+
isProduction: false,
|
|
29
|
+
isDevelopment: true,
|
|
30
|
+
runningWebpackDevServer: false
|
|
31
|
+
})
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
test("with undefined NODE_ENV and RAILS_ENV set to production", () => {
|
|
35
|
+
process.env.RAILS_ENV = "production"
|
|
36
|
+
delete process.env.NODE_ENV
|
|
37
|
+
expect(require("../../package/env")).toStrictEqual({
|
|
38
|
+
railsEnv: "production",
|
|
27
39
|
nodeEnv: "production",
|
|
28
40
|
isProduction: true,
|
|
29
41
|
isDevelopment: false,
|
|
@@ -35,10 +47,10 @@ describe("Env", () => {
|
|
|
35
47
|
delete process.env.NODE_ENV
|
|
36
48
|
delete process.env.RAILS_ENV
|
|
37
49
|
expect(require("../../package/env")).toStrictEqual({
|
|
38
|
-
railsEnv: "
|
|
39
|
-
nodeEnv: "
|
|
40
|
-
isProduction:
|
|
41
|
-
isDevelopment:
|
|
50
|
+
railsEnv: "development",
|
|
51
|
+
nodeEnv: "development",
|
|
52
|
+
isProduction: false,
|
|
53
|
+
isDevelopment: true,
|
|
42
54
|
runningWebpackDevServer: false
|
|
43
55
|
})
|
|
44
56
|
})
|
|
@@ -48,10 +60,33 @@ describe("Env", () => {
|
|
|
48
60
|
process.env.NODE_ENV = "staging"
|
|
49
61
|
expect(require("../../package/env")).toStrictEqual({
|
|
50
62
|
railsEnv: "staging",
|
|
51
|
-
nodeEnv: "
|
|
52
|
-
isProduction:
|
|
53
|
-
isDevelopment:
|
|
63
|
+
nodeEnv: "development",
|
|
64
|
+
isProduction: false,
|
|
65
|
+
isDevelopment: true,
|
|
66
|
+
runningWebpackDevServer: false
|
|
67
|
+
})
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
test("rejects malicious NODE_ENV values and uses default", () => {
|
|
71
|
+
process.env.RAILS_ENV = "development"
|
|
72
|
+
process.env.NODE_ENV = "../../../etc/passwd"
|
|
73
|
+
expect(require("../../package/env")).toStrictEqual({
|
|
74
|
+
railsEnv: "development",
|
|
75
|
+
nodeEnv: "development",
|
|
76
|
+
isProduction: false,
|
|
77
|
+
isDevelopment: true,
|
|
54
78
|
runningWebpackDevServer: false
|
|
55
79
|
})
|
|
56
80
|
})
|
|
81
|
+
|
|
82
|
+
test("warns when NODE_ENV is invalid", () => {
|
|
83
|
+
const consoleSpy = jest.spyOn(console, "warn").mockImplementation()
|
|
84
|
+
process.env.NODE_ENV = "invalid"
|
|
85
|
+
delete process.env.RAILS_ENV
|
|
86
|
+
require("../../package/env")
|
|
87
|
+
expect(consoleSpy).toHaveBeenCalledWith(
|
|
88
|
+
expect.stringContaining("Invalid NODE_ENV value: invalid")
|
|
89
|
+
)
|
|
90
|
+
consoleSpy.mockRestore()
|
|
91
|
+
})
|
|
57
92
|
})
|
|
@@ -7,6 +7,10 @@ const { chdirTestApp, resetEnv } = require("../../helpers")
|
|
|
7
7
|
const rootPath = process.cwd()
|
|
8
8
|
chdirTestApp()
|
|
9
9
|
|
|
10
|
+
// Set NODE_ENV before requiring modules to ensure contenthash is enabled
|
|
11
|
+
// Base config tests expect production-like behavior with contenthash
|
|
12
|
+
process.env.NODE_ENV = "production"
|
|
13
|
+
|
|
10
14
|
const baseConfig = require("../../../package/environments/base")
|
|
11
15
|
const config = require("../../../package/config")
|
|
12
16
|
|
|
@@ -19,7 +19,7 @@ describe("Custom environment", () => {
|
|
|
19
19
|
describe("generateWebpackConfig", () => {
|
|
20
20
|
beforeEach(() => jest.resetModules())
|
|
21
21
|
|
|
22
|
-
test("should use staging config and default
|
|
22
|
+
test("should use staging config and default development environment", () => {
|
|
23
23
|
process.env.RAILS_ENV = "staging"
|
|
24
24
|
delete process.env.NODE_ENV
|
|
25
25
|
|
|
@@ -31,9 +31,10 @@ describe("Custom environment", () => {
|
|
|
31
31
|
resolve("public", "packs-staging")
|
|
32
32
|
)
|
|
33
33
|
expect(webpackConfig.output.publicPath).toBe("/packs-staging/")
|
|
34
|
+
// With the NODE_ENV fix, staging now defaults to development environment
|
|
35
|
+
// instead of production, providing better DX for staging environments
|
|
34
36
|
expect(webpackConfig).toMatchObject({
|
|
35
|
-
devtool: "source-map"
|
|
36
|
-
stats: "normal"
|
|
37
|
+
devtool: "cheap-module-source-map"
|
|
37
38
|
})
|
|
38
39
|
})
|
|
39
40
|
})
|
data/tools/README.md
CHANGED
|
@@ -104,7 +104,7 @@ const Button: React.FC = () => {
|
|
|
104
104
|
|
|
105
105
|
### Notes
|
|
106
106
|
|
|
107
|
-
1. **Kebab-case conversion**: CSS classes with kebab-case (e.g., `my-button`) are automatically converted to camelCase (`myButton`) for JavaScript files, matching css-loader's `exportLocalsConvention: '
|
|
107
|
+
1. **Kebab-case conversion**: CSS classes with kebab-case (e.g., `my-button`) are automatically converted to camelCase (`myButton`) for JavaScript files, matching css-loader's `exportLocalsConvention: 'camelCaseOnly'` setting.
|
|
108
108
|
|
|
109
109
|
2. **Unused imports**: The codemod only imports CSS classes that are actually used in JavaScript files. If you pass the entire styles object to a component, it will convert to namespace import for safety.
|
|
110
110
|
|
|
@@ -121,4 +121,4 @@ const Button: React.FC = () => {
|
|
|
121
121
|
**Solution**: Ensure your TypeScript definitions are updated as shown in the [v9 Upgrade Guide](../docs/v9_upgrade.md).
|
|
122
122
|
|
|
123
123
|
**Issue**: Runtime errors about missing CSS classes
|
|
124
|
-
**Solution**: Check if you have kebab-case class names that need camelCase conversion.
|
|
124
|
+
**Solution**: Check if you have kebab-case class names that need camelCase conversion.
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: shakapacker
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 9.0.0.beta.
|
|
4
|
+
version: 9.0.0.beta.10
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- David Heinemeier Hansson
|
|
@@ -147,6 +147,7 @@ files:
|
|
|
147
147
|
- ".github/workflows/ruby.yml"
|
|
148
148
|
- ".github/workflows/test-bundlers.yml"
|
|
149
149
|
- ".gitignore"
|
|
150
|
+
- ".husky/pre-commit"
|
|
150
151
|
- ".node-version"
|
|
151
152
|
- ".npmignore"
|
|
152
153
|
- ".rspec"
|
|
@@ -346,7 +347,7 @@ homepage: https://github.com/shakacode/shakapacker
|
|
|
346
347
|
licenses:
|
|
347
348
|
- MIT
|
|
348
349
|
metadata:
|
|
349
|
-
source_code_uri: https://github.com/shakacode/shakapacker/tree/v9.0.0.beta.
|
|
350
|
+
source_code_uri: https://github.com/shakacode/shakapacker/tree/v9.0.0.beta.10
|
|
350
351
|
rdoc_options: []
|
|
351
352
|
require_paths:
|
|
352
353
|
- lib
|