Security News
Maven Central Adds Sigstore Signature Validation
Maven Central now validates Sigstore signatures, making it easier for developers to verify the provenance of Java packages.
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.
$ jalla index.js
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
Scripts are compiled using Browserify. Custom transforms can be
added using the browserify.transform
field in your
package.json
file.
// package.json
"browserify": {
"transform": [
"some-browserify-transform"
]
}
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.
Run babel on your sourcecode. Will respect local .babelrc
files for
configuring the babel transform.
The following babel plugins are added by default:
.browserlist
file to configure which babel plugins to support the browsers listed therein.
Not used in watch mode.Inline static assets in your application using the Node.js fs
module.
Use environment variables in your code.
Choo-specific optimization which transpiles html templates for increased browser performance.
A while suite of optimizations and minifications removing unused code, significantly reducing file size.
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.
// package.json
"postcss": {
"plugins": {
"some-postcss-plugin": {}
}
}
// .postcssrc.js
module.exports = config
function config (ctx) {
var plugins = []
if (ctx.env === 'production') {
plugins.push(require('some-postcss-plugin'))
}
return { plugins }
}
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™.
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.
Automatically add vendor prefixes. Respects .browserlist
to
determine which browsers to support.
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.
// package.json
"documentify": {
"transform": [
[
"./my-document.js",
{
"order": "end"
}
]
]
}
// my-document.js
var hyperstream = require('hstream')
module.exports = document
function document () {
return hyperstream({
'html': {
// add a class to the root html element
class: 'Root'
},
'meta[name="viewport"]': {
// instruct Mobile Safari to expand under the iPhone X notch
content: 'width=device-width, initial-scale=1, viewport-fit=cover'
},
head: {
// add some tracking script to the header
_appendHtml: `
<script async src="https://www.tracking-service.com/tracker.js?id=abc123"></script>
<script>
window.dataLayer = window.dataLayer || [];
function track () { dataLayer.push(arguments); }
track('js', new Date());
track('config', 'abc123');
</script>
`
}
})
}
All files located in the root folder ./assets
are automatically being served
under the webpage root.
--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 serverBy 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.use(require('choo-service-worker')('/sw.js'))
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// index.json
var choo = require('choo')
var app = choo()
app.route('/', require('./views/home'))
app.use(require('choo-service-worker')('/sw.js'))
module.exports = app.mount('body')
// sw.js
// use package.json version field as cache key
var CACHE_KEY = process.env.npm_package_version
var FILES = [
'/',
'/manifest.json'
].concat(process.env.ASSET_LIST)
self.addEventListener('install', function oninstall (event) {
// cache landing page and all assets once service worker is installed
event.waitUntil(
caches
.open(CACHE_KEY)
.then((cache) => cache.addAll(FILES))
.then(() => self.skipWaiting())
)
})
self.addEventListener('activate', function onactivate (event) {
// clear old caches on activate
event.waitUntil(clear().then(() => self.clients.claim()))
})
self.addEventListener('fetch', function onfetch (event) {
event.respondWith(
caches.open(CACHE_KEY).then(function (cache) {
return cache.match(req).then(function (cached) {
if (req.cache === 'only-if-cached' && req.mode !== 'same-origin') {
return cached
}
// try and fetch response and fallback to cache
return self.fetch(event.request).then(function (response) {
if (!response.ok) {
if (fallback) return fallback
else return response
}
cache.put(req, response.clone())
return response
}, function (err) {
if (fallback) return fallback
return err
})
})
})
)
})
// clear application cache
// () -> Promise
function clear () {
return caches.keys().then(function (keys) {
var caches = keys.filter((key) => key !== CACHE_KEY)
return Promise.all(keys.map((key) => caches.delete(key)))
})
}
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.
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 = require('koa-mount')
var jalla = require('jalla')
var app = jalla('index.js')
// only allow robots in production
app.use(mount('/robots.txt', function (ctx, next) {
ctx.type = 'text/plain'
ctx.body = `
User-agent: *
Disallow: ${process.env.NODE_ENV === 'production' ? '' : '/'}
`
}))
app.listen(8080)
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 servedWhen 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.
var fetch = require('node-fetch')
var html = require('choo/html')
var choo = require('choo')
var app = choo()
app.route('/', main)
app.use(store)
module.exports = app.mount('body')
function main (state, emit) {
if (!state.name) {
emit('fetch')
return html`<body>Loading…</body>`
}
return html`
<body>
<h1>Hello ${state.name}!</h1>
</body>
`
}
function store (state, emitter) {
state.name = state.name || null
emitter.on('fetch', function () {
var promise = fetch('https://some-api.com')
.then((res) => res.text())
.then(function (name) {
state.name = name
emitter.emit('render')
})
if (state.prefetch) {
// ask jalla to wait for this promise before rendering the resulting HTML
state.prefetch.push(promise)
}
})
}
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.
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.
var purge = require('cccpurge')
var jalla = require('jalla')
var app = jalla('index.js')
app.use(function (ctx, next) {
if (ctx.accepts('html')) {
// cache all html responses on Cloudflare for a week
ctx.set('Cache-Control', `s-maxage=${60 * 60 * 24 * 7}, max-age=0`)
}
return next()
})
if (app.env === 'production') {
// purge cache before starting production server
cccpurge(require('./index'), {
root: 'https://www.my-blog.com',
email: 'foo@my-blog.com',
zone: '<CLOUDFLARE_ZONE_ID>',
key: '<CLOUDFLARE_API_KEY>'
}, function (err) {
if (err) process.exit(1)
app.listen(8080)
})
} else {
app.listen(8080)
}
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.
var geoip = require('geoip-lite')
app.use(function (ctx, next) {
if (ctx.accepts('html')) {
// add meta data
ctx.state.meta = { 'og:url': 'https://webpage.com' + ctx.url }
// expose user location on state
ctx.state.location = geoip.lookup(ctx.ip)
}
return next()
})
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
.
app.use(function (ctx, next) {
if (!ctx.accepts('html')) return next()
// find all javascript assets
var bundles = Object.values(ctx.assets)
.filter((asset) => /\.js$/.test(asset.url))
.map((asset) => `<${asset.url}>; rel=preload; as=script`)
// HTTP/2 push all bundles
ctx.append('Link', bundles)
return next()
})
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.
FAQs
web compiler and server in one
The npm package jalla receives a total of 26 weekly downloads. As such, jalla popularity was classified as not popular.
We found that jalla demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 2 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Maven Central now validates Sigstore signatures, making it easier for developers to verify the provenance of Java packages.
Security News
CISOs are racing to adopt AI for cybersecurity, but hurdles in budgets and governance may leave some falling behind in the fight against cyber threats.
Research
Security News
Socket researchers uncovered a backdoored typosquat of BoltDB in the Go ecosystem, exploiting Go Module Proxy caching to persist undetected for years.