Pagefold

Writing a plugin

Build your own plugin with the same base class the built-in ones use.

A plugin is a class that extends Plugin. It registers hooks when it is mounted and Pagefold removes them when it is unmounted.

import { Plugin } from 'pagefold'

export default class AnalyticsPlugin extends Plugin {
  name = 'AnalyticsPlugin'

  options
  static defaults = { trackingId: null }

  constructor(options = {}) {
    super()
    this.options = { ...AnalyticsPlugin.defaults, ...options }
  }

  mount() {
    this.on('page:view', this.track)
  }

  track(visit, { url, title }) {
    window.gtag?.('config', this.options.trackingId, { page_path: url, page_title: title })
  }
}
new Pagefold({ plugins: [new AnalyticsPlugin({ trackingId: 'G-XXXX' })] })

Start from the template

The repository has a small plugin project ready to copy: TypeScript, one test and a build that publishes the types.

npx degit andresclua/pagefold/templates/plugin my-plugin
cd my-plugin && npm install && npm test

Change the name, write your idea in mount() and publish. Call the package pagefold-plugin-<name> and add the pagefold-plugin keyword so people can find it.

Its life

In your code
new Plugin(options)plugins: [plugin] or pagefold.use()
Pagefold calls
mount()your hooks run on every visitunmount()
unmount() runs on unuse() or destroy(). The hooks registered with this.on are removed for you.

What the base class gives you

MemberWhat it does
this.pagefoldThe instance, available from mount() on
this.on(hook, handler, options?)Register a handler. It is bound to the plugin (this works) and removed on unmount
this.before, this.once, this.replaceThe same with the matching option
mount()Called when the plugin is installed. Register hooks, add listeners
unmount()Called when it is removed. The base class removes the hooks; if you add listeners or elements, remove them and call super.unmount()
requires{ pagefold: '>=0.1' }. Installing fails with a clear error when the version is too old. Only >= ranges are supported

Name the class and set name. It is used by pagefold.findPlugin('AnalyticsPlugin').

A plugin that cleans up

export default class ScrollSpy extends Plugin {
  name = 'ScrollSpy'

  mount() {
    window.addEventListener('scroll', this.onScroll)
    this.on('visit:start', this.reset)
  }

  unmount() {
    window.removeEventListener('scroll', this.onScroll)
    super.unmount()
  }

  onScroll = () => {}
  reset = () => {}
}

Use arrow functions as class fields for listeners you add yourself, so removeEventListener receives the same function.

Custom hooks

A plugin can offer hooks of its own, so that other code can extend what it does:

mount() {
  this.pagefold.hooks.create('my:hook')
}

run() {
  return this.pagefold.hooks.call('my:hook', undefined, { some: 'data' }, (visit, args) => {
    // what happens by default
  })
}

Anyone can then listen with pagefold.hooks.on('my:hook', ...), run before the default with before, or take over with replace. Using hooks has a complete example, what each argument of call is, and how to type the hook in TypeScript.

Conventions the built-in plugins follow

  • Defaults as static defaults, options merged in the constructor.
  • One export, export default class, in its own module, so bundlers only include what is imported.
  • Do not change what you do not own. Add and remove only your own classes and elements.
  • Everything optional. A plugin with no options must do something useful.
  • Say what you did through this.pagefold.log(message), which the Debug plugin shows.