惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

AI
AI
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
N
News and Events Feed by Topic
O
OpenAI News
W
WeLiveSecurity
S
Secure Thoughts
www.infosecurity-magazine.com
www.infosecurity-magazine.com
H
Heimdal Security Blog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
Schneier on Security
Schneier on Security
Cloudbric
Cloudbric
T
Threatpost
T
Troy Hunt's Blog
T
Tenable Blog
Hacker News - Newest:
Hacker News - Newest: "LLM"
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
V
Vulnerabilities – Threatpost
Google Online Security Blog
Google Online Security Blog
Recent Commits to openclaw:main
Recent Commits to openclaw:main
The Hacker News
The Hacker News
Know Your Adversary
Know Your Adversary
Forbes - Security
Forbes - Security
Cyberwarzone
Cyberwarzone
C
CXSECURITY Database RSS Feed - CXSecurity.com
P
Privacy & Cybersecurity Law Blog
L
Lohrmann on Cybersecurity
博客园 - 司徒正美
腾讯CDC
博客园 - 三生石上(FineUI控件)
爱范儿
爱范儿
J
Java Code Geeks
Hugging Face - Blog
Hugging Face - Blog
博客园 - 【当耐特】
M
MIT News - Artificial intelligence
T
The Blog of Author Tim Ferriss
MongoDB | Blog
MongoDB | Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
GbyAI
GbyAI
D
Docker
V
Visual Studio Blog
云风的 BLOG
云风的 BLOG
I
InfoQ
WordPress大学
WordPress大学
人人都是产品经理
人人都是产品经理
A
Arctic Wolf
L
LINUX DO - 热门话题
The Cloudflare Blog
雷峰网
雷峰网
Microsoft Azure Blog
Microsoft Azure Blog
小众软件
小众软件

oida.dev | TypeScript, Rust

TypeScript's `erasableSyntaxOnly` Flag Unsafe for work Tokio: Macros Tokio: Channels Tokio: Getting Started Network Applications on the Tokio Stack Remake, Remodel, Reduce. The `never` type and error handling in TypeScript 5 Inconvenient Truths about TypeScript Refactoring in Rust: Introducing Traits Refactoring in Rust: Abstraction with the Newtype Pattern Announcing the TypeScript Cookbook TypeScript: Iterating over objects The road to universal JavaScript 10 years of oida.dev Rust: Tiny little traits The TypeScript converging point How not to learn TypeScript Getting started with Rust Introducing Slides and Coverage TypeScript: The humble function overload TypeScript + React: Children types are broken TypeScript: In defense of any Rust: Enums to wrap multiple errors Dissecting Deno Error handling in Rust TypeScript: Unexpected intersections Upgrading Node.js dependencies after a yarn audit TypeScript: Array.includes on narrow types TypeScript + React: Typing Generic forwardRefs shared, util, core: Schroedinger's module names Learning Rust and Go TypeScript: Narrow types in catch clauses TypeScript: Low maintenance types Tidy TypeScript: Name your generics Tidy TypeScript: Avoid traditional OOP patterns Tidy TypeScript: Prefer type aliases over interfaces Tidy TypeScript: Prefer union types over enums My new book: TypeScript in 50 Lessons Go Preact! ❤️ this in JavaScript and TypeScript TypeScript and ECMAScript Modules TypeScript + React: Why I don't use React.FC TypeScript + React: Component patterns TypeScript: Augmenting global and lib.dom.d.ts Vite with Preact and TypeScript TypeScript: Union to intersection type 11ty: Generate Twitter cards automatically Are large node module dependencies an issue? TypeScript: Variadic Tuple Types Preview TypeScript: Improving Object.keys Remake, Remodel. Part 4. TypeScript + React: Typing custom hooks with tuple types TypeScript: Assertion signatures and Object.defineProperty TypeScript: Check for object properties and narrow down type Boolean in JavaScript and TypeScript void in JavaScript and TypeScript Symbols in JavaScript and TypeScript Why I use TypeScript TypeScript + React: Extending JSX Elements TypeScript: Validate mapped types and const context TypeScript: Match the exact object shape TypeScript: The constructor interface pattern Streaming your Meetup - Part 4: Directing and Streaming with OBS Streaming your Meetup - Part 3: Speaker audio Streaming your Meetup - Part 2: Speaker video Streaming your Meetup - Part 1: Basics and Projector TypeScript and React Guide: Added a new styles chapter TypeScript and React Guide: Added a new render props chapter TypeScript and React: Styles and CSS TypeScript and React TypeScript and React Guide: Added a new prop types chapter TypeScript without TypeScript -- JSDoc superpowers TypeScript: Mapped types for type maps JAMStack vs serverless web apps The Unsung Benefits of JAMStack Sites TypeScript: Ambient modules for Webpack loaders My most favourite talks in 2018 TypeScript and React Guide: Added a new context chapter TypeScript: Built-in generic types TypeScript: Type predicates JSX is syntactic sugar TypeScript and React Guide: Added a new hooks chapter Getting your CfP application right FAQ on our Angular Connect Talk: Automating UI development TypeScript and Substitutability Debugging Node.js apps in TypeScript with Visual Studio Code From Medium: Deconfusing Pre- and Post-processing From Medium: PostCSS misconceptions Saving and scraping a website with Puppeteer Cutting the mustard - 2018 edition My most favourite podcast episodes in 2017 My most favourite talks in 2017 My most favourite books in 2017 The Best Request Is No Request, Revisited Not so hidden figures - Organizing ScriptConf My podcast journey to ScriptCast Grid layout, grid layout everywhere! #scriptconf and #devone Object streams in Node.js
Wordpress as CMS for your JAMStack sites
Getting author information # · 2018-01-08 · via oida.dev | TypeScript, Rust

