Skip to content

Commit dc1f658

Browse files
authored
Resolve Babel 8 migration follow-ups (#1212)
## Summary Resolves the remaining Babel 8 migration follow-ups from PR #1187: - converts missing `@babel/core` package metadata lookups into Shakapacker-style install guidance instead of a raw module-resolution stack trace - documents the Babel 8 target-default change so apps can preserve the old Babel 7 all-browser output policy when needed - makes React runtime selection explicit in the Babel customization docs so upgrades do not silently inherit Babel 8's `automatic` runtime default Closes #1194. ## Validation - `node node_modules/jest/bin/jest.js test/package/rules/babel.test.js --runInBand` - `node node_modules/eslint/bin/eslint.js package/rules/babel.ts test/package/rules/babel.test.js` - `node node_modules/prettier/bin/prettier.cjs --check package/rules/babel.ts test/package/rules/babel.test.js docs/customizing_babel_config.md` - `node node_modules/typescript/bin/tsc --noEmit` - `git diff --check origin/main...HEAD` ## Notes `CHANGELOG.md` already has the PR #1187 Babel 8 support entry in the v10.2.0 section on current `main`, so this follow-up does not add duplicate changelog coverage.
1 parent eab1306 commit dc1f658

4 files changed

Lines changed: 75 additions & 6 deletions

File tree

docs/customizing_babel_config.md

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,9 @@ module.exports = function (api) {
6565
[
6666
"@babel/preset-react",
6767
{
68+
// Babel 7 defaults to "classic"; Babel 8 defaults to "automatic".
69+
// Choose the runtime intentionally when upgrading.
70+
runtime: "classic",
6871
development: isDevelopmentEnv || isTestEnv
6972
}
7073
]
@@ -97,10 +100,22 @@ npm install --save-dev @babel/core@^8 @babel/plugin-transform-runtime@^8 @babel/
97100
```
98101

99102
Babel 8 requires Node `^22.18.0 || >=24.11.0` while running the build. It also
100-
removed some Babel 7 configuration options. The Shakapacker preset omits those
101-
removed options when Babel 8 is running, but app-level custom Babel config should
102-
also avoid Babel 7-only options such as `useBuiltIns` on `@babel/preset-react`
103-
or `helpers` on `@babel/plugin-transform-runtime`.
103+
removed some Babel 7 configuration options and changed several defaults. The
104+
Shakapacker preset omits options removed by Babel 8 when Babel 8 is running, but
105+
app-level custom Babel config should also avoid Babel 7-only options such as
106+
`useBuiltIns` on `@babel/preset-react`, `useBuiltIns` or `corejs` on
107+
`@babel/preset-env`, or `helpers` on `@babel/plugin-transform-runtime`.
108+
109+
If your app does not have a Browserslist or top-level Babel `targets` setting,
110+
Babel 8's no-target fallback is different from Babel 7's historical all-browser
111+
fallback. Add an explicit top-level `targets` value before upgrading when you
112+
need to preserve the old output policy.
113+
114+
If you use `@babel/preset-react` or `@babel/plugin-transform-react-jsx`, choose
115+
the React runtime explicitly. Babel 7 defaulted to `runtime: "classic"` while
116+
Babel 8 defaults to `runtime: "automatic"`. Keep `runtime: "classic"` to
117+
preserve the previous transform behavior, or switch to `runtime: "automatic"`
118+
intentionally after confirming your React/SSR setup supports it.
104119

105120
Shakapacker validates the loader/core pairing when `javascript_transpiler:
106121
"babel"` is active. If your app installs Babel 8, install `babel-loader` 10 or

package/rules/babel.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,30 @@
11
const { loaderMatches, packageMajorVersion } = require("../utils/helpers")
2+
const { isModuleNotFoundError } = require("../utils/errorHelpers")
23
const { javascript_transpiler: javascriptTranspiler } = require("../config")
34
const { isProduction } = require("../env")
45
const jscommon = require("./jscommon")
56

7+
const babelCoreMajorVersion = (): number => {
8+
try {
9+
return packageMajorVersion("@babel/core")
10+
} catch (error: unknown) {
11+
if (!isModuleNotFoundError(error)) {
12+
throw error
13+
}
14+
15+
throw new Error(
16+
"Your Shakapacker config specified using Babel, but @babel/core package is not installed.\n" +
17+
"\nTo fix this issue, run one of the following commands:\n" +
18+
" npm install --save-dev @babel/core\n" +
19+
" yarn add --dev @babel/core\n" +
20+
"\nOr change your 'javascript_transpiler' setting in shakapacker.yml to use a different loader."
21+
)
22+
}
23+
}
24+
625
const validateBabelLoaderCompatibility = (): void => {
726
if (
8-
packageMajorVersion("@babel/core") >= 8 &&
27+
babelCoreMajorVersion() >= 8 &&
928
packageMajorVersion("babel-loader") < 10
1029
) {
1130
throw new Error(

spec/dummy/Gemfile.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
PATH
22
remote: ../..
33
specs:
4-
shakapacker (10.2.0)
4+
shakapacker (10.3.0)
55
activesupport (>= 5.2)
66
package_json
77
rack-proxy (>= 0.6.1)

test/package/rules/babel.test.js

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,35 @@ const loadBabelRuleWithPackageMajors = (packageMajors) => {
4848
return require("../../../package/rules/babel")
4949
}
5050

51+
const loadBabelRuleWithMissingBabelCore = () => {
52+
jest.resetModules()
53+
jest.doMock("../../../package/config", () => ({
54+
javascript_transpiler: "babel"
55+
}))
56+
jest.doMock("../../../package/env", () => ({
57+
isProduction: false
58+
}))
59+
jest.doMock("../../../package/rules/jscommon", () => ({}))
60+
jest.doMock("../../../package/utils/errorHelpers", () => ({
61+
isModuleNotFoundError: (error) => error?.code === "MODULE_NOT_FOUND"
62+
}))
63+
jest.doMock("../../../package/utils/helpers", () => ({
64+
loaderMatches: (_configLoader, _loaderToCheck, ruleFactory) =>
65+
ruleFactory(),
66+
packageMajorVersion: (packageName) => {
67+
if (packageName === "@babel/core") {
68+
const error = new Error("Cannot find module '@babel/core/package.json'")
69+
error.code = "MODULE_NOT_FOUND"
70+
throw error
71+
}
72+
73+
return 10
74+
}
75+
}))
76+
77+
return require("../../../package/rules/babel")
78+
}
79+
5180
// Skip tests if babel config is not available (not the active transpiler)
5281
if (!babelConfig) {
5382
// eslint-disable-next-line jest/no-disabled-tests
@@ -141,6 +170,12 @@ describe("babel loader compatibility", () => {
141170
}).use[0].loader
142171
).toContain("babel-loader")
143172
})
173+
174+
test("raises an actionable error when @babel/core is missing", () => {
175+
expect(() => loadBabelRuleWithMissingBabelCore()).toThrow(
176+
/@babel\/core package is not installed/
177+
)
178+
})
144179
})
145180

146181
describe("babel preset", () => {

0 commit comments

Comments
 (0)