Skip to content
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

Merged
merged 18 commits into from
Jan 28, 2022
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
shakapacker (6.0.0.rc.13)
shakapacker (6.0.0)
activesupport (>= 5.2)
rack-proxy (>= 0.6.1)
railties (>= 5.2)
Expand Down
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ Discussion forums to discuss debugging and troubleshooting tips. Please open iss
- [Development](#development)
- [Webpack Configuration](#webpack-configuration)
- [Babel configuration](#babel-configuration)
- [SWC configuration](#swc-configuration)
- [Integrations](#integrations)
- [React](#react)
- [Typescript](#typescript)
Expand Down Expand Up @@ -371,6 +372,12 @@ By default, you will find the Webpacker preset in your `package.json`. Note, you

Optionally, you can change your Babel configuration by removing these lines in your `package.json` and add [a Babel configuration file](https://babeljs.io/docs/en/config-files) in your project. For an example customization based on the original, see [Customizing Babel Config](./docs/customizing_babel_config.md).

### SWC configuration

You can try out experimental integration with SWC loader. You can read more at [SWC usage docs](./docs/using_swc_loader.md)

Please note that if you want opt-in to use SWC, you can skip [React](#react) integration instructions as it is supported out of the box
tomdracz marked this conversation as resolved.
Show resolved Hide resolved

### Integrations

Webpacker out of the box supports JS and static assets (fonts, images etc.) compilation. To enable support for CoffeeScript or TypeScript install relevant packages:
Expand Down
61 changes: 61 additions & 0 deletions docs/using_swc_loader.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# 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 it's 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 recognise those and tell SWC to parse the JSX syntax correctly
tomdracz marked this conversation as resolved.
Show resolved Hide resolved

Fast refresh is supported out of the box, provided you use it in development (`NODE_ENV=development`) and `env.WEBPACK_SERVE` is true (running `webpack-dev-server`)

### Typescript

Typescript is supported out of the box. Some features like decorators however might not be currently enabled

## Known limitations

- `browserslist` config at the moment is not being picked up automatically. [Related SWC issue](https://github.com/swc-project/swc/issues/3365)
- 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
3 changes: 3 additions & 0 deletions lib/install/config/webpacker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ default: &default
# 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: 'babel'

development:
<<: *default
compile: true
Expand Down
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@
"@babel/plugin-transform-runtime": "^7.15.0",
"@babel/preset-env": "^7.15.6",
"@babel/runtime": "^7.15.4",
"@swc/core": "^1.2.133",
"babel-loader": "^8.2.2",
"compression-webpack-plugin": "^9.0.0",
"swc-loader": "^0.1.15",
"terser-webpack-plugin": "^5.2.4",
"webpack": "^5.53.0",
"webpack-assets-manifest": "^5.0.6",
Expand All @@ -39,6 +41,7 @@
"eslint-plugin-jsx-a11y": "^6.4.1",
"eslint-plugin-react": "^7.26.0",
"jest": "^27.2.1",
"swc-loader": "^0.1.15",
"webpack": "^5.53.0",
"webpack-assets-manifest": "^5.0.6",
"webpack-merge": "^5.8.0"
Expand Down
44 changes: 23 additions & 21 deletions package/rules/babel.js
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
}
}
]
}))
1 change: 1 addition & 0 deletions package/rules/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const rules = {
css: require('./css'),
sass: require('./sass'),
babel: require('./babel'),
swc: require('./swc'),
erb: require('./erb'),
coffee: require('./coffee'),
less: require('./less'),
Expand Down
52 changes: 52 additions & 0 deletions package/rules/swc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
const { resolve } = require('path')
const { realpathSync } = require('fs')
const { loaderMatches } = require('../utils/helpers')

const {
source_path: sourcePath,
additional_paths: additionalPaths,
webpack_loader: webpackLoader
} = require('../config')
const { isDevelopment, runningWebpackDevServer } = require('../env')

const isJsxFile = (filename) => !!filename.match(/\.(jsx|tsx)?(\.erb)?$/)

const isTypescriptFile = (filename) => !!filename.match(/\.(ts|tsx)?(\.erb)?$/)

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 }) => ({
loader: require.resolve('swc-loader'),
options: {
tomdracz marked this conversation as resolved.
Show resolved Hide resolved
jsc: {
parser: {
dynamicImport: true,
syntax: isTypescriptFile(resource) ? 'typescript' : 'ecmascript',
[isTypescriptFile(resource) ? 'tsx' : 'jsx']: isJsxFile(resource)
},
transform: {
react: {
runtime: 'automatic',
development: isDevelopment,
refresh: runningWebpackDevServer && isDevelopment
}
}
},
sourceMaps: true,
env: {
coreJs: '3.8',
loose: true,
exclude: ['transform-typeof-symbol'],
mode: 'entry'
}
}
})
}))
21 changes: 19 additions & 2 deletions package/utils/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const resolvedPath = (packageName) => {
}
}

