UNPKG

blogger-feeds

Version:
192 lines (132 loc) 6.35 kB
# 📰 blogger-feeds [![minzipped](https://badgen.net/bundlephobia/minzip/blogger-feeds?color=blue)](https://bundlephobia.com/package/blogger-feeds) [![typescript](https://badgen.net/npm/types/blogger-feeds)](https://www.npmjs.com/package/blogger-feeds) Fully typed url builder, client and utilities for [Blogger](https://www.blogger.com) feeds <img width="300" src="./docs/public/blogger-feeds-logo.svg" /> *We [Blogger](https://www.blogger.com)* ## 👨‍💻 Installation ### Via npm ``` bash pnpm i blogger-feeds ``` ### Via cdn ``` html <script type="module"> const { make } = await import("https://cdn.jsdelivr.net/npm/blogger-feeds@latest/core") </script> ``` ## 📦 Available exports You can import just what you need to further tree-shake the package size ``` js // Imports all at once import * as BF from "blogger-feeds"; import { make, // just the url builder function } from "blogger-feeds/core"; import { client, // just the client async generator } from 'blogger-feeds/client'; import { isoDate, // blogger-compatible date formatter merge, // merges blogger feeds urls or configs thumb, // remaps a blogger image url to a resized version ytimg, // remaps a youtube url to its default hq thumbnail } from "blogger-feeds/helpers"; ``` ## 🤔 Why This library serves a very specific use-case: **read-only posts fetching on Blogger blogs**, when the excellent [Gapi](https://github.com/google/google-api-javascript-client) + [Blogger v3 api](https://developers.google.com/blogger/docs/3.0/reference) may be *"overkill"*. In example when building a lightweight *preact / alpinejs widget*. ## 🔧 How it works ### Url builder: `make()` It's very a simple **url builder function**: * builds on top of the **native js URL interface** * takes a fully typed config object of **blogger params** * validates them against **Blogger conventions** * builds a native js URL to the requested resource * **explicits blogger defaults** * normalizes the url for cache consistency ### Client generator: `client()` An **async function generator** that: * builds on top of the **native `fetch()` api** * **lazily loops** over all posts starting from input * reuses Blogger's own pagination features * can be used in a handy **`for await ... of` loop** * returns a **fully-typed feed object** (caveat) * or a fully transparent error object ## Requirements The url builder `make()` at the core of the package works by itself, enforcing parameter-level **validation** and expliciting blogger defaults for **url consistency** (helping caches work). You may use the `client()` **directly on a Blogger page** with no additional setup. But in **any other environment**, since Blogger *correctly* enforces strict *CORS headers*, you are most likely to incur into **cors issues**. To avoid that: 1. Blogger must be setup with a **custom domain** 2. that domain should be **sending proper [CORS headers](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS)** To achieve this setup I am using [Cloudflare's generous free plan](https://www.cloudflare.com/plans/free), but any other solution is equally viable as long as it allows to enforce **your own cors headers**. ## Caveats 1. Please note that the **Feed object is not the fetched data**, but a **simplified representation** exposing: * **relevant meta data** related to the request itself * a flattened **susbet of relevant resource data** 2. Should you need to access raw feeds data, you may prefer to avoid the client and directly fetch the url. 3. **No sanitization is applied to any content returned from Blogger feeds**. Please make sure to **sanitize post.body html output** using an html sanitizer library (like [dom-purify](https://github.com/cure53/DOMPurify), [sanitize-html](https://github.com/apostrophecms/sanitize-html), [js-xss](https://jsxss.com/en/index.html)...) ## 💪 Usage ``` js //---------------------- // import what you need //---------------------- import { make, client } from "blogger-feeds"; //---------------------- // build an url //---------------------- const SinglePostUrl = make({ // you may pass "blogger" to get the blogger.com equivalent blog: new URL("https://www.my-blogger.com"), postId: "1234567", // at least 7 digits // postId takes precedence over any other params // all the rest will be ignored }); // null if invalid config console.log(SinglePostUrl?.href); const PagedPostsUrl = make({ blog: new URL("https://www.my-blogger.com"), // never pass a valid postId // when you need a paginated list // blogger's defaults will be added for consistency // "max-results": defaults to 25 // "start-index": defaults to 1 // "orderby": defaults to "published" }); console.log(PagedPostsUrl?.href); //---------------------- // fetch a resource //---------------------- // you may abort the client at any moment const Aborter = new AbortController(); const DefaultClient = client({ make: { // same config as make() blog: new URL("https://www.my-blogger.com"), }, opt: { // custom fetch options signal: Aborter.signal, }, // cherry-pick meta or post props keep: ["href", "title", "image"], }); for await (const feed of DefaultClient) { console.log(feed); // {meta: {...}, data: Array(25)} // it loops as long as there are still posts to fetch // take a look at meta.next } const PostClient = client({ // reuse this url from: SinglePostUrl, make: { // with new params postId: '9876543', } }) for await (const post of PostClient) { console.log(post); // {meta: {...}, data: Array(1)} // always 1 post in data array } ``` ## Disclaimer This library is **very opinionated**, it was built to consolidate code patterns from **custom Blogger widgets** currently employed in **public-facing production blogs**. I remark that it is **not meant to replace GAPI client**, but it's just a different approach to fetch published posts in a **read-only scenario**, heavily inspired by Blogger's own dynamic templates. **The library is neither affiliated or approved by [Google Blogger](https://www.blogger.com)**