zambezi / ez-build

Zambezi build tool
MIT License
2 stars 1 forks source link

Customizing plugins and presets #36

Closed mstade closed 7 years ago

mstade commented 8 years ago

Expected Behavior

I'd like to be able to customize some of the tooling internal to ez-build, such as adding plugins or presets to the babel setup. The behavior of this should be predictable, such that I understand intuitively how things are set up, and such that I don't run into nasty surprises with plugin ordering or anything of that kind. At the same time, I shouldn't need to know how ez-build works internally in order to customize its behavior.

Ideally, the configuration of ez-build is explicit, and wouldn't rely on magical files like .babelrc. By that I mean all configuration happens on the command line. We've already paved with #35 for --flags to enable things like --flags es2017:true, and surely we could extend this to include things that are quite generic, like --flags stage:{0,1,2,3,4} for feature proposals in various stages of ratification. As well, an option such as --flags add-{presets,plugins}:<... plugins | ... presets> could perhaps be added to allow for that addition of presets and plugins on top of the ones already included in the ez-build setup.

Some questions I have with all this are:

Perhaps my most pressing question is as follows: is there really just an 80/20 rule here, where we can make 80% of the desired functionality simple and easy, and realize the other 20% scenarios are too esoteric such that we don't much care to support them in the first place? This tool isn't meant for everyone after all, it's meant for those that are ok with staying on the beaten path. For instance, the majority of our own projects only add a single additional plugin: react. We can easily just say this is part of the 80%, and add a --flags react or something like that, and call it a day. It doesn't help whoever wants to use random-plugin-incompatible-with-all-of-the-things, and runs the risk of becoming obsolete whenever the next new-hot-thing shows up, but at least it's hidden behind a flag and as such we remain true to the goal of enabling the ratified technology of tomorrow, today.

Current Behavior

Currently you can set the plugins and presets picked up by babel by using a .babelrc file, as explained in the documentation. However, this does not come without issue, as @DimitarChristoff succintly explains in a comment to #35 and as well has been discussed elsewhere. Plugin and preset ordering is an issue, and particularly so since babel doesn't have a notion of plugin/preset dependencies, despite the fact that the order of plugins/presets matters a whole lot in certain situations.

As well, the fact that we pick up .babelrc sometimes makes it difficult to reason about just how ez-build is configured, and is really a sign that ez-build is a leaky abstraction. In most cases, its configuration is explicit, by passing CLI arguments. This is easy to reason about, but can run the issue of having to repeat that configuration over and over again when used in different scripts. This is nothing new, and something that other compiler suits like GCC also suffer from. (In those cases, flags are often set in variables that can be expanded/modified at runtime.)

DimitarChristoff commented 8 years ago

probably also worth mentioning this may get more complicated upon the introduction of webpack/webpack-dev-server setups that use babel loaders. in my experience, when using a webpack.config.js, you avoid .babelrc and set your different env settings on the webpack side of things as you setup the loaders

obviously, it's not just transcompiling, it's loaders, transforms, plugins and presets - so it makes sense not have multiple entry points in and have env specific hooks universal.

an alternative approach can even be to pass in a local ez-build.conf.js that can either compose things or get passed in the options as a callback, allowing you to modify them.

mstade commented 8 years ago

To expand on the 80/20 rationale, I'd like to suggest that if were were to go down that route, we probably should define as-objective-as-possible criteria for what makes it into the 80% that becomes features of ez-build (behind flags or otherwise) and what stays outside. We do discuss this in the rationale to some extent but perhaps this can be made more specific. Here's a potential set of such guidelines:

Feature checklist

