jalla
Jalla is an opinionated compiler and server in one. It makes web development fast, fun and exceptionally performant.
Jalla is an excellent choice when static files just don't cut it. Perhaps you need to render views dynamically, push (HTTP/2) assets or integrate with back-end services.
In short: a Koa server, a Browserify bundler for scripts and a PostCSS for styles. Documents are compiled using Documentify. Jalla is built with Choo in mind and is heavily inspired by Bankai.
Usage
$ jalla index.js
Working With Jalla
Jalla has a watch mode and a production mode. Setting the environment
variable NODE_ENV
to anything but development
will cause jalla to perform
more expensive compilation and optimizations on your code.
If the environment variable NODE_ENV
is missing, jalla assumes you are in
development and will default to watch mode which observes files for changes
and recompiles them on the fly.
$ NODE_ENV=production jalla index.js
JavaScript
Scripts are compiled using Browserify. Custom transforms can be
added using the browserify.transform
field in your
package.json
file.
Example browserify config
// package.json"browserify": "transform": "some-browserify-transform"
Included Browserify optimizations
split-require
Lazily load parts of your codebase. Jalla will transform dynamic imports into
calls to split-require automatically (using a
babel plugin), meaning you only have to call
import('./some-file')
to get bundle splitting right out of the box without any
tooling footprint in your source code.
babelify
Run babel on your sourcecode. Will respect local .babelrc
files for
configuring the babel transform.
The following babel plugins are added by default:
- babel-plugin-dynamic-import-split-require transform dynamic import calls to split-require.
- babel-preset-env: read
.browserlist
file to configure which babel plugins to support the browsers listed therein. Not used in watch mode.
brfs
Inline static assets in your application using the Node.js fs
module.
envify
Use environment variables in your code.
nanohtml (not used in watch mode)
Choo-specific optimization which transpiles html templates for increased browser performance.
tinyify (not used in watch mode)
A while suite of optimizations and minifications removing unused code, significantly reducing file size.
CSS
CSS files are located and included automaticly. Whenever a JavaScript module is
used in your application, jalla will try and find an adjacent index.css
file
in the same location. Jalla will also respect the style
field in a modules
package.json
to determine which CSS file to include.
All CSS files are transpiled using PostCSS. To add PostCSS plugins,
either add a postcss
field to your package.json
or, if you need to
conditionally configure PostCSS, create a .postcssrc.js
in the root of your
project. See postcss-load-config for details.
Example PostCSS config
// package.json"postcss": "plugins": "some-postcss-plugin": {}
// .postcssrc.jsmoduleexports = config { var plugins = if ctxenv === 'production' plugins return plugins }
The included PostCSS plugins
postcss-url
Rewrite URLs and copy assets from their source location. This means you can reference e.g. background images and the like using relative URLs and it'll just work™.
postcss-import
Inline files imported with @import
. Works for both local files as well as for
files in node_modules
, just like it does in Node.js.
autoprefixer (not used in watch mode)
Automatically add vendor prefixes. Respects .browserlist
to
determine which browsers to support.
HTML
Jalla uses Documentify to compile server-rendered markup.
Documentify can be configured in the package.json
(see Documentify
documentation). By default, jalla only applies HTML minification using
posthtml-minifier.
Example Documentify config
// package.json"documentify": "transform": "./my-document.js" "order": "end"
// my-document.jsvar hyperstream = moduleexports = document { return }
Assets
All files located in the root folder ./assets
are automatically being served
under the webpage root.
CLI Options
--service-worker, --sw
entry point for a service worker, uses a subset of the optimization used for the entry file.--css
explicitly include a css file in the build--quiet, -q
disable printing to console--debug, -d
enable the node inspector, accepts a port as value--base, -b
base path where app will be served--port, -p
port to use for server
Service Workers
By supplying the path to a service worker entry file with the sw
option, jalla
will build and serve it's bundle from that path.
Registering a service worker with a Choo app is easily done using choo-service-worker.
app
And then starting jalla with the sw
option.
$ jalla index.js --sw sw.js
Information about application bundles and assets are exposed to the service worker during its build and can be accessed as environment variables.
process.env.ASSET_LIST
a list of URLs to all included assets
Example service worker
// index.jsonvar choo = var app = appapp moduleexports = app
// sw.js// use package.json version field as cache keyvar CACHE_KEY = processenvnpm_package_versionvar FILES = '/' '/manifest.json' self self self // clear application cache// () -> Promise { return caches}
Manifest
A bare-bones application manifest is generated based on the projects
package.json
. You could either place a manifest.json
in the assets folder or
you can generate one using a custom middleware.
API
After instantiating the jalla server, middleware can be added just like you'd do with any Koa app. The application is an instance of Koa and supports all Koa middleware.
Jalla will await all middleware to finish before trying to render a HTML response.
If the response has been redirected (i.e. calling ctx.redirect
) or if a value
has been assigned to ctx.body
jalla will not render any HTML response.
var mount = var jalla = var app = // only allow robots in productionapp app
API Options
Options can be supplied as the second argument (jalla('index.js', opts)
).
sw
entry point for a service workercss
explicitly include a css file in the buildquiet
disable printing to consolebase
base path where app will be served
SSR (Server side render)
When rendering HTML, jalla will make two render passes; once to allow your views
to fetch the content it needs and once again to generate the resulting HTML. On
the application state there will be an prefetch
property which is an array for
you to push promises into. Once all promises are resolved, the second render
will commence.
Example using state.prefetch
var fetch = var html = var choo = var app = appapp moduleexports = app { if !statename return html`Loading…` return html` Hello ! `} { statename = statename || null emitter}
Caching HTML
Jalla will render HTML for every request, which is excellent for dynamic content but might not be what you need for all your views and endpoints. You will probably want to add custom caching middleware or an external caching layer ontop of your server for optimal performance.
Setting up Cloudflare caching with jalla
Cloudflares free tier is an excellent complement to jalla for caching HTML
responses. You'll need to setup Cloudflare to
cache everything and to respect existing cache
headers. This means you'll be able to tell Cloudflare which responses to cache
and for how long by setting the s-maxage
header.
However, when publishing a new version of your webpage or when the cache should be invalidated due to some external service update, you'll need to purge the Cloudflare cache. For that purpose, there's cccpurge.
Example purging cache on server startup
var purge = var jalla = var app = app if appenv === 'production' // purge cache before starting production server else app
ctx.state
Whatever is stored in the state object after all middleware has run will be used
as state when rendering the HTML response. The resulting application state will
be exposed to the client as window.initialState
and will be automatically
picked up by Choo. Using ctx.state
is how you bootstrap your client with
server generated content.
Meta data for the page being rendered can be added to ctx.state.meta
. A
<meta>
tag will be added to the header for every property therein.
Example decorating ctx.state
var geoip = app
ctx.assets
Compiled assets (scripts and styles) are exposed on the koa ctx
object as an
object with the properties file
, map
, buffer
and url
.
Example adding Link headers for all JS assets
app
Events
Most of the internal workings are exposed as events on the application (Koa) instance.
app.on('error', callback(err))
When an internal error occurs or a route could not be served. If an HTTP error was encountered, the status code is available on the error object.
app.on('warning', callback(warning))
When a non-critical error was encountered, e.g. a postcss plugin failed to parse a rule.
app.on('update', callback(file))
When a file has been changed.
app.on('progress', callback(file, uri))
When an entry file is being bundled.
app.on('bundle:script', callback(file, uri, buff)
When a script file finishes bundling.
app.on('bundle:style', callback(file, uri, buff)
When a css file finishes bundling.
app.on('bundle:file', callback(file))
When a file is being included in a bundle.
app.on('timing', callback(time, ctx))
When a HTTP response has been sent.
app.on('start', callback(port))
When the server has started and in listening.
Todo
- Fix CSS asset handling
- Add bundle splitting for CSS
- Export compiled files to disc
- Pretty UI