The almighty JAMStack brings you fast and secure static websites, and with things like headless content management systems they become even easy to edit! However, every once in a while you will find yourself in front of a Wordpress blog that has way too many articles (and way too many authors that fear change!) to be reasonably transferred. But Wordpress can be headless, too. In fact, Wordpress’ own hosting service uses its core only via API, the editing interface comes with the shiny new Calypso.

One of the best advantages of using a static site generator is that they usually don’t care where your content comes from. So let’s use the fancy Wordpress REST API, fetch some content and produce static sites!

In this example, I use Metalsmith as static site generator. Only because I work with it on a daily basis. And it’s pretty easy to get new plug-ins running. But other static site generators will work as well. You can use Jekyll generators for instance. And as long as your static site generator knows how to use JSON files as data input, you can use the code samples below to store fetched output in a pre-processing step. Let’s go!

The Wordpress API #

Every Wordpress installation comes with a full fledged JSON API. This means you can access posts and pages via URLs. This just sounds like a headless CMS to me! If you have a Wordpress installation running somewhere, add /wp-json/wp/v2/posts at the end of the main URL. You most likely will get some output! In fact, the latest 10 posts with all its metadata are presented to you in easy digestible JSON.

You will soon notice that the author field in each entry is but a number. This is Wordpress’ data structure. You would need to look up the table for authors, and Wordpress doesn’t have an API URL for that. However, there’s a secret flag you can add to get all the author data passed along, and it’s called _embed.

So with

https://url-to-your-blog/wp-json/wp/v2/posts?_embed

you have all the data you need!

Getting all posts #

If you have a ton of posts, the next challenge will be to get all of them. Sadly, this can’t be done with one single request. You can max the number of fetched posts to 100 by adding a new parameter called per_page:

https://url-to-your-blog/wp-json/wp/v2/posts?_embed&per_page=100

But after that, you have to fetch paginated. There’s the page parameter where you can select the page you want to retrieve. With that, you can either go recursively and fetch as long as there’s something to fetch. Or you check Wordpress’ custom HTTP headers for information on how many pages there are to fetch. In this example, I go for the latter. But be aware that your server’s CORS settings must allow to pass those headers through to your client. The custom header for the number of total pages is X-WP-TotalPages.

To retrieve data, I’m using isomorphic-fetch, which provides the same fetch API both for Node and the browser. Let’s see:

const fetch = require('isomorphic-fetch');

const mainURL = 'http://path-to-your-blog';
const apiURL = '/wp-json/wp/v2/posts';
const url = `${mainURL}${apiURL}?_embed&per_page=100`;

fetch(url) /* 1 */
.then(res => {
const noPages =
res.headers.get('X-WP-TotalPages'); /* 2 */
const pagesToFetch = new Array(noPages - 1)
.fill(0)
.map((el, id) =>
fetch(`${url}&page=${id+2}`)); /* 3 */
return Promise.all([res, ...(pagesToFetch)]); /* 4 */
})
.then(results =>
Promise.all(results.map(el => el.json()))) /* 5 */
.then(pages => [].concat(...pages)) /* 6 */

  1. Let’s fetch the first 100 posts from our blog. If our Wordpress blog has less than 100 posts, we won’t need any more fetches.
  2. The X-WP-TotalPages header has information on how many more pages we need to fetch.
  3. We create an array of fetch promises, fetching data from page 2 onwards (we fetched page 1 already).
  4. Promise.all allows us to pass the first result and all subsequent results from our pagesToFetch array.
  5. Next promise call: Convert all results to JSON.
  6. Finally, we convert all our results into one array containing all the post data from our blog.

