├── .eleventy.js ├── .gitignore ├── LICENSE ├── README.md ├── content ├── _data │ ├── pages.js │ ├── posts.js │ └── wordpress.js ├── page │ └── page.njk └── post │ └── post.njk └── package.json /.eleventy.js: -------------------------------------------------------------------------------- 1 | module.exports = config => { 2 | 3 | return { 4 | 5 | dir: { 6 | input: 'content', 7 | output: `build` 8 | } 9 | 10 | }; 11 | 12 | }; -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | _site/ 2 | _tmp/ 3 | build/ 4 | .DS_Store 5 | node_modules/ 6 | package-lock.json 7 | .env* -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Dr. Matt Lee 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WordPress to Eleventy tool 2 | 3 | A tool to help you migrate from WordPress to Eleventy, but also can be used with WordPress to make a static website. 4 | 5 | Not affliated with WordPress or Eleventy. 6 | 7 | 1. Clone this repo. 8 | 2. `npm install` 9 | 3. `DOMAIN_NAME=example.com npx eleventy --output=_site` 10 | 11 | * This will assume https:// 12 | * This now supports wordpress.com blogs 13 | * You'll need https://nodejs.org/ or something similar installed on your computer 14 | 15 | ## What this is supposed to do 16 | 17 | You should get a static copy of your WordPress pages and posts in _site. 18 | 19 | I set up a [demo site](https://suchexample.wordpress.com) with a post and a couple pages you can see it here: 20 | 21 | `DOMAIN_NAME=suchexample.wordpress.com npx eleventy --output=_site` 22 | 23 | ## What this doesn't do 24 | 25 | It doesn't move over any images, in fact it doesn't know about your `wp-content` folder at all. 26 | 27 | based on https://www.sitepoint.com/wordpress-headless-cms-eleventy/ 28 | 29 | # Buzzwords 30 | 31 | * "Synergy!" -- [Todd](https://toddpresta.com) 32 | -------------------------------------------------------------------------------- /content/_data/pages.js: -------------------------------------------------------------------------------- 1 | const {getAllPosts} = require("./wordpress"); 2 | 3 | // process WordPress posts 4 | module.exports = async function () { 5 | return getAllPosts("pages"); 6 | }; -------------------------------------------------------------------------------- /content/_data/posts.js: -------------------------------------------------------------------------------- 1 | const {getAllPosts} = require("./wordpress"); 2 | 3 | // process WordPress posts 4 | module.exports = async function () { 5 | return getAllPosts("posts"); 6 | }; -------------------------------------------------------------------------------- /content/_data/wordpress.js: -------------------------------------------------------------------------------- 1 | // fetch WordPress posts 2 | const domainName = process.env.DOMAIN_NAME; 3 | const wordpressAPI = domainName.endsWith("wordpress.com") 4 | ? `https://public-api.wordpress.com/wp/v2/sites/${domainName}` 5 | : `https://${domainName}/wp-json/wp/v2`; 6 | 7 | async function getAllPosts(postType) { 8 | // get number of pages 9 | const pages = await wpPageCount(postType); 10 | if (!pages) return []; 11 | 12 | // fetch all pages of posts 13 | const wpList = []; 14 | for (let w = 1; w <= pages; w++) { 15 | wpList.push(wpPosts(postType, w)); 16 | } 17 | 18 | const all = await Promise.all(wpList); 19 | return all.flat(); 20 | } 21 | module.exports.getAllPosts = getAllPosts; 22 | 23 | // fetch number of WordPress post pages 24 | async function wpPageCount(postType) { 25 | try { 26 | const res = await fetch(`${wordpressAPI}/${postType}?orderby=date&order=desc&per_page=100&_fields=id&page=1`); 27 | console.log(`Found ${res.headers.get('X-WP-Total')} ${postType}`); 28 | return res.headers.get('X-WP-TotalPages') || 0; 29 | } catch (err) { 30 | console.log(`WordPress API call failed: ${err}`); 31 | return 0; 32 | } 33 | } 34 | 35 | // fetch list of WordPress posts 36 | async function wpPosts(postType, page = 1) { 37 | try { 38 | const 39 | res = await fetch(`${wordpressAPI}/${postType}?orderby=date&order=desc&per_page=100&_fields=id,slug,date,title,excerpt,content&page=${page}`), 40 | json = await res.json(); 41 | 42 | // return formatted data 43 | return json 44 | .filter(p => p.content.rendered && !p.content.protected) 45 | .map(p => { 46 | return { 47 | slug: p.slug, 48 | date: new Date(p.date), 49 | dateYMD: dateYMD(p.date), 50 | dateURL: dateURL(p.date), 51 | dateFriendly: dateFriendly(p.date), 52 | title: p.title.rendered, 53 | excerpt: wpStringClean(p.excerpt.rendered), 54 | content: wpStringClean(p.content.rendered) 55 | }; 56 | }); 57 | 58 | } catch (err) { 59 | console.log(`WordPress posts API call failed: ${err}`); 60 | return null; 61 | } 62 | } 63 | 64 | 65 | // pad date digits 66 | function pad(v = '', len = 2, chr = '0') { 67 | return String(v).padStart(len, chr); 68 | } 69 | 70 | // format date as YYYY/MM/DD 71 | function dateURL(d) { 72 | d = new Date(d); 73 | return d.getFullYear() + '/' + pad(d.getMonth() + 1) + '/' + pad(d.getDate()); 74 | } 75 | 76 | // format date as YYYY-MM-DD 77 | function dateYMD(d) { 78 | d = new Date(d); 79 | return d.getFullYear() + '-' + pad(d.getMonth() + 1) + '-' + pad(d.getDate()); 80 | } 81 | 82 | // format friendly date 83 | function dateFriendly(d) { 84 | const toMonth = new Intl.DateTimeFormat('en', { month: 'long' }); 85 | d = new Date(d); 86 | return d.getDate() + ' ' + toMonth.format(d) + ', ' + d.getFullYear(); 87 | } 88 | 89 | 90 | // clean WordPress strings 91 | function wpStringClean(str) { 92 | return str.replace(new RegExp(`https:\/\/${domainName}`, "ig"), '').trim(); 93 | } -------------------------------------------------------------------------------- /content/page/page.njk: -------------------------------------------------------------------------------- 1 | --- 2 | pagination: 3 | data: pages 4 | alias: page 5 | size: 1 6 | permalink: "/{{ page.slug | slug }}/index.html" 7 | --- 8 | 9 | 10 | 11 | 12 | {{ page.title }} 13 | 14 | 17 | 18 | 19 | 20 |

{{ page.title }}

21 | 22 |

23 | 24 | {{ page.content | safe }} 25 | 26 | 27 | -------------------------------------------------------------------------------- /content/post/post.njk: -------------------------------------------------------------------------------- 1 | --- 2 | pagination: 3 | data: posts 4 | alias: post 5 | size: 1 6 | permalink: "/{{ post.dateURL }}/{{ post.slug | slug }}/index.html" 7 | --- 8 | 9 | 10 | 11 | 12 | {{ post.title }} 13 | 14 | 17 | 18 | 19 | 20 |

{{ post.title }}

21 | 22 |

23 | 24 | {{ post.content | safe }} 25 | 26 | 27 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wp11ty", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "scripts": { 6 | "test": "echo \"Error: no test specified\" && exit 1", 7 | "start": "npx @11ty/eleventy --serve", 8 | "build": "npx @11ty/eleventy" 9 | }, 10 | "author": "", 11 | "license": "ISC", 12 | "description": "", 13 | "devDependencies": { 14 | "@11ty/eleventy": "^2.0.1" 15 | } 16 | } 17 | --------------------------------------------------------------------------------