Skip to content

Commit 64b0ff4

Browse files
justin808claude
andcommitted
Migrate from Babel to SWC transpiler (#666)
This commit migrates the project from Babel to SWC for JavaScript transpilation, aligning with Shakapacker 9.0's default transpiler and providing significant performance improvements. ## Changes ### Configuration - Update shakapacker.yml to use SWC transpiler - Create .swcrc configuration file with equivalent Babel settings - Add config/swc.config.js to customize SWC loader for Shakapacker - Update .eslintrc to use espree parser instead of @babel/eslint-parser ### Dependencies - Remove Babel-specific dependencies: - @babel/cli, @babel/core, @babel/plugin-transform-runtime - @babel/preset-env, @babel/preset-react - @babel/eslint-parser - babel-loader, babel-jest, babel-plugin-macros - babel-plugin-transform-react-remove-prop-types - Add SWC dependencies: - @swc/core, @swc/jest, swc-loader ### Testing Configuration - Update Jest to use @swc/jest instead of babel-jest - Keep @babel/runtime for polyfills ### Cleanup - Remove babel.config.js file - Remove unused Babel configuration ## Benefits - 10-20x faster build times for large codebases - Lower memory footprint during builds - Modern, actively maintained transpiler - Aligns with Shakapacker 9.0 defaults ## Testing - ✅ Test build passes (yarn build:test) - ✅ Development build passes (yarn build:dev) - ✅ ESLint passes - ✅ RuboCop passes 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 79a7efc commit 64b0ff4

File tree

12 files changed

+355
-1129
lines changed

12 files changed

+355
-1129
lines changed

.eslintrc

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,20 @@
1+
root: true
2+
13
extends:
24
- eslint-config-shakacode
35
- plugin:react/recommended
46
- plugin:prettier/recommended
57
- plugin:jsx-a11y/recommended
68
- prettier
79

10+
parser: espree
11+
12+
parserOptions:
13+
ecmaVersion: 2022
14+
sourceType: module
15+
ecmaFeatures:
16+
jsx: true
17+
818
plugins:
919
- react
1020
- jsx-a11y

.swcrc

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
{
2+
"jsc": {
3+
"parser": {
4+
"syntax": "ecmascript",
5+
"jsx": true,
6+
"dynamicImport": true,
7+
"decorators": false
8+
},
9+
"transform": {
10+
"react": {
11+
"runtime": "automatic",
12+
"development": false,
13+
"refresh": true
14+
}
15+
}
16+
},
17+
"module": {
18+
"type": "es6"
19+
},
20+
"env": {
21+
"targets": {
22+
"browsers": [
23+
">1%",
24+
"last 5 versions",
25+
"safari >= 7",
26+
"Firefox ESR",
27+
"not IE 11"
28+
]
29+
},
30+
"coreJs": 3,
31+
"mode": "entry"
32+
}
33+
}

babel.config.js

Lines changed: 0 additions & 32 deletions
This file was deleted.
Lines changed: 101 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -1,129 +1,130 @@
11
// Generated by ReScript, PLEASE EDIT WITH CARE
22

3-
import * as React from "react";
4-
import * as Header from "../../Header/Header.bs.js";
5-
import * as Actions from "../../Actions/Actions.bs.js";
6-
import * as AlertError from "../../CommentList/AlertError/AlertError.bs.js";
7-
import * as ActionCable from "../../bindings/ActionCable.bs.js";
8-
import * as CommentForm from "../../CommentForm/CommentForm.bs.js";
9-
import * as CommentList from "../../CommentList/CommentList.bs.js";
10-
import * as JsxRuntime from "react/jsx-runtime";
3+
import * as React from 'react';
4+
import * as Header from '../../Header/Header.bs.js';
5+
import * as Actions from '../../Actions/Actions.bs.js';
6+
import * as AlertError from '../../CommentList/AlertError/AlertError.bs.js';
7+
import * as ActionCable from '../../bindings/ActionCable.bs.js';
8+
import * as CommentForm from '../../CommentForm/CommentForm.bs.js';
9+
import * as CommentList from '../../CommentList/CommentList.bs.js';
10+
import * as JsxRuntime from 'react/jsx-runtime';
1111

1212
function reducer(param, action) {
13-
if (typeof action !== "object") {
13+
if (typeof action !== 'object') {
1414
return {
15-
commentsFetchStatus: "FetchError"
16-
};
15+
commentsFetchStatus: 'FetchError',
16+
};
1717
} else {
1818
return {
19-
commentsFetchStatus: {
20-
TAG: "CommentsFetched",
21-
_0: action._0
22-
}
23-
};
19+
commentsFetchStatus: {
20+
TAG: 'CommentsFetched',
21+
_0: action._0,
22+
},
23+
};
2424
}
2525
}
2626