If one or more of these questions have an affirmative answer, the feature is potentially a good candidate for ez-build.

  • [ ] Is the feature part of a ratified standard, such as ECMAScript or a W3C spec?
  • [ ] Is the feature on track to become part of a ratified standard, such as ECMAScript or a W3C spec?
  • [ ] Is the feature widely used in the developer community? (How would we measure this? Code search on GitHub?)
  • [ ] Is the feature reasonably stable; are there frequent breaking changes?
  • [ ] Is the feature backed by any organizations, commercial, non-profit, or otherwise?
  • [ ] Is the feature actively developed or supported?
  • [ ] Is the feature in line with the goals of ez-build, as described in the rationale?

Not all of these are entirely objective, but we should strive for objectivity to as much as possible eliminate bike shedding discussions boiling down into preference. For example, I personally think JSX is a bad idea for various reasons; but it's evidently widely popular, has good support, doesn't break hard very often (these days) and for projects that make heavy use of React it's very useful. Thus, adding support for JSX (i.e. the react babel preset) in ez-build makes sense, even though it isn't and quite possibly never will be part of a ratified standard.

DimitarChristoff commented 8 years ago

Not really sure how the feature roadmap into ES matters very much, it's about how useful the plugin or transform are. eg. TypeScript won't ever make ES spec but it's something that may be required.

stage-0 is a very good example of that--things like static props etc (class transforms) or @Decorators are very much in the wild and prevalent in a lot of the tutorials and examples out there, yet there is no hurry or immediate danger of it making the spec just yet. In fact, decorators are being somewhat resisted (but possibly due to other reasons around composition side effects). other things that it does do are :: bindings, particularly useful in react handlers like onClick={::this.handleClick}. things like do expressions etc are kind of pointless imo. anyway, https://github.com/tc39/proposals/blob/master/stage-0-proposals.md

the points around stability/proliferation/changes are kind of important - but everything changes so fast these days, there literally are no guarantees left. this is why trying to support/allow/reject things is the wrong mentality - allowing people to get sensible defaults and ability to do their own thing instead will give a lot more mileage.

we can still adopt an approach of abstraction, eg --experimental flag, that allows us internally to decide what is considered experimental - 2017, latest, stage-1, stage-0 and how to order and implement these should the landscape change.

mstade commented 8 years ago

Not really sure how the feature roadmap into ES matters very much, it's about how useful the plugin or transform are. eg. TypeScript won't ever make ES spec but it's something that may be required.

The roadmap does matter, because if ES supports it or most likely will support it (i.e. the feature is in later stages) then regardless of what ez-build thinks of the feature, it should support it. Because that's the point, to build typical projects based primarily on web standards. TypeScript may very well be useful to a great many people, but it will never be part of ez-build for a couple of reasons:

Other tech that is substantially different from the ES story usually falls into this bucket as well. Dart and ClojureScript comes to mind – both have a much better and consistent build story than ES, simply because that's been part of the tech from the start.

this is why trying to support/allow/reject things is the wrong mentality - allowing people to get sensible defaults and ability to do their own thing instead will give a lot more mileage.

Sensible defaults is one thing, and that's definitely one of the goals. But more than that, eliminating flexibility is a feature, not a bug. If you do want to stray far off the beaten path (stage {0,1,2,3,4} is not far off the beaten path btw, since ES is one of the techs we want to support) then ez-build is probably not the right fit.

It's also not so much about allowing or rejecting things, it's about making choice for people so they don't have to. If they want to, then ez-build will always be at odds with that, and it may well be more useful to use webpack or whatever other setup might make sense for you. The point of making choices on your behalf ties into another thing you said:

the points around stability/proliferation/changes are kind of important - but everything changes so fast these days, there literally are no guarantees left

This is very much why ez-build exists, to try and catch up to that ever moving goal post for you. This is nigh impossible to do without drawing some lines in the sand, and saying "here's a path for you to walk." Stepping off that path is cool too, but you may want to consider different tooling then. If flexibility is a line, going from full flexibility to zero flexibility, ez-build is closer to the latter by design:

            |---------------+---------------|---------------+---------------|
            ^               ^                               ^               ^
     Full flexibility       |                               |         No flexibility
