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

datocms-plugin-web-previews

v1.0.18

Published

Offer your editors side-by-side previews of unpublished/draft content, directly within DatoCMS

Downloads

149

Readme

Web Previews DatoCMS plugin

This plugin adds side-by-side previews, and quick links in the record sidebar to preview your webpages.

🚨 Important: This is not a drag & drop plugin! It requires a lambda function on your frontend website(s) in order to function. Read more in the following sections!

Installation and configuration

Once the plugin is installed you need to specify:

  • A list of frontends. Each frontend specifies a name and a preview webhook, which will be called as soon as the plugin is loaded. Read more about it on the next chapter.
  • Sidebar open: to specify whether you want the sidebar panel to be opened by default.

⚠️ For side-by-side previews to work, if your website implements a Content Security Policy frame-ancestors directive, you need to add https://plugins-cdn.datocms.com to your list of allowed sources, ie.:

Content-Security-Policy: frame-ancestors 'self' https://plugins-cdn.datocms.com;

The Previews webhook

Each frontend must implement a CORS-ready JSON endpoint that, given a specific DatoCMS record, returns an array of preview link(s).

The plugin performs a POST request to the Previews webhook URL, passing a payload that includes the current environment, record and model:

{
  "item": {…},
  "itemType": {…},
  "currentUser": {…},
  "environmentId": "main",
  "locale": "en",
}
  • item: CMA entity of the current record
  • itemType: CMA entity of the model of the current record
  • currentUser: CMA entity of the collaborator, SSO user or account owner currently logged in
  • environmentId: the current environment ID
  • locale: the locale currently active on the form

The endpoint is expected to return a 200 response, with the following JSON structure:

{
  "previewLinks": [
    {
      "label": "Published (en)",
      "url": "https://mysite.com/blog/my-article"
    },
    {
      "label": "Draft (en)",
      "url": "https://mysite.com/api/preview/start?slug=/blog/my-article"
    }
  ]
}

The plugin will show all the preview links that are returned. If you want to make sure that a preview's URL is reloaded after each save, you can include an extra option (please be aware that because of cross-origin iframe issues, maintaining the scroll position between reloads will not be possible):

{
  "label": "Draft (en)",
  "url": "https://mysite.com/api/preview/start?slug=/blog/my-article",
  "reloadPreviewOnRecordUpdate": { "delayInMs": 100 }
}

Implementation examples

If you have built alternative endpoint implementations for other frameworks/SSGs, please open up a PR to this plugin and share it with the community!

Next.js

We suggest you look at the code of our official Next.js Starter Kit:

Lightweight Authentication

In our Next.js starter kit, the preview link URLs also include a token query parameter that the plugin would send to the webhook receiver, like https://www.mywebsite.com/api/preview-links?token=some-secret-ish-string. The token is a string of your choice that just has to match in both the plugin settings and in your frontend's environment variables. While not encryption, this token is an easy way to limit access to your preview content.

Nuxt 3

Below here, you'll find a similar example, adapted for Nuxt. For the purpose of this example, let's say we want to return a link to the webpage that contains the published content.

If you deploy on a provider that supports edge functions, Nuxt 3 applications can expose a dynamic API: files in the /server/api folders will be converted into endpoints. So it's possible for DatoCMS to make a POST request to the Nuxt app with the info about the current record. What we'll actually do, is to implement a CORS enabled API endpoint returning an array of preview links built on the base of the record, the item type and so on:

// Put this code in the /server/api directory of your Nuxt website (`/server/api/preview-links.ts` will work):
// this function knows how to convert a DatoCMS record into a canonical URL within the website.

// this function knows how to convert a DatoCMS record
// into a canonical URL within the website
const generatePreviewUrl = ({ item, itemType, locale }) => {
  switch (itemType.attributes.api_key) {
    case 'landing_page':
      return `/landing-pages/${item.attributes.slug}`;
    case 'blog_post':
      // blog posts are localized:
      const localePrefix = locale === 'en' ? '' : `/${locale}`;
      return `${localePrefix}/blog/${item.attributes.slug[locale]}`;
    default:
      return null;
  }
};

export default eventHandler(async (event) => {
  // In this method, we'll make good use of the utility methods that 
  // H3 make available: they all take the `event` as first parameter.
  // For more info, see: https://github.com/unjs/h3#utilities

  // Setup content-type and CORS permissions.
  setResponseHeaders(event, {
    'Content-Type': 'application/json',
    'Access-Control-Allow-Origin': '*',
    'Access-Control-Allow-Methods': 'POST',
    'Access-Control-Allow-Headers': 'Content-Type, Authorization', // add any other headers you need
  })

  // This will allow OPTIONS request
  if (event.req.method === 'OPTIONS') {
    return send(event, 'ok')
  }

  // Actually generate the URL using the info that DatoCMS is sending.
  const url = generatePreviewUrl(await readBody(event))

  // No URL? No problem: let's send back no link.
  if (!url) {
    return { previewLinks: [] }
  }

  // Let's guess the base URL using environment variables:
  // if you're not working with Vercel or Netlify,
  // ask for instructions to the provider you're deploying to.
  const baseUrl = process.env.VERCEL_BRANCH_URL
    ? // Vercel auto-populates this environment variable
      `https://${process.env.VERCEL_BRANCH_URL}`
    : // Netlify auto-populates this environment variable
      process.env.URL

  // Here is the list of links we're returnig to DatoCMS and that
  // will be made available in the sidebar of the record editing page.
  const previewLinks = [
    // Public URL:
    {
      label: 'Published version',
      url: `${baseUrl}${url}`,
    },
  ]

  return { previewLinks }
})

SvelteKit 2

Below here, you'll find a similar example, adapted for SvelteKit. For the purpose of this example, let's say we want to return a link to the webpage that contains the published content.

Create a +server.ts file under src/routes/api/preview-links/ with following contents:

import { json } from '@sveltejs/kit';

const generatePreviewUrl = ({ item, itemType, locale }: any) => {
  switch (itemType.attributes.api_key) {
    case 'landing_page':
      return `/landing-pages/${item.attributes.slug}`;
    case 'blog_post':
      // blog posts are localized:
      const localePrefix = locale === 'en' ? '' : `/${locale}`;
      return `${localePrefix}/blog/${item.attributes.slug[locale]}`;
    case 'post':
      return `posts/${item.attributes.slug}`;
    default:
      return null;
  }
};

const corsHeaders = {
  'Access-Control-Allow-Origin': '*',
  'Access-Control-Allow-Methods': 'POST',
  'Access-Control-Allow-Headers': 'Content-Type, Authorization',
  'Content-Type': 'application/json'
};

export async function OPTIONS() {
  setHeaders(corsHeaders);

  return json('ok');
}

export async function POST({ request, setHeaders }) {
  setHeaders(corsHeaders);

  const data = await request.json();

  const url = generatePreviewUrl(data);

  if (!url) {
    return json({ previewLinks: [] });
  }

  const baseUrl = process.env.VERCEL_BRANCH_URL
    ? // Vercel auto-populates this environment variable
      `https://${process.env.VERCEL_BRANCH_URL}`
    : // Netlify auto-populates this environment variable
      process.env.URL;

  const previewLinks = [
    // Public URL:
    {
      label: 'Published version',
      url: `${baseUrl}${url}`
    }
  ];

  return json({ previewLinks });
}