|
1 | 1 | # martinstabe.github.io |
2 | 2 |
|
3 | | -This repository now contains a parallel Eleventy implementation of `martinstabe.com`, migrated from the original Jekyll site while preserving the historical content archive and legacy permalink structure. |
| 3 | +A new Eleventy implementation of `martinstabe.com`, migrated from the original Jekyll site while preserving the historical content archive and legacy permalink structure. |
4 | 4 |
|
5 | | -The original Jekyll source is still present in the repository: |
| 5 | +Some of the original Jekyll source is still present in the repository: |
6 | 6 |
|
7 | 7 | - posts remain in `_posts/` |
8 | 8 | - Sass sources remain in `css/` and `_sass/` |
9 | | -- legacy tag metadata remains in `tags/` |
10 | | -- the original Jekyll layouts and includes remain in `_layouts/` and `_includes/` |
11 | 9 |
|
12 | 10 | The active Eleventy source lives in `src/`, and the generated deployable output is written to `dist/`. |
13 | 11 |
|
14 | | -## Build And Run |
15 | | - |
16 | | -Install dependencies: |
17 | | - |
18 | | -```bash |
19 | | -npm install |
20 | | -``` |
21 | | - |
22 | | -Build the site: |
23 | | - |
24 | | -```bash |
25 | | -npm run build |
26 | | -``` |
27 | | - |
28 | | -Sync the FT RSS feed into the local stories YAML source: |
29 | | - |
30 | | -```bash |
31 | | -npm run sync:stories |
32 | | -``` |
33 | | - |
34 | | -Run a local Eleventy dev server: |
35 | | - |
36 | | -```bash |
37 | | -npm run dev |
38 | | -``` |
39 | | - |
40 | | -The current build target is `dist/`. The build script cleans `dist/` before each build so removed routes do not leave stale files behind. |
41 | | - |
42 | | -## URL Parity Check |
43 | | - |
44 | | -The repository includes a repeatable parity check for the deployment contract defined in the migration spec: any internal `martinstabe.com` URL reachable by recursively following links from `https://www.martinstabe.com/blog/` must also exist in the new build output. |
45 | | - |
46 | | -Build the site first: |
47 | | - |
48 | | -```bash |
49 | | -npm run build |
50 | | -``` |
51 | | - |
52 | | -Run the full parity workflow: |
53 | | - |
54 | | -```bash |
55 | | -npm run parity:blog |
56 | | -``` |
57 | | - |
58 | | -This writes: |
59 | | - |
60 | | -- `data/url_parity_live_blog.json`: crawl inventory from the live site |
61 | | -- `data/url_parity_dist.json`: local route inventory from `dist/` |
62 | | -- `data/url_parity_report.json`: parity diff report |
63 | | - |
64 | | -The full parity command exits non-zero if the live crawl finds internal paths that do not exist in `dist/`. |
65 | | - |
66 | | -You can also run the stages individually: |
67 | | - |
68 | | -```bash |
69 | | -npm run parity:blog:live |
70 | | -npm run parity:blog:dist |
71 | | -npm run parity:blog:check |
72 | | -``` |
73 | | - |
74 | | -For bounded smoke tests while iterating on the crawler, you can cap the live crawl: |
75 | | - |
76 | | -```bash |
77 | | -node scripts/crawl_live_blog.js --limit 200 |
78 | | -``` |
79 | | - |
80 | | -## Deployment |
81 | | - |
82 | | -This implementation is intended for Cloudflare Pages. |
83 | | - |
84 | | -- build command: `npm run build` |
85 | | -- output directory: `dist` |
86 | | - |
87 | | -The repository also includes GitHub Pages safeguards: |
88 | | - |
89 | | -- [`.nojekyll`](.nojekyll) at the repo root to bypass branch-based Jekyll builds |
90 | | -- [`.github/workflows/pages.yml`](.github/workflows/pages.yml) to build with Node and deploy `dist/` directly via GitHub Actions |
91 | | - |
92 | | -If GitHub Pages is still enabled for the repository, it should be switched to GitHub Actions rather than branch-based publishing. |
93 | | - |
94 | | -## Implementation Structure |
95 | | - |
96 | | -### Eleventy App |
97 | | - |
98 | | -- [eleventy.config.js](eleventy.config.js) |
99 | | -- [package.json](package.json) |
100 | | -- [src](src) |
101 | | - |
102 | | -The Eleventy config: |
103 | | - |
104 | | -- uses `src/` as the input directory |
105 | | -- uses Nunjucks for layouts and templates |
106 | | -- outputs to `dist/` |
107 | | -- passthrough-copies `img/`, `slides/`, `CNAME`, and the Google verification file |
108 | | -- adds custom filters for relative URLs, human-readable dates, RFC 822 dates, and XML escaping |
109 | | - |
110 | | -### Layouts And Partials |
111 | | - |
112 | | -Shared templates are in: |
113 | | - |
114 | | -- [src/_includes/layouts/default.njk](src/_includes/layouts/default.njk) |
115 | | -- [src/_includes/layouts/page.njk](src/_includes/layouts/page.njk) |
116 | | -- [src/_includes/partials](src/_includes/partials) |
117 | | - |
118 | | -These replicate the old Jekyll page shell, header, footer, analytics, social, comments, and head metadata in Eleventy form. |
119 | | - |
120 | | -### Content Sources |
121 | | - |
122 | | -The Eleventy build does not duplicate the archive into `src/`. Instead it imports the existing source content: |
123 | | - |
124 | | -- blog posts from [_posts](_posts) |
125 | | -- tag metadata from [tags](tags) |
126 | | -- links archive data from [_data/links.yml](_data/links.yml) |
127 | | - |
128 | | -## Additional Features Added During Migration |
129 | | - |
130 | | -The Eleventy implementation includes behavior that did not previously exist as a single coherent layer in the Jekyll site. |
131 | | - |
132 | | -### Stories Section From FT RSS |
133 | | - |
134 | | -Stories sourced from the FT RSS feed can now be synced into [_data/stories.yml](_data/stories.yml) with [scripts/sync_stories.js](scripts/sync_stories.js). |
135 | | - |
136 | | -Each synced entry is stored as YAML and preserves manual overrides on subsequent syncs, including the `include: false` switch to exclude an item from the generated site. |
137 | | - |
138 | | -Included stories are rendered into the new `/stories/` section and linked from the main navigation. |
139 | | - |
140 | | -#### Updating Stories |
141 | | - |
142 | | -Source: |
143 | | - |
144 | | -- RSS feed: `https://www.ft.com/martin-stabe?format=rss` |
145 | | -- local cache: [_data/stories.yml](_data/stories.yml) |
146 | | -- sync script: [scripts/sync_stories.js](scripts/sync_stories.js) |
147 | | - |
148 | | -To refresh the Stories section: |
149 | | - |
150 | | -```bash |
151 | | -npm run sync:stories |
152 | | -npm run build |
153 | | -``` |
154 | | - |
155 | | -Notes: |
156 | | - |
157 | | -- the sync merges by feed `guid` |
158 | | -- new feed items default to `include: true` |
159 | | -- setting `include: false` on any item suppresses it from `/stories/` |
160 | | -- manual edits in `_data/stories.yml` can be used to override display text or links before rebuilding |
161 | | -- the generated Stories page links outward to the canonical FT URLs |
162 | | - |
163 | | -### Graphics Section From FT Asset Database |
164 | | - |
165 | | -Graphics sourced from the FT elections newsbox asset database can now be synced into [_data/graphics.yml](_data/graphics.yml) with [scripts/sync_graphics.js](scripts/sync_graphics.js) and [scripts/export_graphics.R](scripts/export_graphics.R). |
166 | | - |
167 | | -The sync filters records to `flourish_author == "Martin Stabe"`, transforms the fields required for the site, preserves manual `include: false` overrides, and renders included entries into the `/data-visualisation/` section as outbound links to FT.com. |
168 | | - |
169 | | -#### Updating Data Visualisation |
170 | | - |
171 | | -Source: |
172 | | - |
173 | | -- R data file: set `GRAPHICS_SOURCE_URL` in your environment before running the sync |
174 | | -- local cache: [_data/graphics.yml](_data/graphics.yml) |
175 | | -- sync scripts: [scripts/sync_graphics.js](scripts/sync_graphics.js) and [scripts/export_graphics.R](scripts/export_graphics.R) |
176 | | - |
177 | | -To refresh the Data Visualisation section: |
178 | | - |
179 | | -```bash |
180 | | -npm run sync:graphics |
181 | | -npm run build |
182 | | -``` |
183 | | - |
184 | | -The sync currently: |
185 | | - |
186 | | -- downloads the remote `.rds` file |
187 | | -- filters rows to `flourish_author == "Martin Stabe"` |
188 | | -- transforms the selected fields into YAML entries |
189 | | -- stores the results in `_data/graphics.yml` |
190 | | -- preserves manual `include: false` overrides on existing items |
191 | | - |
192 | | -### Tag Deduplication And Alias Merging |
193 | | - |
194 | | -Multiple tag tokens can now map to the same rendered tag page simply by pointing their legacy `tags/*.md` files at the same permalink. |
195 | | - |
196 | | -Example: |
197 | | - |
198 | | -- `ft` |
199 | | -- `financialtimes` |
200 | | -- `financial-times` |
201 | | - |
202 | | -This affects: |
203 | | - |
204 | | -- the generated tag index |
205 | | -- individual tag pages |
206 | | -- the tag footer on each post |
207 | | -- inline legacy tag links embedded inside imported HTML posts |
208 | | - |
209 | | -### Canonical Post Footer Tags |
210 | | - |
211 | | -Post pages are generated through [src/posts.njk](src/posts.njk) using metadata prepared in [src/_data/posts.js](src/_data/posts.js). |
212 | | - |
213 | | -The footer tag list now uses canonical tag definitions rather than raw front matter values, so a post tagged `ft` can render as `Financial Times` and link to the canonical merged tag page. |
214 | | - |
215 | | -### Legacy Inline Tag Link Rewriting |
216 | | - |
217 | | -Some older imported posts, especially the `links-for-*` archive entries, contain literal HTML with embedded `/tags/.../` links in the body. |
218 | | - |
219 | | -Those links are now rewritten during the Eleventy import step so they resolve to the canonical tag permalink rather than an obsolete or duplicate path. |
220 | | - |
221 | | -### Special Handling For Delicious Archive Posts |
222 | | - |
223 | | -Many `links-for-*` posts are already HTML rather than Markdown. The importer in [src/_data/posts.js](src/_data/posts.js) detects those entries and bypasses Markdown rendering so they do not get mangled into escaped code blocks. |
224 | | - |
225 | | -### Relative Internal Links For Local Review |
226 | | - |
227 | | -The Eleventy build uses a custom `relativeUrl` filter so internal links are written relative to the current page. This makes manual inspection of the generated `dist/` output work correctly when served from a local static server. |
228 | | - |
229 | | -Canonical and feed URLs remain absolute where appropriate. |
230 | | - |
231 | | -### RSS Feed Recreation |
232 | | - |
233 | | -The RSS feed is generated by [src/feed.njk](src/feed.njk), using Eleventy data and custom XML/date filters to approximate the old Jekyll feed behavior. |
234 | | - |
235 | | -### Links Archive Rebuilt From Data |
236 | | - |
237 | | -The `/links/` page is now generated from [_data/links.yml](_data/links.yml) through [src/_data/links.js](src/_data/links.js) and [src/links/index.njk](src/links/index.njk), rather than relying on Jekyll template iteration. |
238 | | - |
239 | | -### Jekyll Sass Reused In Node Build |
240 | | - |
241 | | -The original Jekyll Sass has been retained and compiled via Node: |
242 | | - |
243 | | -- [scripts/build_sass.js](scripts/build_sass.js) |
244 | | -- [css/main.scss](css/main.scss) |
245 | | -- [_sass](_sass) |
246 | | - |
247 | | -This preserves the historical styling rather than replacing it with a new CSS layer. |
248 | | - |
249 | | -### Clean Build Output |
250 | | - |
251 | | -The build now removes `dist/` first using [scripts/clean_dist.js](scripts/clean_dist.js). This prevents stale generated pages from surviving route changes, which is especially important for merged tag aliases. |
252 | | - |
253 | | -### Footer Social Update |
254 | | - |
255 | | -The Eleventy footer partial at [src/_includes/partials/footer.njk](src/_includes/partials/footer.njk) has been updated to point to Bluesky instead of the legacy Twitter profile, including a new icon and layout adjustments to accommodate the longer handle. |
256 | | - |
257 | | -## Current Page Coverage |
258 | | - |
259 | | -The Eleventy build currently generates: |
260 | | - |
261 | | -- home page |
262 | | -- blog index |
263 | | -- individual post pages |
264 | | -- tag index |
265 | | -- tag detail pages |
266 | | -- links archive |
267 | | -- about page |
268 | | -- RSS feed |
269 | | - |
270 | | -Static assets are passed through for: |
271 | | - |
272 | | -- `img/` |
273 | | -- `slides/` |
274 | | -- `CNAME` |
275 | | -- `googlea1fed2510060dd96.html` |
276 | | - |
277 | | -## Notes |
278 | | - |
279 | | -- The original Jekyll site remains in the repository for reference during migration. |
280 | | -- The generated `dist/` directory is a build artifact and should not be edited directly. |
281 | | -- The Sass pipeline currently emits deprecation warnings from the legacy stylesheet source, but the build completes successfully. |
0 commit comments