(Shell scripts, Makefiles)  |                               |     (No build step at all)
                            |                               |
                  A lot of flexibility               Some flexibility
                    (Babel, Webpack)            (ez-build, Closure compiler)

Now this is obviously arguable, but what I mean by this is that there's a spectrum of users here, where some want lots of flexibility, and others want less of it. The point of ez-build is to try and fill some of the gaps in the less flexible end of the spectrum.


Veering off topic for a minute here, but this discussion makes me think the rationale isn't describing these goals and ideals (and thus, the direction) very clearly. Perhaps there are parts where we can make it clearer?

DimitarChristoff commented 8 years ago

Veering off topic for a minute here, but this discussion makes me think the rationale isn't describing these goals and ideals (and thus, the direction) very clearly. Perhaps there are parts where we can make it clearer?

nope, all of the above does make sense, I guess I am not entirely clear on the overall goals / strategy. previously, I have witnessed a number of efforts to abstract this side of things away from projects, resulting in very opinionated grunt/glup abstractions or even things like [enterprise]-grunt forks that try to replace / simplify the originals. It hasn't always worked very well, eliminating flexibility is a feature, not a bug. can be a scary and subjective sentiment and needs to be implemented with a lot of thought and planning. I really like that in the approach here. :+1:

btw, I would guess that TypeScript will likely become a requested feature as/if Angular 2 ever comes out. and would probably need to be a supported feature as well.

mstade commented 8 years ago

I guess I am not entirely clear on the overall goals / strategy

Which is why I'm thinking maybe the rationale isn't all that clear. :o)

Ideally, it should be entirely clear after having read the rationale, but if there are outstanding questions I'd consider that a bug with the rationale and it should be fixed. I'd love to work with you to fix this if you think there are holes to be filled!

btw, I would guess that TypeScript will likely become a requested feature as/if Angular 2 ever comes out. and would probably need to be a supported feature as well.

I'd say no. TS already has a build story, and more importantly one that's difficult to internalize or compose. Maybe there's some stuff ez-build can do on top of the result produced by that build, i.e. optimised-modules.json perhaps. That said, the correct answer there is probably that we should deprecate that file altogether (happy to explain to you why, offline.)

There's nothing to say you can't have different projects using different build processes, in fact, that should be a goal. Another way to put it perhaps, is to explain ez-build as a necessary evil, something that helps you go from authored JS, CSS and HTML code (i.e. web standards) into runnable code. Ideally, it wouldn't be necessary, ideally we'd live in a world where runtimes understand modules and CSS variables and all those other nice things that exist in modern standards – but we don't. :o) (There are other features that make sense to have as well that might always constitute the need for a build step, such as optimizations.)

Other tech may be very cool and useful, but I've yet to see a tech like TS or Dart or ClojureScript, that targets the web but doesn't have a solid build story. No point in trying to compete with that, and likely always be a step behind, delivering a sub-par solution.

mstade commented 8 years ago

I'm keen to explore the idea of removing support for .babelrc and other implicit configuration mechanisms altogether, in favor of flags, such as --flags react, and/or what @DimitarChristoff describes in a previous comment:

we can still adopt an approach of abstraction, eg --experimental flag, that allows us internally to decide what is considered experimental - 2017, latest, stage-1, stage-0 and how to order and implement these should the landscape change.

This makes sense to me, and provides a means of opting in to experimental features that at the very least have the ambition to become part of a spec – whether they do or don't is a different matter. We'd have to carefully document that making use of such experimental features significantly increases the risk of breaking changes, even without changes to the project configuration. Essentially, something like ez-build --flags es-stage-0 (tentative syntax) would mean a different thing today, than it would six months from now. The configuration stays the same, but the actual meaning of it is likely to be very different, due to the volatility in these proposals. This adds a significant maintenance cost, and it's important you understand what you're opting into. Conversely, ez-build --flags es2017 is less dangerous, since it would only include stage-4 proposals (i.e. they are for all intents and purposes done) and eventually become part of the standard feature set. So ez-build and ez-build --flags es2017 six months from now is effectively the same thing.

