🐑 Commons Host

Documentation

@commonshost/manifest

API

npm install @commonshost/manifest
const {
  schema,
  validate,
  normalise,
  trace,
  generate,
  translate
} = require('@commonshost/manifest')

const manifest = [
  {
    get: '/index.html',
    push: ['/app.js', '/design.css']
  }
]

Table of Contents

schema

Use the formal JSON Schema with your validator of choice.

const validator = new MyValidator()
validator.validate(manifest, schema)

validate(manifest)

Check if the syntax is valid.

try {
  validate(manifest)
  // Returns `true` or throws a validation error
} catch (error) {
  console.error(error)
}

normalise(manifest)

Transform the shorthand rules into a fully exploded format.

normalise(manifest)

// Returns:
[
  {
    get: [{glob: ['/index.html']}],
    push: [{glob: ['/app.js', '/design.css'], priority: 16}]
  }
]

trace(manifest, filepaths, entry[, dependencies])

Finds the dependencies of an entry file in a list of filepaths according to the manifest rules. Recursively traces to resolve all inherited dependencies.

Adds each dependency to the dependencies object. This must be a Map-like object with .has and .set methods.

Each dependency's key is the file path and its value is the push priority (ranging from 1 to 256, inclusive).

const entry = '/index.html'

const filepaths = [
  '/index.html',
  '/design.css',
  '/photo.jpg',
  '/app.js',
  '/lib.js'
]

const dependencies = new Map()

trace(manifest, filepaths, entry, dependencies)

// dependencies:
// Map {
//   '/app.js' => 256,
//   '/design.css' => 256
// }

generate(root)

Automatically generate a manifest. Processes all static HTML, JS, and CSS files in the directory path root to trace their dependencies.

Priorities are assigned by resource type.

await generate('/var/www/public')

// Returns:
[
  {
    get: '/index.html',
    push: [
      {glob: ['/app.js'], priority: 256},
      {glob: ['/cartoon-sans.ttf'], priority: 128},
      {glob: ['/photo.png'], priority: 1}
    ]
  },
  {
    get: '/app.js',
    push: [
      {glob: ['/lib.js'], priority: 256}
    ]
  }
]

translate(manifest, format)

Converts a JSON server push manifest to the syntax used by varions web servers and CDNs.

await translate(manifest, format)

Supported format values:

Format: nginx

location = "/index.html" {
    http2_push "/app.js";
    http2_push "/design.css";
}

NGINX documentation: ngx_http_v2_module

Format: apache

<Location "/">
  H2PushResource add "/app.js"
  H2PushResource add "/design.css"
</Location>

Apache documentation: mod_http2

Format: h2o

paths:
  "/index.html":
    mruby.handler: |
      Proc.new do |env|
        [399, {"link" => <<~eos
          </app.js>; rel=preload
          </design.css>; rel=preload
        eos
        }, []]
      end

H2O documentation: http2-*

Format: cloudflare

const manifest = new Map([
  ['/', '</app.js>; rel=preload; as=script, </design.css>; rel=preload; as=style']
])

self.addEventListener('fetch', async (event) => {
  const response = await self.fetch(event.request)
  const {pathname} = new URL(event.request.url)
  if (manifest.has(pathname)) {
    const header = manifest.get(pathname)
    response.headers.set('link', header)
  }
  event.respondWith(response)
})

Cloudflare Workers documentation

Format: fastly

sub vcl_recv {
#FASTLY recv
  if (fastly_info.is_h2 && req.url ~ "^/")
  {
    h2.push("/app.js");
    h2.push("/design.css");
  }
}

Fastly documentation: h2.push()

Format: netlify-toml

[[headers]]
  for = "/"
  [headers.values]
    Link = '''
    </app.js>; rel=preload; as=script, \
    </design.css>; rel=preload; as=style'''

Netlify documentation: netlify.toml

Format: netlify-headers

/
  Link: </app.js>; rel=preload; as=script
  Link: </design.css>; rel=preload; as=style

Netlify documentation: _headers