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

express-dom

v8.11.2

Published

Prepare, render web pages - express middleware

Downloads

199

Readme

express-dom

Express middleware for (pre)rendering web pages with playwright.

Uses system-installed chromium or google-chrome.

Synopsis

const express = require('express');
const app = express();
const dom = require('express-dom');

app.get('*.html', dom(), express.static('public/'));

To do rendering outside express middleware:

// obj can be a url string, or an IncomingMessage,
// or an object with { url, status?, headers?, body? } properties
// status defaults to 200, headers to { Content-Type: 'text/html' }

const res = await dom()(obj);
const { statusCode, headers, body } = res;

A page is requested by a browser for three purposes:

  • offline: hidden offline web page changed by outside scripts
  • online: hidden online web page built by its own scripts
  • visible: show the web page to the user

The offline phase is done only on the server, while the online phase is typical prerendering and can be done on the server or the user browser.

Configuration functions can setup a handler instance, valid for all requests on that handler.

Routers can change settings depending on the current request.

Plugins can change page settings before it is loaded, and can run scripts when the page is 'idle'.

A phase is skipped if it has no registered plugins.

The 'idle' event is emitted on the page instance after DOMContentLoaded, and after requests have settled and custom or default tracker has resolved.

The listeners of the 'idle' event can be asynchronous and are run serially.

Options

dom holds some global settings:

  • browser: the playwright channel to use, defaults to 'chrome'
  • executable: the path to a browser executable (optional)
  • pageMax: number of open pages per browser
  • pageUse: number of uses before recycling browser
  • debug: show browser, disables timeout. Also set by PWDEBUG=1.
  • defaults: per-instance settings
  • plugins: map of plugins functions
  • online, offline: per-phase settings defaults

Middleware settings:

  • log: boolean, or level (info, log, warn, error)
  • timeout: async resources timeout
  • scale: changes window.devicePixelRatio
  • cookies (used only with cookies plugin)

Handler properties:

  • online, offline, visible: custom phase settings, takes precedence

Phase settings:

  • policies: object for configuring Content-Security-Policies
  • enabled: boolean
  • track: boolean, or custom track function (see below)
  • styles: list of css strings
  • scripts: list of [function, arg?] pairs
  • plugins: list (set) of names

Default offline settings:

  • enabled: false
  • track: false
  • plugins: console, hidden, html
  • policies: default: "'none'"

Default online settings:

  • enabled: true
  • track: true
  • plugins: console, hidden, cookies, referer, redirect, html
  • policies:
    • default: "'none'"
    • script: "'self' 'unsafe-inline'"
    • connect: "'self'"

Mind that policies of the requesting phase are obtained from settings of the responding phase: route handler cannot change policies of current phase.

tracker

If phase setting track is true, the default tracker waits for async operations:

  • loading of script/link nodes
  • DOMContentLoaded listeners
  • fetch, xhr calls
  • timeouts (capped by page timeout)
  • animation frame requests
  • microtasks

Otherwise, track can be a custom async function that is evaluated by the default tracker to determine when the page has settled.

When track is false, the idle event just wait for first batch of files to be loaded.

Route settings

Route-dependent configuration can be done by passing to dom():

  • an object with { online, offline, visible } settings
  • a function accepting a handler instance as argument

Phase settings

Configuration depending on the route and the phase can be set using a router function accepting (phase, req, res) as argument.

dom().route((phase, req, res) => {
  // change phase.settings.plugins depending on req and phase.online/offline/visible
})

phase has the following properties:

  • visible, online, offline: booleans, purpose of the requesting phase
  • settings: current phase settings
  • policies: requesting phase policies
  • location: parse url of the current phase
app.get('*.html', dom().route((phase, req, res) => {
  if (phase.visible && req.query.url) {
    // overwrite default location
    location.href = req.query.url;
  } else if (phase.online) {
    res.type('html');
    res.send('<html><script src="asset.js"></script></html>');
  }
}));

Page settings and plugins

Plugins are asynchronous functions, executed in order.

dom.plugins.fragment = async (page, settings, req, res) => {
  settings.timeout = 30000;
  page.on('idle', async () => {
    const html = await page.evaluate(sel => {
      return document.querySelector(sel)?.outerHTML;
    }, req.query.fragment);
    if (html) {
      res.type('html');
      res.send(html);
    } else {
      // html plugin will send page content
    }
  });
};
dom.online.plugins.delete('html').add('fragment').add('html');
app.get('*.html', dom(), express.static(app.get('views')));

page is a playwright page instance, with additional page.location, a URL instance that can be modified synchronously.

Bundled plugins

This is a limited list of plugins, some are used by default:

  • console Report browser console to node console. Depends on settings.log value.

  • hidden Force document.visibilityState == "hidden". Adds user stylesheet to keep rendering to minimum; Honors settings.hidden boolean, if set by a previous plugin.

  • media Sets media options, see playwright doc.

  • cookies If settings.cookies is true, copy all cookies, else only copy cookies with names in this Set. Defaults to an empty Set.

  • equivs Parse meta[http-equiv] tags and set response headers accordingly. Supports http-equiv="Status".

  • languages Parses request Accept-Language and sets navigator.languages. Sets Content-Language to the lang attribute found on documentElement.

  • preloads Parse link[rel=preload] tags and set 'Link' response header.

  • referrer, referer Sets headers.referer to express req.get('referrer')

  • redirect catch navigation requests and instead sends a 302 redirection

Compatibility with caching proxies

express-dom currently uses Sec-Purpose request header, and set Vary: Sec-Purpose response headers, so all proxies should be okay with that.

Logs

  • DEBUG=express-dom

Backend

express-dom installs playwright-core and expects a system-installed chrome browser to be available.

License

MIT License, see LICENSE file.