A proposal

The removal of .babelrc support is probably controversial, but given that it works poorly with some plugins anyway I doubt it would be that painful. It would mean configuration changes however, so should be deprecated responsibly with a clear runtime warning that the support is going away fully in the near future, but provide a one-version moratorium. It would be good if we could survey users of ez-build, to get a clearer picture of how it's being used.

DimitarChristoff commented 8 years ago

removing .babelrc is a serious breaking change, it's like taking away my driving license and sticking me in a google self driving car. I still would like to see support for .babelrc as single source of truth (set my own wiring) as well as things like webpack.conf.js - use cases such as:

to enable support for babel in Jest, you need:

{
  "presets": [
    "react",
    ["es2015", {"modules": false}],
    "stage-0"
  ],

  "env": {
    "test": {
      "plugins": ["transform-es2015-modules-commonjs"]
    }
  }
}

or for my DEV workflow for react, I like redbox-react and other debug instrumentation, like why-did-you-update:

{
  "presets": ["es2015", "stage-0"],
  "env": {
    // only enable it when process.env.NODE_ENV is 'development' or undefined
    "development": {
      "plugins": [["react-transform", {
        "transforms": [{
          "transform": "react-transform-catch-errors",
          // now go the imports!
          "imports": [

            // the first import is your React distribution
            // (if you use React Native, pass "react-native" instead)

            "react",

            // the second import is the React component to render error
            // (it can be a local path too, like "./src/ErrorReporter")

            "redbox-react"

            // the third import is OPTIONAL!
            // when specified, its export is used as options to the reporter.
            // see specific reporter's docs for the options it needs.

            // it will be imported from different files so it either has to be a Node module
            // or a file that you configure with Webpack/Browserify/SystemJS to resolve correctly.
            // for example, see https://github.com/gaearon/babel-plugin-react-transform/pull/28#issuecomment-144536185

            // , "my-reporter-options"
          ]
        }]
        // note: you can put more transforms into array
        // this is just one of them!
      }]]
    }
  }
}

Disabling it means I cannot control settings per NODE_ENV as well as I cannot run the test framework of my choice in the prescribed way or I need to re-implement all the whole babel stuff and maintain it locally. Seems wasteful.

here is a chart to help visualise it, though it's not ASCII: yes

:)

mstade commented 8 years ago

I don't think that's a fair analogy. It's more like encapsulating the car engine to prevent you from trying to upgrade (and probably break) it yourself, when that should be done by an authorized mechanic. In any case, I don't think either analogy is particularly helpful.

What's more important is that your comment actually highlights exactly why ez-build should remain conservative, and favor consistency and reliability over flexibility. Just look no further than redbox-react which has this in big bold lettering right at the very top of the readme:

This Project Is Deprecated

This notice was added in late April meaning if I relied on this six months ago, my project now depends on an explicitly deprecated dependency. In contrast, what I'm proposing is that we focus on conservative support where there is explicit intent for long time support and compatibility. That means primarily supporting features destined for standards tracks, or features that have considerable support and resources to back them up. React certainly falls in this category, particularly after v15, but a lot of tooling in that same ecosystem falls squarely in the nifty tech-demo bucket instead, not something particularly reliable.

The other tool you mention seems to be a runtime tool, not compile time, so presumably wouldn't be incompatible with ez-build at all.

Now, I'd have to look closer at what "support for babel in Jest" means, but it looks like JIT for which we have #27 – an important discussion in its own right. But Jest or no Jest, the configuration you posted isn't actually incompatible with my proposal. The equivalent ez-build configuration would be:

ez-build --flags react,es-stage-0,modules:commonjs

