RFC: Preact CLI #496

addyosmani opened this Issue Jan 10, 2017 · 8 comments


None yet

3 participants

addyosmani commented Jan 10, 2017 edited

Preact currently offers great compatibility with the rest of the CLI tools offered by the React ecosystem. These include nwb, which exposes Preact scaffolding and flags for build-time switching to Preact and create-react-app which you can mostly drop Preact into.

Increasingly, with folks seeing preact-compat as a stepping-stone into using Preact and more than just a drop-in for better loading perf than React in production, there are opportunities for Preact to define it's own CLI with Preact ecosystem best-practices. I see great opportunities for providing a "fast-by-default", zero-config experience out of the box ⚡️

Something that encodes:

  • Minimal scaffolding with a verifiable performance baseline 🏠
  • Minimal unit testing setup (c-r-a uses Jest)
  • Minimal prescribed application/directory structure 📁
  • Upgrade/Migration-friendly (similar to how c-r-a delegates this out to react-scripts) 🏃
  • Optionally prescribed minimal add-ons like preact-router
  • Pre-configured Webpack 2.x build. VueJS does an excellent job of this for both dev and prod.
  • Perf best practices, defaulting to things like code-splitting ✂️ (we're pushing for this in create-react-app too), link rel="preload" and maybe even Service Worker support (e.g support via sw-precache-webpack-plugin or offline-plugin) for Progressive Web Apps (as we documented here).
  • Depending on how brave we are, we could even aim for every app scaffolded and prod-built by preact-cli to be a PWA by default 🥇 (the Service Worker caching would help with instant repeat visit perf on both desktop and mobile. Also opts you into V8's code-caching earlier).
  • Given Preact's focus on size and performance, it could even use the Webpack performance budgeting feature to help folks keep their app size under check.
  • Something that supports create-react-app's awesome eject feature

A common question we might get is "Why does Preact need it's own CLI? Why not just continue using create-react-app?". My view is that as much as possible, c-r-a are open to baking in ideas like performance best practices out of the box, but are going to need to balance that with DX and beginner-friendliness. I feel like Preact is targeting a more performance savy audience and this gives us opportunities to explore this space a little more ambitiously.

Like, what if Preact even baked in ideas like PRPL for you? or could intelligently help you figure out what needs to be prefetched. This might be out of scope, but just throwing ideas out there to see if there would be interest in something like this 😄

PRPL support becomes a lot easier to automate once you know the directory structure. We could potentially take inspiration from Next.js's pages/ and introduce a routes/ directory that gets paired with bundle-loader?async in there.

Thoughts welcome! I'm approaching thinking of this through a browser vendor's perspective (perf) but there are probably other takes and ideas folks will have on what this might look like.

@addyosmani addyosmani changed the title from Preact CLI to RFC: Preact CLI Jan 10, 2017
lukeed commented Jan 10, 2017

@developit I was talking to you about exactly this yesterday, all features mentioned 😄


@lukeed if you've got time, would love if we could summarize some of the notes shared about your WIP from slack earlier 😸

lukeed commented Jan 11, 2017

@addyosmani Would be happy to but unfortunately will have to wait until tomorrow. Not enough sleep and too much time in front of my screen today means that I'm toying with a potential migraine right now.

Full force tomorrow 👌 Will have time to work on my pwa-cli too!

lukeed commented Jan 11, 2017

@addyosmani I'm building a PWA generator for different libraries. So far, I'm for sure implementing for Preact, Inferno, and React. At some point I plan to extend the templating to include Angular2, Polymer2, etc.

Like most of the recent build tools, the CLI can run through a series of prompts (like Yeoman) or by passing in flags.

pwa new preact --sass --no-offline
# or
pwa new preact # run thru prompts

By default, all builds will be with webpack2 (but rollup is also an option for the smaller pwa), center around AppShell, and be capable of offline use. One may also be able to specify a preference for SASS, LESS, Stylus, or CSS-modules. There will probably be a Babel vs Buble choice too, with default on Babel. I'm not sure what other flags to include at this time, but we'll get there.

Once an app is created, it will create a simple directory structure:

|-- config
    |-- webpack.js
    |-- uglify.js
    |-- babel.js
    |-- sw.js
|-- src
    |-- index.html
    |-- index.js
    |-- index.sass
    |-- styles
        |-- ...
    |-- static
        |-- img
        |-- icon
    |-- views
        |-- index.js (see routes)
        |-- tags (aka components)
        |-- pages
            |-- home.js
            |-- ...
            |-- errors
                |-- 404.js

You can explore explore this instead. Its structure is similar enough.

The important part here is that all configurations are easily accessible via config. The app is built with great defaults, but most people don't like to be constrained by magic or by a predefined, untouchable set of defaults (like CRA).

For example, one may want to add or remove webpack chunks, be more aggressive with their code splitting, or add service worker behaviors beyond an offline precache.

Now, let's assume a dev created a PWA with an older version of the CLI. After upgrading the tool, the project config is scanned & diff'd for upgradeable features (as long as the structure remains intact).

pwa upgrade

Then another CLI prompt list appears with the installable features (a la Yeoman generators upgrade), which are really just installable configs. This is because the CLI/local install will already ship with all capabilities, but only an file entry in config/ will flag any given feature for use.

Similarly, individual PWA & build features can be installable by name... or you may want to just add everything new:

# caved & now need babel
pwa add babel

# add multiple
pwa add babel offline

# add all!
pwa upgrade --all

This adds capabilities that "upgrades" the app itself, not its dependencies. Build patterns & dependency versions are handled internally.

By "upgrading" to use Babel, a new files is placed at config/babel.js. Having this here now enables Babel incorporation in subsequent builds. It also marks Babel as "installed" in future upgrade scans.

Regarding routes (comment at src/views/index.js):
I've been toying with the idea of auto-generating routes based on the file names & structure inside src/views/pages. This is very much inspired by Next.js.

The problem here is that, so far, Preact & Inferno have each ported a different version of react-router as their official routers, and so the usage / structure is different. (compare here and here)

One way around this is to just create a new router for one of the libraries. But then I think that the CLI should not interfere with App code itself, that magic can be bad, and that there are too many options in this space & I shouldn't be forcing only one.

So then the easy route (heh) would be just to stub a router template for each library. They're all fairly easy to work with & I personally find it reassuring to actually see the structure of my application. Plus, devs can pick 'n choose routers, or drop 'em entirely if they so please.

This would also be the largest point of complexity/hindrance when trying to introduce NG2, Polymer2, and others to the CLI! 😨

The application code doesn't affect the build operations, which is my only priority after leaving them with a customized (and further customizable) PWA in their preferred flavor of library.

(Sorta came to this conclusion while writing 😄)

I haven't published any of the code yet because it lives as a handful of scattered, working pieces. It definitely needs to be unified under one codebase before being looked at. And some of the behaviors described are still TBD, but within reach.

This was probably a bit longer than I (or you) wanted...

developit commented Jan 11, 2017 edited

@lukeed would you be open to having an option to use an internal (or via npm) directory for config/? That would get us a zeroconfig option, which I'm keen on.

Regarding router, I'd argue an async-aware router probably warrants a new (or extended) router entirely, similar to how Next.js ships with a next-aware Router.

lukeed commented Jan 11, 2017 edited

@developit Sure, I just don't want to lose the ability to persist & save a feature list across machines or installs. It should be a part of version control, if using non-defaults.

A .config directory would work for me, but probably not what you're looking for.

I'm down for hiding all default config, then exposing it when asked. Then anything inside config/*.js would override the feature's settings. But then the same question remains: How do you save a project's custom mix of choices (SASS, Bublé, Rollup; non-default choices) running on default settings?

Would a root file (pwa.json or .pwa) suffice? It'd likely be a direct representation of the CLI prompts' responses. Then any customization is still maintained separately in config/.


Last paragraph makes sense to me - basically a CLI accepts arguments, which are config, so those could be saved somewhere (perhaps in the package.json even?).

lukeed commented Jan 11, 2017

Yup, I'll work towards that @developit.

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