The next .then call will include one array with all blog entries. You can store this data as JSON file (if your static site generator is not extendable), or in our case: Create actual page data we want to generate.

Add your posts to Metalsmith #

Metalsmith is – like any other static site generator – aware of a source directory which contains files. Most likely Markdown. These files are then converted into HTML. However, Metalsmith also allows adding data from any other source. It’s pretty easy to manipulate the files array and add new files. The only thing you need to know is that each file needs a unique key: The URL or path it is going to be stored. The contents of each entry is an object with all the data you want to store. Let’s check it out!

Wordpress metalsmith plug-in #

Metalsmith works with plug-ins. Each run of the Metalsmith build pipeline runs through the list of plug-ins you defined, much like Gulp does.

Let’s use the code sample from above and extend it to a Metalsmith plug-in:

const { URL } = require('url');

const wordpress = (url) => (files, smith, done) => { /* 1 */
fetch(url)
/* ... include code from above ...*/
.then(allPages => {
allPages.forEach(page => {
const relativeURL
= new URL(page.link).pathname; /* 2 */
const key = `./${relativeURL}/index.html`;
let value = page; /* 3 */
value.layout = 'post.hbs';
value.contents =
new Buffer(page.content.rendered, 'utf8');
files[key] = value; /* 4 */
});
done(); /* 5 */
});
}

  1. The interface for Metalsmith plug-ins is (files, metalsmith, done). The first parameter is the set of files which needs to be transformed to HTML. The second parameter is the Metalsmith object, which contains all the metadata information of the Metalsmith build. Parameter three is a done callback function. This is particularly helpful for async operations. Call it when your plug-in is finished.
  2. Once we have all posts from the API calls (see above), we need to transform some data. First, we change the permalinks from Wordpress to something Metalsmith can work with. We use Node’s URL package to get the relative URL (sans domain name), and make a relative path on the file system out of it. Notice that we add index.html. In doing so, we create lots of folders with one HTML file inside. Prettified URLs for static sites.
  3. Next, we create key/value pairs for the file object. Each value is one entry of the post array we retrieved earlier. Additionally, we add a layout flag (for metalsmith-layouts) and set the contents (this is also something metalsmith-layouts needs to work properly).
  4. After that, we store the value under the relative path name we defined earlier.
  5. Once we did that for all posts, we call the done() callback to end our plug-ins process.

Perfect. In just a few lines of code we told Metalsmith to extend the files it already transforms with the files we fetched from an API. This makes Metalsmith extremely powerful, because you are not tied to a single CMS any more. In fact, you can hook into a ton of new and legacy content management systems and still produce one output. Nice!

Metalsmith build pipeline #

We want to use our new plug-in with a very straightforward Metalsmith build pipeline. We don’t use much but a layout plug-in that builds upon Handlebars to squeeze our content into something more semantically correct.

const Metalsmith = require('metalsmith');
const layouts = require('metalsmith-layouts');

/** the plug-in from above **/

Metalsmith('.')
.use(wordpress(apiURL))
.use(layouts({
engine: 'handlebars'
}))
.source('./source')
.destination('./build')
.build((err) => {
if (err) throw err;
console.log('Finished');
});

This pipeline fetches all data from the Wordpress API, and then runs it through metalsmith-layouts. After we call build, the pipeline is actually executed. Run this file, and you will see a build directory in your file system.

Layout file #

The layout file is a handlebars file which defines the basic HTML structure. contents refers to the field we defined earlier in our Wordpress Metalsmith plug-in. The rest comes from the object directly, including the _embedded Author data. It’s pretty straightforward:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{{title.rendered}}</title>
</head>
<body>
<h1>{{title.rendered}}</h1>
{{{contents}}}

<aside>
by {{_embedded.author.0.name}}
</aside>
</body>
</html>

Next steps #

Sweet! After I got acquainted with the Wordpress API, fetching all the contents and creating static sites out of it was super easy. You can find a sample repository on Github. Let me know what you think.

Next steps would be to create a small Wordpress plug-in (a real one, in PHP and all that), that uses the publish hook to automatically kick off your continuous integration system. But knowing Wordpress’ vast plug-in ecosystem, something like that might actually already exist.

Related Articles