const moduleExists = (packageName) => (!!resolvedPath(packageName))
const moduleExists = (packageName) => !!resolvedPath(packageName)

const canProcess = (rule, fn) => {
const modulePath = resolvedPath(rule)
Expand All @@ -39,6 +39,22 @@ const canProcess = (rule, fn) => {
return null
}

const loaderMatches = (configLoader, loaderToCheck, fn) => {
if (configLoader !== loaderToCheck) {
return null
}

const loaderName = `${configLoader}-loader`

if (!moduleExists(loaderName)) {
throw new Error(
`Your webpacker config specified using ${configLoader}, but ${loaderName} package is not installed. Please install ${loaderName} first.`
tomdracz marked this conversation as resolved.
Show resolved Hide resolved
)
}

return fn()
}

module.exports = {
chdirTestApp,
chdirCwd,
Expand All @@ -47,5 +63,6 @@ module.exports = {
ensureTrailingSlash,
canProcess,
moduleExists,
resetEnv
resetEnv,
loaderMatches
}
1 change: 1 addition & 0 deletions test/mounted_app/test/dummy/config/webpacker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ default: &default
source_entry_path: entrypoints
public_output_path: packs
cache_path: tmp/webpacker
webpack_loader: babel

# Additional paths webpack should look up modules
# ['app/assets', 'engine/foo/app/assets']
Expand Down
1 change: 1 addition & 0 deletions test/test_app/config/webpacker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ default: &default
public_output_path: packs
cache_path: tmp/webpacker
webpack_compile_output: false
webpack_loader: babel

# Additional paths webpack should look up modules
# ['app/assets', 'engine/foo/app/assets']
Expand Down
16 changes: 16 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2841,6 +2841,15 @@ loader-utils@^1.4.0:
emojis-list "^3.0.0"
json5 "^1.0.1"

loader-utils@^2.0.0:
version "2.0.2"
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.2.tgz#d6e3b4fb81870721ae4e0868ab11dd638368c129"
integrity sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==
dependencies:
big.js "^5.2.2"
emojis-list "^3.0.0"
json5 "^2.1.2"

locate-path@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e"
Expand Down Expand Up @@ -3621,6 +3630,13 @@ supports-preserve-symlinks-flag@^1.0.0:
resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==

swc-loader@^0.1.15:
version "0.1.15"
resolved "https://registry.yarnpkg.com/swc-loader/-/swc-loader-0.1.15.tgz#cb9c630ccfbb46dabc5aebc5560cced658e32992"
integrity sha512-cn1WPIeQJvXM4bbo3OwdEIapsQ4uUGOfyFj0h2+2+brT0k76DCGnZXDE2KmcqTd2JSQ+b61z2NPMib7eEwMYYw==
dependencies:
loader-utils "^2.0.0"

symbol-tree@^3.2.4:
version "3.2.4"
resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2"
Expand Down