npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@creditkarma/hapi-galaxy

v2.0.0

Published

Front-end component rendering plugin for hapi.js.

Downloads

21

Readme

Hapi Galaxy

Front-end component rendering plugin for hapi.js.

Build Status

Introduction

Use hapi-galaxy to transform any front-end component into server-rendered output.

Motivations

  1. Provide a bridge between Credit Karma's server side JavaScript rendering conventions and our Web App servers.

Front End code ships on-demand and often, so rendering servers need to be able to execute newly discovered modules safely and consistently.

  1. Isolated, multi-tennant rendering.

We need to render many versions of the same module at the same time, so every front end application gets rendered in a dedicated process using worker-farm.

  1. Programmatic integration with any framework that supports server side rendering.

Next.js, create-react-app, Vue.js, Ember, or even vanilla string templates will work. Not tied to any client side framework.

Assumptions

  • Your front end code lives in some other module
  • You have somehow packaged and exported that module so that is an entry point intended for server-side rendering
    • e.g., a React app compiled with Babel and Webpack, without any references to browser specific globals in the runtime
  • "Bring your own render method"
    • Runtime errors are bad and make universal rendering unpredictable
    • Requiring your entire application's dependency tree is onerous
    • All renders are wrapped in a promise
    • Dependencies for render belong to the front end application
      • We use React today, but we may use something else tomorrow

Usage

Install hapi-galaxy and add it to your Hapi.js project's dependencies

npm install @creditkarma/hapi-galaxy --save

Then, register the plugin with your Hapi.js server:

server.register(require('hapi-galaxy'), pluginErr => {
  if (pluginErr) throw pluginErr

  server.route({
    path: '/',
    method: 'GET',
    handler: {
      galaxy: {
        component: require.resolve('./frontend/component')
      }
    }
  })

  server.start((err) => {
    console.log(`Server started at: ${server.info.uri}`);
  })
})

Where require.resolve('./frontend/component') is the absolute path to any module that uses the client interface, which means any async function will work. This means that the minimum viable "front end component" would look like this:

module.exports = (props, path) => Promise.resolve('<h1>Hello World</h1>')

Options

The galaxy handler object has the following properties:

  • component — a string with the name of the module to use when require'ing your FE code, or function with the signature function(props, location) which returns a promise that resolves with the rendered output of your component.
  • layout – function with the signature function(props, content) which wraps your component output, presumably with in an HTML document with head and body tags
    • If no layout function is provided a generic HTML 5 layout will be used.
    • layout: false will disable the default view.
    • Will be ignored if view option is provided.
  • path — current url being requested. Useful for handling route logic within a bundled component.
  • props — object containing any additional properties to be passed to the view.
  • view – string corresponding to the vision view template to be used. The view template with be the output of the render and props used as content and props, respectively.
  • workerFarm — object containing any configuration options for worker-farm

reply.galaxy(component, options, errorCallback)

  • component — a string or function, following the same restrictions as the component option defined above
  • options — an object including the same keys and restrictions defined by the route galaxy handler options, excluding the component
  • errorCallback — a function with the signature function(err) which should handle the error returned

Client Interface

So, here's the thing:

Client code can throw errors when you try to render it on the server.

Obviously, in a perfect world this doesn't happen very often. But since it can, and since we don't want the output of random client errors manifesting themselves on a production server, your render method needs to be a Promise that resolves with the successful output of your render.

Here's what a simple Client would look like:

In frontend/component.js

'use strict'
import React from 'react'
import { renderToString } from 'react-dom/server'
import MyApp from './app'

export default props =>
  new Promise((resolve, reject) => {
    resolve(renderToString(<MyApp {...props} />))
  })

In server.js

server.route({
  path: '/',
  method: 'GET',
  handler: {
    galaxy: {
      component: require.resolve('./frontend/component')
    }
  }
})

This has several benefits over error-first callbacks for our use-case:

  1. It provides a consistent way of preventing client code from throwing errors in your server process. Consider the following:
  • The server is a secondary runtime for the code being rendered and therefore it would be inappropriate to let the client code throw errors.
  • We're delegating the render method to the client code, error-first callbacks would require a try/catch to do correctly.
  • Error-swallowing becomes a feature as it provides de facto encapsulation.
  1. Promises are a requirement for using fetch and associated libraries).
  2. Async/await will be ready soon without compilation and it's interface is compatible with promises. Next.js uses this pattern to great effect and it contributes to a really nice developer experience.