Nonstop Perpetual Motion

@launch/app

0.27.0 • Public • Published
Launch.js

Build Status npm license

Launch.js is a micro-framework for building Webpack-powered universal apps, sans boilerplate.


Features

  • Isomorphic. Write once, run server rendered and in-browser.
  • Development mode: Hot code reload (SSR/browser push), full source maps.
  • Production mode: ES6 tree-shaking, code minification, asset crunching.
  • Async code splitting on import(); smart Webpack 4 code chunking.
  • BYO stack. Minimally opinionated defaults. Add any UI/state/back-end.
  • Official presets for React, GraphQL, CSS/SASS, images, and more.
  • Import via NPM. No boilerplate; trivial upgrades.
  • Highly configurable API. Add routes/middleware. Configure in code - no CLI needed.
  • Pluggable. Easily extend to match your project requirements.
  • Written in Typescript. Run .ts(x) / .js(x) files out-the-box.
  • Extensive code coverage. 100+ tests. Used in production.

Quick start

The Launch.js core is exposed as an NPM module at @launch/app -- simply import into your Node project and configure.

Here's an example that builds a universal React app in Typescript:

main.ts

import App from "@launch/app"; // <-- import Launch.js
import EntryPlugin from "@launch/entry"; // <-- import plugin that lets us specify entrypoints
 
void new App() // <-- create a new app
  .plugin( // <-- Launch.js uses plugins to specify Webpack config
    new EntryPlugin()
      .server(require.resolve("./entry/server.tsx")) // <-- set server entry point
      .client(require.resolve("./entry/client.tsx")) // <-- ... and what's loaded in the browser
  )
  .production() // <-- set build mode to 'production' to minify assets
  .launch(); // <-- build/run the app!

Launch.js uses plugins to provide Webpack configuration.

See the references to entry/server.tsx and entry/client.tsx? In this example, we're using the entry plugin to specify client/server entrypoints.

Inside each of those entrypoints is our userland code for configuring what happens on the server, and in the browser.

Code in those files occur within the Webpack'd environment, so we can import whatever file type our Webpack 4 loaders allow for.

Let's create them now.

First, we'll tackle the server. A server entry point is simply a function that receives a Launch.js Output object containing server/client build stats that we can easily query against for cross-environment awareness, and returns Koa middleware.

In this simple app, we'll grab a <Root> component and render it out to the screen as HTML, along with a <script> tag that contains the rendered client-side JS:

entry/server.tsx

// Server entry point
 
// ----------------------------------------------------------------------------
// IMPORTS
 
/* NPM */
import { Output } from "@launch/app"; // <-- server entry will get one of these
import { Context } from "koa";
import * as React from "react";
import * as ReactDOM from "react-dom/server";
 
/* Local */
import Root from "../components/root";
 
// ----------------------------------------------------------------------------
 
export default function(output: Output) {
 
  // Create Koa middleware to handle React requests
  return (ctx: Context) => {
 
    // Render React, and dump it as HTML output. Note the call
    // to `output.client.main("js")` -- this references our 'main' client
    // JS chunk... so we can easily include it in our render!
    ctx.body = `
      <!doctype html>
      <html>
        <head>
          <title>React running on Launch.js</title>
        </head>
        <body>
          <div id="root">${ReactDOM.renderToString(<Root />)}</div>
          <script src="${output.client.main("js")}"></script>
        </body>
      </html>
    `.replace(/(\s)\s+/g, "$1").trim();
  };
}

Now, we'll create the version that the browser sees. This will be really simple - just grab the same <Root> component, and sync it up with the <div id="root"> tag that it was mounted to on the server:

entry/client.tsx

// Client entry point
 
// ----------------------------------------------------------------------------
// IMPORTS
 
/* NPM */
import * as React from "react";
import * as ReactDOM from "react-dom";
 
/* Local */
import Root from "../components/root"; // <-- same root import as on the server
 
// ----------------------------------------------------------------------------
 
// Attach our `<Root />` component to <div id="root"> in our SSR'd HTML output
ReactDOM.hydrate(<Root />, document.getElementById("root"));

And finally -- create the sample <Root/> that both the server and client will look for:

components/root.tsx

import * as React from "react";
 
export default () => (
  <h1>Hello from Launch.js - coming at you from within Webpack!</h1>
);

When we fire up main.ts in the shell, after a few seconds, you'll see something that looks like this:

Viewing the source of http://localhost:3000 will yield something close to:

See that assets/js/main.*.js reference? Assets are versioned and, because we set app.production(), minified:

Plugins

Simple plugin for specifying an entrypoint via .server() and .client(). Useful when you want to use Launch.js as a platform for your own custom, universal apps.

Import .css, .s(c|a)ss, and .less files in your project. Features hot-reloading in development, local/global modules, PostCSS plugins, and access to the resulting bundle via output.client.mainCss.

Coming Soon

  • Full API documentation
  • Tutorials
  • React, GraphQL, image plugins -- and more

install

npm i @launch/app

Downloadsweekly downloads

0

version

0.27.0

license

MIT

homepage

github.com

repository

Gitgithub

last publish

collaborators

  • avatar
Report a vulnerability