2727
function ReScriptShow$default(props) {
2828
var match = React.useReducer(reducer, {
29-
commentsFetchStatus: {
30-
TAG: "CommentsFetched",
31-
_0: []
32-
}
33-
});
29+
commentsFetchStatus: {
30+
TAG: 'CommentsFetched',
31+
_0: [],
32+
},
33+
});
3434
var dispatch = match[1];
3535
var fetchData = async function () {
3636
var comments = await Actions.Fetch.fetchComments();
37-
if (comments.TAG === "Ok") {
37+
if (comments.TAG === 'Ok') {
3838
return dispatch({
39-
TAG: "SetComments",
40-
_0: comments._0
41-
});
39+
TAG: 'SetComments',
40+
_0: comments._0,
41+
});
4242
} else {
43-
return dispatch("SetFetchError");
43+
return dispatch('SetFetchError');
4444
}
4545
};
4646
var subscribeToCommentsChannel = function () {
47-
return ActionCable.subscribeConsumer("CommentsChannel", {
48-
connected: (function () {
49-
console.log("Connected");
50-
}),
51-
received: (function (data) {
52-
dispatch({
53-
TAG: "SetComments",
54-
_0: [data]
55-
});
56-
}),
57-
disconnected: (function () {
58-
console.log("Disconnected");
59-
})
60-
});
47+
return ActionCable.subscribeConsumer('CommentsChannel', {
48+
connected: function () {
49+
console.log('Connected');
50+
},
51+
received: function (data) {
52+
dispatch({
53+
TAG: 'SetComments',
54+
_0: [data],
55+
});
56+
},
57+
disconnected: function () {
58+
console.log('Disconnected');
59+
},
60+
});
6161
};
62-
React.useEffect((function () {
63-
var scription = subscribeToCommentsChannel();
64-
return (function () {
65-
scription.unsubscribe();
66-
});
67-
}), []);
68-
React.useEffect((function () {
69-
fetchData();
70-
}), []);
62+
React.useEffect(function () {
63+
var scription = subscribeToCommentsChannel();
64+
return function () {
65+
scription.unsubscribe();
66+
};
67+
}, []);
68+
React.useEffect(function () {
69+
fetchData();
70+
}, []);
7171
var comments = match[0].commentsFetchStatus;
7272
var tmp;
73-
tmp = typeof comments !== "object" ? JsxRuntime.jsx(AlertError.make, {
74-
errorMsg: "Can't fetch the comments!"
75-
}) : JsxRuntime.jsx(CommentList.make, {
76-
comments: comments._0
73+
tmp =
74+
typeof comments !== 'object'
75+
? JsxRuntime.jsx(AlertError.make, {
76+
errorMsg: "Can't fetch the comments!",
77+
})
78+
: JsxRuntime.jsx(CommentList.make, {
79+
comments: comments._0,
7780
});
78-
return JsxRuntime.jsxs("div", {
79-
children: [
80-
JsxRuntime.jsxs("h2", {
81-
children: [
82-
"Rescript + Rails Backend (with ",
83-
JsxRuntime.jsx("a", {
84-
children: "react_on_rails gem",
85-
href: "https://github.com/shakacode/react_on_rails"
86-
}),
87-
")"
88-
]
89-
}),
90-
JsxRuntime.jsx(Header.make, {}),
91-
JsxRuntime.jsxs("div", {
92-
children: [
93-
JsxRuntime.jsx("h2", {
94-
children: "Comments"
95-
}),
96-
JsxRuntime.jsxs("ul", {
97-
children: [
98-
JsxRuntime.jsx("li", {
99-
children: "Text supports Github Flavored Markdown."
100-
}),
101-
JsxRuntime.jsx("li", {
102-
children: "Comments older than 24 hours are deleted."
103-
}),
104-
JsxRuntime.jsx("li", {
105-
children: "Name is preserved. Text is reset, between submits"
106-
}),
107-
JsxRuntime.jsx("li", {
108-
children: "To see Action Cable instantly update two browsers, open two browsers and submit a comment!"
109-
})
110-
]
111-
}),
112-
JsxRuntime.jsx(CommentForm.make, {
113-
fetchData: fetchData
114-
}),
115-
tmp
116-
],
117-
className: "prose max-w-none prose-a:text-sky-700 prose-li:my-0"
118-
})
119-
]
120-
});
81+
return JsxRuntime.jsxs('div', {
82+
children: [
83+
JsxRuntime.jsxs('h2', {
84+
children: [
85+
'Rescript + Rails Backend (with ',
86+
JsxRuntime.jsx('a', {
87+
children: 'react_on_rails gem',
88+
href: 'https://github.com/shakacode/react_on_rails',
89+
}),
90+
')',
91+
],
92+
}),
93+
JsxRuntime.jsx(Header.make, {}),
94+
JsxRuntime.jsxs('div', {
95+
children: [
96+
JsxRuntime.jsx('h2', {
97+
children: 'Comments',
98+
}),
99+
JsxRuntime.jsxs('ul', {
100+
children: [
101+
JsxRuntime.jsx('li', {
102+
children: 'Text supports Github Flavored Markdown.',
103+
}),
104+
JsxRuntime.jsx('li', {
105+
children: 'Comments older than 24 hours are deleted.',
106+
}),
107+
JsxRuntime.jsx('li', {
108+
children: 'Name is preserved. Text is reset, between submits',
109+
}),
110+
JsxRuntime.jsx('li', {
111+
children:
112+
'To see Action Cable instantly update two browsers, open two browsers and submit a comment!',
113+
}),
114+
],
115+
}),
116+
JsxRuntime.jsx(CommentForm.make, {
117+
fetchData: fetchData,
118+
}),
119+
tmp,
120+
],
121+
className: 'prose max-w-none prose-a:text-sky-700 prose-li:my-0',
122+
}),
123+
],
124+
});
121125
}
122126

123127
var $$default = ReScriptShow$default;
124128

125-
export {
126-
reducer ,
127-
$$default as default,
128-
}
129+
export { reducer, $$default as default };
129130
/* react Not a pure module */

client/app/packs/server-bundle.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
import './stores-registration';
33

44
// import statement added by react_on_rails:generate_packs rake task
5-
import "./../generated/server-bundle-generated.js";
5+
import '../generated/server-bundle-generated.js';

config/shakapacker.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ default: &default
88
cache_path: tmp/shakapacker
99
webpack_compile_output: true
1010
nested_entries: true
11-
javascript_transpiler: babel
11+
javascript_transpiler: swc
1212

1313
# Additional paths webpack should lookup modules
1414
# ['app/assets', 'engine/foo/app/assets']

config/swc.config.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
module.exports = {
2+
options: {
3+
jsc: {
4+
parser: {
5+
syntax: 'ecmascript',
6+
jsx: true,
7+
dynamicImport: true,
8+
decorators: false,
9+
},
10+
transform: {
11+
react: {
12+
runtime: 'automatic',
13+
development: process.env.NODE_ENV === 'development',
14+
refresh: process.env.WEBPACK_SERVE === 'true',
15+
},
16+
},
17+
loose: true,
18+
},
19+
module: {
20+
type: 'es6',
21+
},
22+
sourceMaps: true,
23+
// Use env instead of jsc.target (they're mutually exclusive)
24+
env: {
25+
targets: {
26+
browsers: ['>1%', 'last 5 versions', 'safari >= 7', 'Firefox ESR', 'not IE 11'],
27+
},
28+
coreJs: 3,
29+
mode: 'entry',
30+
},
31+
},
32+
};

config/webpack/clientWebpackConfig.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,3 @@ const configureClient = () => {
2525
};
2626

2727
module.exports = configureClient;
28-

config/webpack/commonWebpackConfig.js

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ const ignoreWarningsConfig = {
2323
ignoreWarnings: [/Module not found: Error: Can't resolve 'react-dom\/client'/],
2424
};
2525

26-
const scssConfigIndex = baseClientWebpackConfig.module.rules.findIndex((config) =>
27-
'.scss'.match(config.test) && config.use,
26+
const scssConfigIndex = baseClientWebpackConfig.module.rules.findIndex(
27+
(config) => '.scss'.match(config.test) && config.use,
2828
);
2929

3030
if (scssConfigIndex === -1) {
@@ -45,8 +45,8 @@ if (scssConfigIndex === -1) {
4545
scssRule.use[sassLoaderIndex] = {
4646
loader: sassLoader,
4747
options: {
48-
api: 'modern'
49-
}
48+
api: 'modern',
49+
},
5050
};
5151
} else {
5252
sassLoader.options = sassLoader.options || {};
@@ -72,4 +72,3 @@ if (scssConfigIndex === -1) {
7272
const commonWebpackConfig = () => merge({}, baseClientWebpackConfig, commonOptions, ignoreWarningsConfig);
7373

7474
module.exports = commonWebpackConfig;
75-

0 commit comments

Comments
 (0)