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

feat(dev): allow manipulating esbuild config from remix.config.js #1529

Closed
wants to merge 4 commits into from

Conversation

mister-what
Copy link

Motivation

There are times where you need certain customizations in your build (like esbuild plugins or definitions). This PR adds the option to change the config object that is being passed to esbuild and customize it according to your needs.

The way this new config option is defined also allows specifying remix build plugins in a composable way (see example below).

Examples

Adding SVGR plugin

const svgrPlugin = require("esbuild-plugin-svgr");

/**
 * @type {import("@remix-run/dev/config").AppConfig}
 */
module.exports = {
  appDirectory: "app",
  assetsBuildDirectory: "public/build",
  publicPath: "/build/",
  serverBuildDirectory: "build",
  devServerPort: 8002,
  ignoredRouteFiles: [".*"],
+  esbuildConfig(config, mode) {
+    return {
+      ...config,
+      plugins: [svgrPlugin(), ...config.plugins],
+    };
+  },
};

Remix build plugins

(I think it is a good idea if applyPlugins is provided by @remix-run/dev)

/**
 * @file remix-plugin-svgr.js
 */

const svgrPlugin = require("esbuild-plugin-svgr");

module.exports = (svgrOptions) => (appConfig) => ({
  ...appConfig,
  esbuildConfig(buildOptions, mode) {
    buildOptions = {
      ...buildOptions,
      plugins: [svgrPlugin(svgrOptions), ...buildOptions],
    };
    return appConfig.esbuildConfig?.(buildOptions, mode) ?? buildOptions;
  },
});

/**
 * @file remix-plugin-sass.js
 * @description because it was requested that often
 */

const sassPlugin = require("esbuild-plugin-sass");

module.exports = (sassOptions) => (appConfig) => ({
  ...appConfig,
  esbuildConfig(buildOptions, mode) {
    buildOptions = {
      ...buildOptions,
      plugins: [sassPlugin(sassOptions), ...buildOptions],
    };
    return appConfig.esbuildConfig?.(buildOptions, mode) ?? buildOptions;
  },
});

/**
 * @file applyPlugins.js
 */

module.exports = (config, ...plugins) =>
  plugins.reduce((config, plugin) => plugin(config), config);

/**
 * @file remix.config.js
 */
const SassPlugin = require("./remix-plugin-sass");
const SvgrPlugin = require("./esbuild-plugin-svgr");
const applyPlugins = require("./applyPlugins");

module.exports = applyPlugins(
  {
    appDirectory: "app",
    assetsBuildDirectory: "public/build",
    publicPath: "/build/",
    serverBuildDirectory: "build",
    devServerPort: 8002,
    ignoredRouteFiles: [".*"],
  },
  SassPlugin({ rootDir: process.cwd() }),
  SvgrPlugin()
);

@remix-cla-bot
Copy link
Contributor

remix-cla-bot bot commented Jan 17, 2022

Hi @mister-what,

Welcome, and thank you for contributing to Remix!

Before we consider your pull request, we ask that you sign our Contributor License Agreement (CLA). We require this only once.

You may review the CLA and sign it by adding your name to contributors.yml.

Once the CLA is signed, the CLA Signed label will be added to the pull request.

If you have already signed the CLA and received this response in error, or if you have any questions, please contact us at hello@remix.run.

Thanks!

- The Remix team

@remix-cla-bot
Copy link
Contributor

remix-cla-bot bot commented Jan 17, 2022

Thank you for signing the Contributor License Agreement. Let's get this merged! 🥳

@jacob-ebey
Copy link
Member

As much as I personally like the idea of exposing compiler customization points I doubt we will be merging this for a few reasons:

  • We do not want to expose any implementation details of the remix compiler (and the underlying esbuild choice is an implementation detail)
  • Adding plugins will increase build times and I don't want to field questions of "why is my build now 30 seconds?" due to badly written plugins
  • If we do expose compiler customization points it will be in a way that is agnostic to the underlying compiler (i.e, not esbuild plugins)

As for SVGs, you'll better serve your end user by using external SVG files imported via import mySvgHref from "~/svgs/a.svg". Smaller JS bundles, faster page hydration, and immutable .svg's over the network that are only ever downloaded once.

@mister-what
Copy link
Author

@jacob-ebey

As much as I personally like the idea of exposing compiler customization points I doubt we will be merging this for a few reasons:

  • We do not want to expose any implementation details of the remix compiler (and the underlying esbuild choice is an implementation detail)
  • Adding plugins will increase build times and I don't want to field questions of "why is my build now 30 seconds?" due to badly written plugins
  • If we do expose compiler customization points it will be in a way that is agnostic to the underlying compiler (i.e, not esbuild plugins)