I'd have to explore Jest more to understand whether there are fundamental differences that make Jest and ez-build incompatible (just a brief look suggest there are not.) Nonetheless I think it's important to not conflate the issues here: dropping support for .babelrc is fundamentally a matter of removing a leak from the abstraction. The fact that you feel compelled to use babel alongside ez-build at this point (largely due to the lack of #27) is a bug, not a feature.

mstade commented 8 years ago

An addendum to my proposal: the order in which flags are specified should not matter. For example these should all be equivalent:

ez-build --flags es2017,es-stage-0
ez-build --flags es-stage-0,es2017
ez-build --flags es-stage-0 --flags es2017
ez-build --flags es2017 --flags es-stage-0

Whatever logic needs to happen to determine preset/plugin order etc should be entirely internal to ez-build, and not something a user should have to care or wonder about.

DimitarChristoff commented 8 years ago

redbox plugin was a quick example. it's now part of react in development mode - we all see the red screen of an exception that has been caught.

But, the argument stands that the pipeline is mine to do what I want with.

I understand that you can abstract a lot of possible supported options for the cli tool--it adds a layer of complexity and learning of mapping that is unknown outside our ecosystem.

A likely scenario is, as a dev, you find a transform; a plugin; a loader that you'd like to use. The github page always has the right examples and ways to plug into webpack / babel / whatever layer. But then, you can't just use that, you need to discover if ez-build has catered for it, if not, will it / can it, if yes, you need to ensure version compatibility and correct syntax.

it's a LOT easier on the user to just:

So, my advice is simply going to remain "Don't remove it, embrace it and build on top of it."

mstade commented 8 years ago

But, the argument stands that the pipeline is mine to do what I want with.

If that's your firm belief, then ez-build isn't for you. It really is that simple and the reason why is explained in the rationale. The whole premise of ez-build is to trade off choice and flexibility for stability and maintainability, now and in the future. If you need full flexibility and access to bleeding edge technology, there are other tools available that do a better now and will continue to do a better job in perpetuity. This is not where ez-build tries or even wants to compete.

Furthermore, in your example you're ignoring the hidden maintenance costs and complexities. Maybe, maybe, the experience is similar to the steps you outline. More realistically you'll find it's not clear how the transform or plugin fits into your specific setup, which is about 90% similar to all other setups but includes some bespoke-yet-ultimately-unnecessary modifications. It may be that you depend on another transform that's incompatible, or the plugin is for babel but really you need one for rollup. Perhaps more importantly, it may work today but there's no guarantee that it will work six months from now. If all you're building are one offs that never get rebuilt – fine – but that simply won't fly in project developed over any significant amount of time.


Listen, I'm sorry buddy, but you're arguing for ez-build to be a webpack clone and there's just no point to that – just use webpack. To that end, I'm going to kill this line of discussion here because it's straying much too far from the main topic. The premise of this issue is what's described in the very first paragraph:

I'd like to be able to customize some of the tooling internal to ez-build, such as adding plugins or presets to the babel setup. The behavior of this should be predictable, such that I understand intuitively how things are set up, and such that I don't run into nasty surprises with plugin ordering or anything of that kind. At the same time, I shouldn't need to know how ez-build works internally in order to customize its behavior.

(Emphasis mine.)

That's what we're trying to solve here, and I've come up with a proposal to get there. I think the next step is to implement a PR and get the discussion back to focusing on the task at hand. I'll leave this issue open till then of course, in the hopes that there will be further feedback and perhaps counter proposals, but please let's try to keep it focused.

FabienDeshayes commented 8 years ago

As much as I'm a bit afraid that we might end up with a flag orgy to support all different use cases, I think removing .babelrc support is a good thing.

Just reading the rationale again reminded me the goals of ez-build and looking at the different use cases I know of in our organisation for example, it's pretty much always just using react + es2015, which ez-build tackles out of the box perfectly.

So I won't go here about the debate of flexibility over stability, and will just give a :+1: to see that implemented.

mstade commented 7 years ago

Closed by #43.