Skip to content

Commit

Permalink
Enable esbuild-loader support (#53)
Browse files Browse the repository at this point in the history
  • Loading branch information
Tom Dracz authored Feb 7, 2022
1 parent 3bca40e commit 54d46b2
Show file tree
Hide file tree
Showing 9 changed files with 353 additions and 3 deletions.
8 changes: 7 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ Changes since last non-beta release.

*Please add entries here for your pull requests that are not yet released.*

## [v6.1.1] - February 6, 2022

### Added
- Support for esbuild-loader. [PR 53](https://github.com/shakacode/shakapacker/pull/53) by [tomdracz](https://github.com/tomdracz).

## [v6.1.0] - February 4, 2022
### Added
- Support for SWC loader. [PR 29](https://github.com/shakacode/shakapacker/pull/29) by [tomdracz](https://github.com/tomdracz).
Expand Down Expand Up @@ -69,7 +74,8 @@ Changes since last non-beta release.
## v5.4.3 and prior changes from rails/webpacker
See [CHANGELOG.md in rails/webpacker (up to v5.4.3)](https://github.com/rails/webpacker/blob/master/CHANGELOG.md)

[Unreleased]: https://github.com/shakacode/shakapacker/compare/v6.1.0...master
[Unreleased]: https://github.com/shakacode/shakapacker/compare/v6.1.1...master
[v6.1.1]: https://github.com/shakacode/shakapacker/compare/v6.1.1...v6.1.1
[v6.1.0]: https://github.com/shakacode/shakapacker/compare/v6.0.2...v6.1.0
[v6.0.2]: https://github.com/shakacode/shakapacker/compare/v6.0.1...v6.0.2
[v6.0.1]: https://github.com/shakacode/shakapacker/compare/v6.0.0...v6.0.1
Expand Down
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ Discussion forums to discuss debugging and troubleshooting tips. Please open iss
- [Webpack Configuration](#webpack-configuration)
- [Babel configuration](#babel-configuration)
- [SWC configuration](#swc-configuration)
- [esbuild loader configuration](#esbuild-loader-configuration)
- [Integrations](#integrations)
- [React](#react)
- [Typescript](#typescript)
Expand Down Expand Up @@ -388,6 +389,12 @@ You can try out experimental integration with the SWC loader. You can read more

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.

### esbuild loader configuration

You can try out experimental integration with the esbuild-loader. You can read more at [esbuild-loader usage docs](./docs/using_esbuild_loader.md).

Please note that if you want opt-in to use esbuild-loader, you can skip [React](#react) integration instructions as it is supported out of the box.

### 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
128 changes: 128 additions & 0 deletions docs/using_esbuild_loader.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
# Using esbuild-loader

:warning: This feature is currently experimental. The configuration and API are subject to change during the beta release cycle.

If you face any issues, please report them at https://github.com/shakacode/shakapacker/issues.

## About esbuild

[esbuild](https://esbuild.github.io/) is a Go-based bundler tool that can offer [significant improvement](https://esbuild.github.io/faq/#benchmark-details) over other similar tools.

While esbuild is a complete bundler, through the usage of [esbuild-loader](https://github.com/privatenumber/esbuild-loader), you can still leverage esbuild's speedy transpilation and minifcation in your Webpack-based configs.

Please note, that unlike Babel or SWC loader, esbuild-loader has got no support for things like:
- React Hot Module reload
- ES5 as a compilation target
- Automatic polyfills for missing browser features

Those are limitations of esbuild itself and might make use of esbuild-loader in your project unfeasible. If you don't care about HMR and don't need to support older browsers, esbuild-loader might be a suitable option for you.

## Switching your Shakapacker project to esbuild-loader

To use esbuild as your transpiler today. You need to do two things:

1. Make sure you've installed `esbuild` and `esbuild-loader` packages.

```
yarn add -D esbuild esbuild-loader
```

2. Add or change `webpacker_loader` value in your default `webpacker.yml` config to `esbuild`
The default configuration of babel is done by using `package.json` to use the file within the `shakapacker` package.

```yml
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), 'swc' or 'esbuild'
webpack_loader: 'esbuild'
```
### (Optional) Replace minification with esbuild
You can gain an additional performance boost if you replace the default Terser minification with esbuild plugin.
o do so, you need to modify your webpack configuration and use `ESBuildMinifyPlugin` provided by `esbuild-loader`.

Example:

```js
const { webpackConfig: baseWebpackConfig, merge } = require('shakapacker')
const { ESBuildMinifyPlugin } = require('esbuild-loader')
const options = {
optimization: {
minimizer: [
new ESBuildMinifyPlugin({
target: 'es2015'
})
]
}
}
module.exports = merge({}, baseWebpackConfig, options)
```

For more details, see instructions at https://github.com/shakacode/shakapacker#webpack-configuration and https://github.com/privatenumber/esbuild-loader#js-minification-eg-terser.

## 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 esbuild to parse the JSX syntax correctly. If you wish to customize the likes of JSX fragment function, you can do that through customizing loader options as described below. You can see available options at https://github.com/privatenumber/esbuild-loader#%EF%B8%8F-options.

### Typescript

Typescript is supported out of the box and `.tsconfig.json` root file is automatically detected. Only a subset of `.tsconfig.json` options is supported. Please refer to the [loader docs](https://github.com/privatenumber/esbuild-loader#configuration) for additional information.

## Customizing loader options

You can see the default loader options at [esbuild/index.js](../package/esbuild/index.js).

If you wish to customize the loader defaults further, you need to create a `esbuild.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 esbuild-loader. For the options reference, please refer to [esbuild-loader docs](https://github.com/privatenumber/esbuild-loader#%EF%B8%8F-options).

See some examples below of potential `config/babel.config.js`.

### Example: Specifying esnext target environment


```js
const customConfig = {
options: {
target: 'esnext'
}
}
module.exports = customConfig
```

### Example: Using custom jsxFragment and jsxFactory

```js
const { env } = require('shakapacker')
const customConfig = {
options: {
jsxFragment: 'Fragment',
jsxFactory: 'h'
}
}
module.exports = customConfig
```
2 changes: 1 addition & 1 deletion lib/install/config/webpacker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ 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'
# Select loader to use, available options are 'babel' (default), 'swc' or 'esbuild'
webpack_loader: 'babel'

development:
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"devDependencies": {
"babel-loader": "^8.2.2",
"compression-webpack-plugin": "^9.0.0",
"esbuild-loader": "^2.18.0",
"eslint": "^7.32.0",
"eslint-config-airbnb": "^18.2.1",
"eslint-config-prettier": "^8.3.0",
Expand Down
40 changes: 40 additions & 0 deletions package/esbuild/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/* eslint global-require: 0 */
/* eslint import/no-dynamic-require: 0 */

const { resolve } = require('path')
const { existsSync } = require('fs')
const { merge } = require('webpack-merge')

const getLoaderExtension = (filename) => {
const matchData = filename.match(/\.([jt]sx?)?(\.erb)?$/)

if (!matchData) {
return 'js'
}

return matchData[1]
}

const getCustomConfig = () => {
const path = resolve('config', 'esbuild.config.js')
if (existsSync(path)) {
return require(path)
}
return {}
}

const getEsbuildLoaderConfig = (filenameToProcess) => {
const customConfig = getCustomConfig()
const defaultConfig = {
loader: require.resolve('esbuild-loader'),
options: {
loader: getLoaderExtension(filenameToProcess)
}
}

return merge(defaultConfig, customConfig)
}

module.exports = {
getEsbuildLoaderConfig
}
23 changes: 23 additions & 0 deletions package/rules/esbuild.js
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 { getEsbuildLoaderConfig } = require('../esbuild')

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

module.exports = loaderMatches(webpackLoader, 'esbuild', () => ({
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 }) => getEsbuildLoaderConfig(resource)
}))
1 change: 1 addition & 0 deletions package/rules/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const rules = {
sass: require('./sass'),
babel: require('./babel'),
swc: require('./swc'),
esbuild: require('./esbuild'),
erb: require('./erb'),
coffee: require('./coffee'),
less: require('./less'),
Expand Down
Loading

0 comments on commit 54d46b2

Please sign in to comment.