As for SVGs, you'll better serve your end user by using external SVG files imported via import mySvgHref from "~/svgs/a.svg". Smaller JS bundles, faster page hydration, and immutable .svg's over the network that are only ever downloaded once.

You are right about the SVGs. Nevertheless, the possible use-cases go beyond importing SVGs.
I understand that esbuild is an implementation detail and exposing an implementation detail comes with a price.

But I disagree that a build tool agnostic interface for plugins/customizations would be a good idea. With that, you would introduce another competing way of writing build plugins and at the same time prevent usage of existing plugins (forcing users to either re-invent the wheel or write custom adapters around existing plugins).

Exposing an implementation detail is in that case absolutely worth the price. There is no way that you could ever support each and every custom build demand in the framework.

This missing flexibility is a blocker for considering Remix as a migration target for mid- to enterprise-sized application deployments.
Don't get me wrong! I love Remix and the concepts behind it. I'd love to see it being used in our software (and this is my personal motivation for this PR).

@hollandThomas
Copy link
Contributor

I'm with Jacob on this one.

There is no way that you could ever support each and every custom build demand in the framework.

While that's technically true, I can see that Remix will support >99% of all common use cases — and that should be good enough I think 🤷‍♂️ . In my experience, those "custom built plugins" are oftentimes half-baked, error-prone solutions anyways. And if there's a legitimate use case Remix actually does not solve, I'm sure that you can always open a discussion in this repo.

From the top of my head, here are two recent developments that I could see making their way into the Remix compiler one day:

  • bun: "bun is like postcss, babel, node & webpack in one 100x faster tool for building modern web frontends.". I follow its development on twitter. It's remarkable how much effort is put into the tiniest details
  • parcel-css: "A CSS parser, transformer, and minifier written in Rust.". Truly great stuff here, squeezing the last bit of performance out of CSS-bundling.

And that's only what's hot right now. Who knows what else will happen in the next couple of years. Adding stuff like this to Remix will be infinitely harder (and next to impossible without causing breaking changes) when exposing implementation details.

@jacob-ebey
Copy link
Member

@jacob-ebey

As much as I personally like the idea of exposing compiler customization points I doubt we will be merging this for a few reasons:

  • We do not want to expose any implementation details of the remix compiler (and the underlying esbuild choice is an implementation detail)
  • Adding plugins will increase build times and I don't want to field questions of "why is my build now 30 seconds?" due to badly written plugins
  • If we do expose compiler customization points it will be in a way that is agnostic to the underlying compiler (i.e, not esbuild plugins)

As for SVGs, you'll better serve your end user by using external SVG files imported via import mySvgHref from "~/svgs/a.svg". Smaller JS bundles, faster page hydration, and immutable .svg's over the network that are only ever downloaded once.

You are right about the SVGs. Nevertheless, the possible use-cases go beyond importing SVGs. I understand that esbuild is an implementation detail and exposing an implementation detail comes with a price.

But I disagree that a build tool agnostic interface for plugins/customizations would be a good idea. With that, you would introduce another competing way of writing build plugins and at the same time prevent usage of existing plugins (forcing users to either re-invent the wheel or write custom adapters around existing plugins).

Exposing an implementation detail is in that case absolutely worth the price. There is no way that you could ever support each and every custom build demand in the framework.

This missing flexibility is a blocker for considering Remix as a migration target for mid- to enterprise-sized application deployments. Don't get me wrong! I love Remix and the concepts behind it. I'd love to see it being used in our software (and this is my personal motivation for this PR).

When I say agnostic I meant an existing plugin format, I've been thinking about how to integrate the rollup / vite plugin API. I do not want to re-invent the wheel.

@ryanflorence
Copy link
Member

ryanflorence commented Feb 11, 2022

Thanks for taking the time to put this together.

We may one day open up the compiler, but with recent compiler trends, the network tab in the user's browser is a common casualty (like SVGs for instance!) and ofc the developer experience starts to erode as build times increase.

We aren't saying "no" forever on this, but definitely "not right now". It's really early, and esbuild has its own opinions and may one day express one that doesn't work for Remix!

It's annoying, but it's not necessarily a blocker. You can almost always run your own build in front of Remix to do whatever you want and then import the results of that into your Remix app.

@ryanflorence
Copy link
Member

Oops, didn't mean to hit send.

Just adding plugins isn't enough either. Considering something like a CSS plugin, you need to somehow load the files the css plugin emitted, for that we'd need to have additional API for you to add things to the remix manifest and plug into the client-side transition sequence to load them. There's a lot more to think about than "let me add some esbuild plugins".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants