-
-
Notifications
You must be signed in to change notification settings - Fork 95
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Use SWC loader #29
Use SWC loader #29
Changes from all commits
0963dbf
4c682e8
0a17f28
bdeddc2
90d49e7
68c5126
658cb86
7ab6fb7
05051fb
a38fbcc
aa37020
4302084
747092d
be1282b
62febd2
23f9457
959ec56
571baa6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
# Using SWC Loader | ||
|
||
:warning: This feature is currently experimental. If you face any issues, please report at https://github.com/shakacode/shakapacker/issues. | ||
|
||
## About SWC | ||
|
||
[SWC (Speedy Web compiler)](https://swc.rs/) is a Rust-based compilation and bundler tool that can be used for Javascript and Typescript files. It claims to be 20x faster than Babel! | ||
|
||
It supports all ECMAScript features and it's designed to be a drop-in replacement for Babel and its plugins. Out of the box, it supports TS, JSX syntax, React fast refresh, and much more. | ||
|
||
For comparison between SWC and Babel, see the docs at https://swc.rs/docs/migrating-from-babel. | ||
|
||
## Switching your Shakapacker project to SWC | ||
|
||
In order to use SWC as your compiler today. You need to do two things: | ||
|
||
1. Make sure you've installed `@swc/core` and `swc-loader` packages. | ||
|
||
``` | ||
yarn add -D @swc/core swc-loader | ||
``` | ||
|
||
2. Add or change `webpacker_loader` value in your default `webpacker.yml` config to `swc` | ||
The default configuration of babel is done by using `package.json` to use the file within the `shakapacker` package. | ||
|
||
```json | ||
default: &default | ||
source_path: app/javascript | ||
source_entry_path: / | ||
public_root_path: public | ||
public_output_path: packs | ||
cache_path: tmp/webpacker | ||
webpack_compile_output: true | ||
|
||
# Additional paths webpack should look up modules | ||
# ['app/assets', 'engine/foo/app/assets'] | ||
additional_paths: [] | ||
|
||
# Reload manifest.json on all requests so we reload latest compiled packs | ||
cache_manifest: false | ||
|
||
# Select loader to use, available options are 'babel' (default) or 'swc' | ||
webpack_loader: 'swc' | ||
``` | ||
|
||
## Usage | ||
|
||
### React | ||
|
||
React is supported out of the box, provided you use `.jsx` or `.tsx` file extension. Shakapacker config will correctly recognize those and tell SWC to parse the JSX syntax correctly. If you wish to customize the transform options to match any existing `@babel/preset-react` settings, you can do that through customizing loader options as described below. You can see available options at https://swc.rs/docs/configuration/compilation#jsctransformreact. | ||
|
||
### Typescript | ||
|
||
Typescript is supported out of the box, but certain features like decorators need to be enabled through the custom config. You can see available customizations options at https://swc.rs/docs/configuration/compilation, which you can apply through customizing loader options as described below. | ||
|
||
Please note that SWC is not using the settings from `.tsconfig` file. Any non-default settings you might have there will need to be applied to the custom loader config. | ||
|
||
## Customizing loader options | ||
|
||
You can see the default loader options at [swc/index.js](../package/swc/index.js). | ||
|
||
If you wish to customize the loader defaults further, for example, if you want to enable support for decorators or React fast refresh, you need to create a `swc.config.js` file in your app config folder. | ||
|
||
This file should have a single default export which is an object with an `options` key. Your customizations will be merged with default loader options. You can use this to override or add additional configurations. | ||
|
||
Inside the `options` key, you can use any options available to the SWC compiler. For the options reference, please refer to [official SWC docs](https://swc.rs/docs/configuration/compilation). | ||
|
||
See some examples below of potential `config/swc.config.js`. | ||
|
||
### Example: Enabling top level await and decorators | ||
|
||
|
||
```js | ||
const customConfig = { | ||
options: { | ||
jsc: { | ||
parser: { | ||
topLevelAwait: true, | ||
decorators: true | ||
} | ||
} | ||
} | ||
} | ||
|
||
module.exports = customConfig | ||
``` | ||
|
||
### Example: Matching existing `@babel/present-env` config | ||
|
||
```js | ||
const { env } = require('shakapacker') | ||
|
||
const customConfig = { | ||
options: { | ||
jsc: { | ||
transform: { | ||
react: { | ||
development: env.isDevelopment, | ||
useBuiltins: true | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
module.exports = customConfig | ||
``` | ||
|
||
### Example: Enabling React Fast Refresh | ||
|
||
:warning: Remember that you still need to add [@pmmmwh/react-refresh-webpack-plugin](https://github.com/pmmmwh/react-refresh-webpack-plugin) to your webpack config. The setting below just replaces equivalent `react-refresh/babel` Babel plugin. | ||
|
||
|
||
```js | ||
const { env } = require('shakapacker') | ||
|
||
const customConfig = { | ||
options: { | ||
jsc: { | ||
transform: { | ||
react: { | ||
refresh: env.isDevelopment && env.runningWebpackDevServer | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
module.exports = customConfig | ||
``` | ||
|
||
### Example: Adding browserslist config | ||
|
||
```js | ||
|
||
const customConfig = { | ||
options: { | ||
env: { | ||
targets: '> 0.25%, not dead' | ||
} | ||
} | ||
} | ||
|
||
module.exports = customConfig | ||
``` | ||
|
||
|
||
## Known limitations | ||
|
||
- `browserslist` config at the moment is not being picked up automatically. [Related SWC issue](https://github.com/swc-project/swc/issues/3365). You can add your browserlist config through customizing loader options as outlined above. | ||
- Using `.swcrc` config file is currently not supported. You might face some issues when `.swcrc` config is diverging from the SWC options we're passing in the Webpack rule. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,30 +1,32 @@ | ||
const { resolve } = require('path') | ||
const { realpathSync } = require('fs') | ||
const { loaderMatches } = require('../utils/helpers') | ||
|
||
const { | ||
source_path: sourcePath, | ||
additional_paths: additionalPaths | ||
additional_paths: additionalPaths, | ||
webpack_loader: webpackLoader | ||
} = require('../config') | ||
const { isProduction } = require('../env') | ||
|
||
module.exports = { | ||
test: /\.(js|jsx|mjs|ts|tsx|coffee)?(\.erb)?$/, | ||
include: [sourcePath, ...additionalPaths].map((p) => { | ||
try { | ||
return realpathSync(p) | ||
} catch (e) { | ||
return resolve(p) | ||
} | ||
}), | ||
exclude: /node_modules/, | ||
use: [ | ||
{ | ||
loader: require.resolve('babel-loader'), | ||
options: { | ||
cacheDirectory: true, | ||
cacheCompression: isProduction, | ||
compact: isProduction | ||
module.exports = loaderMatches(webpackLoader, 'babel', () => ({ | ||
test: /\.(js|jsx|mjs|ts|tsx|coffee)?(\.erb)?$/, | ||
include: [sourcePath, ...additionalPaths].map((p) => { | ||
try { | ||
return realpathSync(p) | ||
} catch (e) { | ||
return resolve(p) | ||
} | ||
} | ||
] | ||
} | ||
}), | ||
exclude: /node_modules/, | ||
use: [ | ||
{ | ||
loader: require.resolve('babel-loader'), | ||
options: { | ||
cacheDirectory: true, | ||
cacheCompression: isProduction, | ||
compact: isProduction | ||
} | ||
} | ||
] | ||
})) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
const { resolve } = require('path') | ||
const { realpathSync } = require('fs') | ||
const { loaderMatches } = require('../utils/helpers') | ||
const { getSwcLoaderConfig } = require('../swc') | ||
|
||
const { | ||
source_path: sourcePath, | ||
additional_paths: additionalPaths, | ||
webpack_loader: webpackLoader | ||
} = require('../config') | ||
|
||
module.exports = loaderMatches(webpackLoader, 'swc', () => ({ | ||
test: /\.(ts|tsx|js|jsx|mjs|coffee)?(\.erb)?$/, | ||
include: [sourcePath, ...additionalPaths].map((p) => { | ||
try { | ||
return realpathSync(p) | ||
} catch (e) { | ||
return resolve(p) | ||
} | ||
}), | ||
exclude: /node_modules/, | ||
use: ({ resource }) => getSwcLoaderConfig(resource) | ||
})) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
/* eslint global-require: 0 */ | ||
/* eslint import/no-dynamic-require: 0 */ | ||
|
||
const { resolve } = require('path') | ||
const { existsSync } = require('fs') | ||
const { merge } = require('webpack-merge') | ||
|
||
const isJsxFile = (filename) => !!filename.match(/\.(jsx|tsx)?(\.erb)?$/) | ||
|
||
const isTypescriptFile = (filename) => !!filename.match(/\.(ts|tsx)?(\.erb)?$/) | ||
|
||
const getCustomConfig = () => { | ||
const path = resolve('config', 'swc.config.js') | ||
if (existsSync(path)) { | ||
return require(path) | ||
} | ||
return {} | ||
} | ||
|
||
const getSwcLoaderConfig = (filenameToProcess) => { | ||
const customConfig = getCustomConfig() | ||
const defaultConfig = { | ||
loader: require.resolve('swc-loader'), | ||
options: { | ||
jsc: { | ||
parser: { | ||
dynamicImport: true, | ||
syntax: isTypescriptFile(filenameToProcess) | ||
? 'typescript' | ||
: 'ecmascript', | ||
[isTypescriptFile(filenameToProcess) ? 'tsx' : 'jsx']: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we move React support back into Shakapacker? Seems that we should keep it the same for both babel and swc? However, now that I'm maintaining shakapacker, I don't mind having React support being supported by default. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @justin808 there's two parts to it - understanding jsx syntax and React transform. I think we can move the latter to a config but with the former relying on the filename it might need to stay. I'll see how it handles non-React jsx files and adjust accordingly. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @justin808 There's some weirdness with passing options to swc inline, for more details swc-project/swc#3374 This setup will work though regardless and I see no real reason not to support basic React config out of the box if we're not requiring any specific presets or plugins like Still, I've removed the transform setting from the default config and added section to the docs on how might one add settings to match any settings they have in preset-react if they wish to do so. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We'll iterate on this after the first release. Maybe we can just be clear that SWC is experimental and the final API might change by the time 6.1.0 is released. |
||
isJsxFile(filenameToProcess) | ||
} | ||
}, | ||
sourceMaps: true, | ||
env: { | ||
coreJs: '3.8', | ||
loose: true, | ||
exclude: ['transform-typeof-symbol'], | ||
mode: 'entry' | ||
} | ||
} | ||
} | ||
|
||
return merge(defaultConfig, customConfig) | ||
} | ||
|
||
module.exports = { | ||
getSwcLoaderConfig | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can either change this package or make a new one that makes this integration super easy.
https://github.com/shakacode/react-on-webpacker/