[{"data":1,"prerenderedAt":2344},["ShallowReactive",2],{"page:\u002Fchoosing-the-right-static-site-generator-for-production\u002Fmigrating-between-static-site-generators\u002Fconverting-front-matter-at-scale-during-migration":3,"all-docs-nav":1801},{"id":4,"title":5,"body":6,"breadcrumb":1774,"dateModified":1784,"datePublished":1784,"description":1785,"extension":1786,"faq":1787,"meta":1794,"navigation":1795,"path":1796,"seo":1797,"slug":12,"stem":1798,"type":1799,"__hash__":1800},"content\u002Fchoosing-the-right-static-site-generator-for-production\u002Fmigrating-between-static-site-generators\u002Fconverting-front-matter-at-scale-during-migration\u002Findex.md","Converting Front Matter at Scale During Migration",{"type":7,"value":8,"toc":1756},"minimark",[9,13,38,52,57,88,92,95,452,460,474,610,614,617,863,878,1171,1175,1178,1314,1317,1447,1451,1454,1460,1467,1471,1491,1494,1552,1556,1622,1626,1672,1676,1679,1683,1688,1691,1695,1698,1702,1705,1709,1712,1716,1752],[10,11,5],"h1",{"id":12},"converting-front-matter-at-scale-during-migration",[14,15,16,17,21,22,25,26,29,30,33,34,37],"p",{},"Moving content between static site generators is mostly moving Markdown — and the Markdown body usually moves unchanged. What changes is the front matter: Hugo's ",[18,19,20],"code",{},"weight"," becomes Astro's ",[18,23,24],{},"order",", Jekyll's ",[18,27,28],{},"permalink"," becomes a route, Docusaurus's ",[18,31,32],{},"sidebar_position"," becomes Starlight's ",[18,35,36],{},"sidebar.order",", dates written three different ways over five years need to be one type, and a dozen fields nobody remembers adding need a decision. On a few pages you edit by hand. On 3,000 pages, hand edits are slow, inconsistent and impossible to review.",[14,39,40,41,46,47,51],{},"This guide converts the front matter of 3,140 Hugo pages to an Astro content collection with a script: a declarative field map, type normalisation, validation against the destination schema, and a summary that makes the change reviewable. It is part of ",[42,43,45],"a",{"href":44},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fmigrating-between-static-site-generators\u002F","Migrating Between Static Site Generators"," and was used in ",[42,48,50],{"href":49},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fmigrating-between-static-site-generators\u002Fmigrating-from-hugo-to-astro-without-breaking-urls\u002F","Migrating from Hugo to Astro Without Breaking URLs",".",[53,54,56],"h2",{"id":55},"prerequisites","Prerequisites",[58,59,60,64,74],"ul",{},[61,62,63],"li",{},"The source content in Git, so every change is a diff.",[61,65,66,67,70,71,51],{},"The destination schema written down — for Astro, the ",[18,68,69],{},"z.object"," in ",[18,72,73],{},"content.config.ts",[61,75,76,77,80,81,84,85,51],{},"Node.js with the ",[18,78,79],{},"yaml"," and ",[18,82,83],{},"gray-matter"," packages, or Python with ",[18,86,87],{},"ruamel.yaml",[53,89,91],{"id":90},"step-1-inventory-the-fields","Step 1: Inventory the Fields",[14,93,94],{},"Before writing a map, list every key actually used, its frequency and the types it appears as. Real content is always messier than the documented schema:",[96,97,102],"pre",{"className":98,"code":99,"language":100,"meta":101,"style":101},"language-js shiki shiki-themes github-light github-dark","\u002F\u002F scripts\u002Ffm-inventory.mjs\nimport matter from 'gray-matter';\nimport { globSync, readFileSync } from 'node:fs';\nconst stats = {};\nfor (const f of globSync('content\u002F**\u002F*.md')) {\n  const { data } = matter(readFileSync(f, 'utf8'));\n  for (const [k, v] of Object.entries(data)) {\n    const t = Array.isArray(v) ? 'array' : v instanceof Date ? 'date' : typeof v;\n    (stats[k] ??= {})[t] = (stats[k][t] ?? 0) + 1;\n  }\n}\nconsole.table(Object.entries(stats).sort((a, b) => sum(b[1]) - sum(a[1])).map(([k, t]) => ({ key: k, ...t })));\n","js","",[18,103,104,113,134,149,165,195,230,267,319,353,359,365],{"__ignoreMap":101},[105,106,109],"span",{"class":107,"line":108},"line",1,[105,110,112],{"class":111},"sJ8bj","\u002F\u002F scripts\u002Ffm-inventory.mjs\n",[105,114,116,120,124,127,131],{"class":107,"line":115},2,[105,117,119],{"class":118},"szBVR","import",[105,121,123],{"class":122},"sVt8B"," matter ",[105,125,126],{"class":118},"from",[105,128,130],{"class":129},"sZZnC"," 'gray-matter'",[105,132,133],{"class":122},";\n",[105,135,137,139,142,144,147],{"class":107,"line":136},3,[105,138,119],{"class":118},[105,140,141],{"class":122}," { globSync, readFileSync } ",[105,143,126],{"class":118},[105,145,146],{"class":129}," 'node:fs'",[105,148,133],{"class":122},[105,150,152,155,159,162],{"class":107,"line":151},4,[105,153,154],{"class":118},"const",[105,156,158],{"class":157},"sj4cs"," stats",[105,160,161],{"class":118}," =",[105,163,164],{"class":122}," {};\n",[105,166,168,171,174,176,179,182,186,189,192],{"class":107,"line":167},5,[105,169,170],{"class":118},"for",[105,172,173],{"class":122}," (",[105,175,154],{"class":118},[105,177,178],{"class":157}," f",[105,180,181],{"class":118}," of",[105,183,185],{"class":184},"sScJk"," globSync",[105,187,188],{"class":122},"(",[105,190,191],{"class":129},"'content\u002F**\u002F*.md'",[105,193,194],{"class":122},")) {\n",[105,196,198,201,204,207,210,213,216,218,221,224,227],{"class":107,"line":197},6,[105,199,200],{"class":118},"  const",[105,202,203],{"class":122}," { ",[105,205,206],{"class":157},"data",[105,208,209],{"class":122}," } ",[105,211,212],{"class":118},"=",[105,214,215],{"class":184}," matter",[105,217,188],{"class":122},[105,219,220],{"class":184},"readFileSync",[105,222,223],{"class":122},"(f, ",[105,225,226],{"class":129},"'utf8'",[105,228,229],{"class":122},"));\n",[105,231,233,236,238,240,243,246,249,252,255,258,261,264],{"class":107,"line":232},7,[105,234,235],{"class":118},"  for",[105,237,173],{"class":122},[105,239,154],{"class":118},[105,241,242],{"class":122}," [",[105,244,245],{"class":157},"k",[105,247,248],{"class":122},", ",[105,250,251],{"class":157},"v",[105,253,254],{"class":122},"] ",[105,256,257],{"class":118},"of",[105,259,260],{"class":122}," Object.",[105,262,263],{"class":184},"entries",[105,265,266],{"class":122},"(data)) {\n",[105,268,270,273,276,278,281,284,287,290,293,296,299,302,305,308,311,313,316],{"class":107,"line":269},8,[105,271,272],{"class":118},"    const",[105,274,275],{"class":157}," t",[105,277,161],{"class":118},[105,279,280],{"class":122}," Array.",[105,282,283],{"class":184},"isArray",[105,285,286],{"class":122},"(v) ",[105,288,289],{"class":118},"?",[105,291,292],{"class":129}," 'array'",[105,294,295],{"class":118}," :",[105,297,298],{"class":122}," v ",[105,300,301],{"class":118},"instanceof",[105,303,304],{"class":184}," Date",[105,306,307],{"class":118}," ?",[105,309,310],{"class":129}," 'date'",[105,312,295],{"class":118},[105,314,315],{"class":118}," typeof",[105,317,318],{"class":122}," v;\n",[105,320,322,325,328,331,333,336,339,342,345,348,351],{"class":107,"line":321},9,[105,323,324],{"class":122},"    (stats[k] ",[105,326,327],{"class":118},"??=",[105,329,330],{"class":122}," {})[t] ",[105,332,212],{"class":118},[105,334,335],{"class":122}," (stats[k][t] ",[105,337,338],{"class":118},"??",[105,340,341],{"class":157}," 0",[105,343,344],{"class":122},") ",[105,346,347],{"class":118},"+",[105,349,350],{"class":157}," 1",[105,352,133],{"class":122},[105,354,356],{"class":107,"line":355},10,[105,357,358],{"class":122},"  }\n",[105,360,362],{"class":107,"line":361},11,[105,363,364],{"class":122},"}\n",[105,366,368,371,374,377,379,382,385,388,391,393,396,398,401,404,407,410,413,416,418,421,423,426,429,432,434,436,439,441,443,446,449],{"class":107,"line":367},12,[105,369,370],{"class":122},"console.",[105,372,373],{"class":184},"table",[105,375,376],{"class":122},"(Object.",[105,378,263],{"class":184},[105,380,381],{"class":122},"(stats).",[105,383,384],{"class":184},"sort",[105,386,387],{"class":122},"((",[105,389,42],{"class":390},"s4XuR",[105,392,248],{"class":122},[105,394,395],{"class":390},"b",[105,397,344],{"class":122},[105,399,400],{"class":118},"=>",[105,402,403],{"class":184}," sum",[105,405,406],{"class":122},"(b[",[105,408,409],{"class":157},"1",[105,411,412],{"class":122},"]) ",[105,414,415],{"class":118},"-",[105,417,403],{"class":184},[105,419,420],{"class":122},"(a[",[105,422,409],{"class":157},[105,424,425],{"class":122},"])).",[105,427,428],{"class":184},"map",[105,430,431],{"class":122},"(([",[105,433,245],{"class":390},[105,435,248],{"class":122},[105,437,438],{"class":390},"t",[105,440,412],{"class":122},[105,442,400],{"class":118},[105,444,445],{"class":122}," ({ key: k, ",[105,447,448],{"class":118},"...",[105,450,451],{"class":122},"t })));\n",[96,453,458],{"className":454,"code":456,"language":457,"meta":101},[455],"language-text","key              string  date  number  boolean  array  object\ntitle              3140\ndate                 94  3046\nweight                           2211      18\ndraft                                     412\ntags                                                  1860\naliases                                               1203\ndescription        2804\ntoc                                       960\nauthor              410                            88\nlastmod              12  1510\n_build                                                         6\n","text",[18,459,456],{"__ignoreMap":101},[14,461,462,463,465,466,469,470,473],{},"Three problems surfaced immediately: 94 dates stored as strings in five different formats, ",[18,464,20],{}," sometimes a boolean (a template hack from 2021), and ",[18,467,468],{},"author"," both a string and an array. Six pages used Hugo's ",[18,471,472],{},"_build"," options, which have no Astro equivalent.",[475,476,477,606],"figure",{},[478,479,486,487,486,491,486,495,486,502],"svg",{"viewBox":480,"role":481,"ariaLabelledBy":482,"xmlns":485},"0 0 760 290","img",[483,484],"fmc-inv-title","fmc-inv-desc","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","\n  ",[488,489,490],"title",{"id":483},"Front matter keys and their type inconsistencies",[492,493,494],"desc",{"id":484},"Bars for the most common keys across 3,140 files, each split by type. title is always a string. date is mostly a date with 94 strings. weight is mostly a number with 18 booleans. author is 410 strings and 88 arrays. lastmod is mostly a date with 12 strings. The mixed types are highlighted as conversion work.",[496,497],"rect",{"x":498,"y":498,"width":499,"height":500,"fill":501},"0","760","290","#ffffff",[503,504,506,507,506,514,506,521,506,525,506,535,506,539,506,543,506,550,506,552,506,556,506,561,506,565,506,569,506,573,506,576,506,583,506,587,506,590,506,594,506,600,486],"g",{"style":505},"font-family:system-ui, sans-serif;font-size:12px","\n    ",[457,508,513],{"x":509,"y":510,"fill":511,"style":512},"380","28","#1f2937","font-size:16px;font-weight:700;text-anchor:middle","Keys by type across 3,140 files (red = inconsistent)",[107,515],{"x1":516,"y1":517,"x2":516,"y2":518,"stroke":519,"style":520},"140","46","246","#d9e2ef","stroke-width:1.5px",[457,522,488],{"x":523,"y":524,"fill":511},"40","72",[496,526],{"x":516,"y":527,"width":528,"height":529,"rx":530,"fill":531,"opacity":532,"stroke":533,"style":534},"58","560","20","3","#8ac926","0.35","#5a8a16","stroke-width:1px",[457,536,538],{"x":523,"y":537,"fill":511},"106","date",[496,540],{"x":516,"y":541,"width":542,"height":529,"rx":530,"fill":531,"opacity":532,"stroke":533,"style":534},"92","543",[496,544],{"x":545,"y":541,"width":546,"height":529,"rx":530,"fill":547,"opacity":548,"stroke":549,"style":534},"683","17","#ff595e","0.5","#d83b41",[457,551,20],{"x":523,"y":516,"fill":511},[496,553],{"x":516,"y":554,"width":555,"height":529,"rx":530,"fill":531,"opacity":532,"stroke":533,"style":534},"126","394",[496,557],{"x":558,"y":554,"width":559,"height":529,"rx":560,"fill":547,"opacity":548,"stroke":549,"style":534},"534","6","2",[457,562,564],{"x":523,"y":563,"fill":511},"174","lastmod",[496,566],{"x":516,"y":567,"width":568,"height":529,"rx":530,"fill":531,"opacity":532,"stroke":533,"style":534},"160","269",[496,570],{"x":571,"y":567,"width":572,"height":529,"rx":409,"fill":547,"opacity":548,"stroke":549,"style":534},"409","4",[457,574,468],{"x":523,"y":575,"fill":511},"208",[496,577],{"x":516,"y":578,"width":579,"height":529,"rx":530,"fill":580,"opacity":581,"stroke":582,"style":534},"194","73","#ffca3a","0.45","#a97b00",[496,584],{"x":585,"y":578,"width":586,"height":529,"rx":530,"fill":547,"opacity":548,"stroke":549,"style":534},"213","16",[457,588,472],{"x":523,"y":589,"fill":511},"242",[496,591],{"x":516,"y":592,"width":560,"height":529,"fill":547,"opacity":593,"stroke":549,"style":534},"228","0.6",[457,595,599],{"x":596,"y":589,"fill":597,"style":598},"152","#b32b30","font-size:11px;font-weight:700","6 with no equivalent",[457,601,605],{"x":509,"y":602,"fill":603,"style":604},"272","#556071","font-size:11px;text-anchor:middle","The small red segments are where hand-written rules and manual review go",[607,608,609],"figcaption",{},"Inconsistencies are small in count and large in effort — the inventory finds them before they become build errors.",[53,611,613],{"id":612},"step-2-write-a-declarative-field-map","Step 2: Write a Declarative Field Map",[14,615,616],{},"Express the conversion as data, so it can be reviewed like configuration rather than read as code:",[96,618,620],{"className":98,"code":619,"language":100,"meta":101,"style":101},"\u002F\u002F scripts\u002Ffm-map.mjs\nexport const MAP = {\n  title:       { to: 'title' },\n  description: { to: 'description' },\n  date:        { to: 'pubDate', type: 'date' },\n  lastmod:     { to: 'updatedDate', type: 'date' },\n  weight:      { to: 'order', type: 'int', ifInvalid: 'drop' },\n  draft:       { to: 'draft', type: 'bool' },\n  tags:        { to: 'tags', type: 'string[]', transform: (v) => v.map((t) => t.toLowerCase().trim()) },\n  author:      { to: 'authors', type: 'string[]' },          \u002F\u002F string → [string]\n  aliases:     { to: null, collect: 'redirects' },           \u002F\u002F moved out of front matter\n  toc:         { to: 'tableOfContents', type: 'bool' },\n  _build:      { to: null, manual: true },                   \u002F\u002F flag for a human\n};\n",[18,621,622,627,643,654,664,680,694,715,730,785,803,823,837,857],{"__ignoreMap":101},[105,623,624],{"class":107,"line":108},[105,625,626],{"class":111},"\u002F\u002F scripts\u002Ffm-map.mjs\n",[105,628,629,632,635,638,640],{"class":107,"line":115},[105,630,631],{"class":118},"export",[105,633,634],{"class":118}," const",[105,636,637],{"class":157}," MAP",[105,639,161],{"class":118},[105,641,642],{"class":122}," {\n",[105,644,645,648,651],{"class":107,"line":136},[105,646,647],{"class":122},"  title:       { to: ",[105,649,650],{"class":129},"'title'",[105,652,653],{"class":122}," },\n",[105,655,656,659,662],{"class":107,"line":151},[105,657,658],{"class":122},"  description: { to: ",[105,660,661],{"class":129},"'description'",[105,663,653],{"class":122},[105,665,666,669,672,675,678],{"class":107,"line":167},[105,667,668],{"class":122},"  date:        { to: ",[105,670,671],{"class":129},"'pubDate'",[105,673,674],{"class":122},", type: ",[105,676,677],{"class":129},"'date'",[105,679,653],{"class":122},[105,681,682,685,688,690,692],{"class":107,"line":197},[105,683,684],{"class":122},"  lastmod:     { to: ",[105,686,687],{"class":129},"'updatedDate'",[105,689,674],{"class":122},[105,691,677],{"class":129},[105,693,653],{"class":122},[105,695,696,699,702,704,707,710,713],{"class":107,"line":232},[105,697,698],{"class":122},"  weight:      { to: ",[105,700,701],{"class":129},"'order'",[105,703,674],{"class":122},[105,705,706],{"class":129},"'int'",[105,708,709],{"class":122},", ifInvalid: ",[105,711,712],{"class":129},"'drop'",[105,714,653],{"class":122},[105,716,717,720,723,725,728],{"class":107,"line":269},[105,718,719],{"class":122},"  draft:       { to: ",[105,721,722],{"class":129},"'draft'",[105,724,674],{"class":122},[105,726,727],{"class":129},"'bool'",[105,729,653],{"class":122},[105,731,732,735,738,740,743,745,748,751,753,755,757,760,762,764,766,768,770,773,776,779,782],{"class":107,"line":321},[105,733,734],{"class":122},"  tags:        { to: ",[105,736,737],{"class":129},"'tags'",[105,739,674],{"class":122},[105,741,742],{"class":129},"'string[]'",[105,744,248],{"class":122},[105,746,747],{"class":184},"transform",[105,749,750],{"class":122},": (",[105,752,251],{"class":390},[105,754,344],{"class":122},[105,756,400],{"class":118},[105,758,759],{"class":122}," v.",[105,761,428],{"class":184},[105,763,387],{"class":122},[105,765,438],{"class":390},[105,767,344],{"class":122},[105,769,400],{"class":118},[105,771,772],{"class":122}," t.",[105,774,775],{"class":184},"toLowerCase",[105,777,778],{"class":122},"().",[105,780,781],{"class":184},"trim",[105,783,784],{"class":122},"()) },\n",[105,786,787,790,793,795,797,800],{"class":107,"line":355},[105,788,789],{"class":122},"  author:      { to: ",[105,791,792],{"class":129},"'authors'",[105,794,674],{"class":122},[105,796,742],{"class":129},[105,798,799],{"class":122}," },          ",[105,801,802],{"class":111},"\u002F\u002F string → [string]\n",[105,804,805,808,811,814,817,820],{"class":107,"line":361},[105,806,807],{"class":122},"  aliases:     { to: ",[105,809,810],{"class":157},"null",[105,812,813],{"class":122},", collect: ",[105,815,816],{"class":129},"'redirects'",[105,818,819],{"class":122}," },           ",[105,821,822],{"class":111},"\u002F\u002F moved out of front matter\n",[105,824,825,828,831,833,835],{"class":107,"line":367},[105,826,827],{"class":122},"  toc:         { to: ",[105,829,830],{"class":129},"'tableOfContents'",[105,832,674],{"class":122},[105,834,727],{"class":129},[105,836,653],{"class":122},[105,838,840,843,845,848,851,854],{"class":107,"line":839},13,[105,841,842],{"class":122},"  _build:      { to: ",[105,844,810],{"class":157},[105,846,847],{"class":122},", manual: ",[105,849,850],{"class":157},"true",[105,852,853],{"class":122}," },                   ",[105,855,856],{"class":111},"\u002F\u002F flag for a human\n",[105,858,860],{"class":107,"line":859},14,[105,861,862],{"class":122},"};\n",[14,864,865,866,869,870,873,874,877],{},"The script applies the map, normalises types (five date formats parsed with explicit patterns, never ",[18,867,868],{},"new Date(string)"," guessing), collects ",[18,871,872],{},"aliases"," into a redirect file rather than front matter, and flags anything marked ",[18,875,876],{},"manual"," or not in the map at all.",[96,879,881],{"className":98,"code":880,"language":100,"meta":101,"style":101},"\u002F\u002F scripts\u002Ffm-convert.mjs (core)\nconst out = {}, notes = [];\nfor (const [k, v] of Object.entries(data)) {\n  const rule = MAP[k];\n  if (!rule) { notes.push(`unknown key ${k}`); continue; }\n  if (rule.manual) { notes.push(`manual: ${k}`); continue; }\n  if (rule.collect) { collected[rule.collect].push(...[].concat(v).map((from) => ({ from, to: url }))); continue; }\n  const val = coerce(v, rule.type);\n  if (val === INVALID) { if (rule.ifInvalid === 'drop') { notes.push(`dropped ${k}=${v}`); continue; } throw new Error(`${file}: ${k}`); }\n  out[rule.to] = rule.transform ? rule.transform(val) : val;\n}\n",[18,882,883,888,908,934,948,983,1007,1046,1061,1141,1167],{"__ignoreMap":101},[105,884,885],{"class":107,"line":108},[105,886,887],{"class":111},"\u002F\u002F scripts\u002Ffm-convert.mjs (core)\n",[105,889,890,892,895,897,900,903,905],{"class":107,"line":115},[105,891,154],{"class":118},[105,893,894],{"class":157}," out",[105,896,161],{"class":118},[105,898,899],{"class":122}," {}, ",[105,901,902],{"class":157},"notes",[105,904,161],{"class":118},[105,906,907],{"class":122}," [];\n",[105,909,910,912,914,916,918,920,922,924,926,928,930,932],{"class":107,"line":136},[105,911,170],{"class":118},[105,913,173],{"class":122},[105,915,154],{"class":118},[105,917,242],{"class":122},[105,919,245],{"class":157},[105,921,248],{"class":122},[105,923,251],{"class":157},[105,925,254],{"class":122},[105,927,257],{"class":118},[105,929,260],{"class":122},[105,931,263],{"class":184},[105,933,266],{"class":122},[105,935,936,938,941,943,945],{"class":107,"line":151},[105,937,200],{"class":118},[105,939,940],{"class":157}," rule",[105,942,161],{"class":118},[105,944,637],{"class":157},[105,946,947],{"class":122},"[k];\n",[105,949,950,953,955,958,961,964,966,969,971,974,977,980],{"class":107,"line":167},[105,951,952],{"class":118},"  if",[105,954,173],{"class":122},[105,956,957],{"class":118},"!",[105,959,960],{"class":122},"rule) { notes.",[105,962,963],{"class":184},"push",[105,965,188],{"class":122},[105,967,968],{"class":129},"`unknown key ${",[105,970,245],{"class":122},[105,972,973],{"class":129},"}`",[105,975,976],{"class":122},"); ",[105,978,979],{"class":118},"continue",[105,981,982],{"class":122},"; }\n",[105,984,985,987,990,992,994,997,999,1001,1003,1005],{"class":107,"line":197},[105,986,952],{"class":118},[105,988,989],{"class":122}," (rule.manual) { notes.",[105,991,963],{"class":184},[105,993,188],{"class":122},[105,995,996],{"class":129},"`manual: ${",[105,998,245],{"class":122},[105,1000,973],{"class":129},[105,1002,976],{"class":122},[105,1004,979],{"class":118},[105,1006,982],{"class":122},[105,1008,1009,1011,1014,1016,1018,1020,1023,1026,1029,1031,1033,1035,1037,1039,1042,1044],{"class":107,"line":232},[105,1010,952],{"class":118},[105,1012,1013],{"class":122}," (rule.collect) { collected[rule.collect].",[105,1015,963],{"class":184},[105,1017,188],{"class":122},[105,1019,448],{"class":118},[105,1021,1022],{"class":122},"[].",[105,1024,1025],{"class":184},"concat",[105,1027,1028],{"class":122},"(v).",[105,1030,428],{"class":184},[105,1032,387],{"class":122},[105,1034,126],{"class":390},[105,1036,344],{"class":122},[105,1038,400],{"class":118},[105,1040,1041],{"class":122}," ({ from, to: url }))); ",[105,1043,979],{"class":118},[105,1045,982],{"class":122},[105,1047,1048,1050,1053,1055,1058],{"class":107,"line":269},[105,1049,200],{"class":118},[105,1051,1052],{"class":157}," val",[105,1054,161],{"class":118},[105,1056,1057],{"class":184}," coerce",[105,1059,1060],{"class":122},"(v, rule.type);\n",[105,1062,1063,1065,1068,1071,1074,1077,1080,1083,1085,1088,1091,1093,1095,1098,1100,1103,1105,1107,1109,1111,1114,1117,1120,1123,1125,1128,1131,1134,1136,1138],{"class":107,"line":321},[105,1064,952],{"class":118},[105,1066,1067],{"class":122}," (val ",[105,1069,1070],{"class":118},"===",[105,1072,1073],{"class":157}," INVALID",[105,1075,1076],{"class":122},") { ",[105,1078,1079],{"class":118},"if",[105,1081,1082],{"class":122}," (rule.ifInvalid ",[105,1084,1070],{"class":118},[105,1086,1087],{"class":129}," 'drop'",[105,1089,1090],{"class":122},") { notes.",[105,1092,963],{"class":184},[105,1094,188],{"class":122},[105,1096,1097],{"class":129},"`dropped ${",[105,1099,245],{"class":122},[105,1101,1102],{"class":129},"}=${",[105,1104,251],{"class":122},[105,1106,973],{"class":129},[105,1108,976],{"class":122},[105,1110,979],{"class":118},[105,1112,1113],{"class":122},"; } ",[105,1115,1116],{"class":118},"throw",[105,1118,1119],{"class":118}," new",[105,1121,1122],{"class":184}," Error",[105,1124,188],{"class":122},[105,1126,1127],{"class":129},"`${",[105,1129,1130],{"class":122},"file",[105,1132,1133],{"class":129},"}: ${",[105,1135,245],{"class":122},[105,1137,973],{"class":129},[105,1139,1140],{"class":122},"); }\n",[105,1142,1143,1146,1148,1151,1153,1156,1158,1161,1164],{"class":107,"line":355},[105,1144,1145],{"class":122},"  out[rule.to] ",[105,1147,212],{"class":118},[105,1149,1150],{"class":122}," rule.transform ",[105,1152,289],{"class":118},[105,1154,1155],{"class":122}," rule.",[105,1157,747],{"class":184},[105,1159,1160],{"class":122},"(val) ",[105,1162,1163],{"class":118},":",[105,1165,1166],{"class":122}," val;\n",[105,1168,1169],{"class":107,"line":361},[105,1170,364],{"class":122},[53,1172,1174],{"id":1173},"step-3-validate-against-the-destination-schema","Step 3: Validate Against the Destination Schema",[14,1176,1177],{},"The destination generator already defines what valid front matter is. Reuse it rather than re-implementing checks:",[96,1179,1183],{"className":1180,"code":1181,"language":1182,"meta":101,"style":101},"language-ts shiki shiki-themes github-light github-dark","\u002F\u002F scripts\u002Ffm-validate.ts — import the same Zod schema the Astro collection uses\nimport { docsSchema } from '..\u002Fsrc\u002Fschema';\nfor (const [file, fm] of converted) {\n  const r = docsSchema.safeParse(fm);\n  if (!r.success) errors.push({ file, issues: r.error.issues.map((i) => `${i.path.join('.')}: ${i.message}`) });\n}\n","ts",[18,1184,1185,1190,1204,1228,1246,1310],{"__ignoreMap":101},[105,1186,1187],{"class":107,"line":108},[105,1188,1189],{"class":111},"\u002F\u002F scripts\u002Ffm-validate.ts — import the same Zod schema the Astro collection uses\n",[105,1191,1192,1194,1197,1199,1202],{"class":107,"line":115},[105,1193,119],{"class":118},[105,1195,1196],{"class":122}," { docsSchema } ",[105,1198,126],{"class":118},[105,1200,1201],{"class":129}," '..\u002Fsrc\u002Fschema'",[105,1203,133],{"class":122},[105,1205,1206,1208,1210,1212,1214,1216,1218,1221,1223,1225],{"class":107,"line":136},[105,1207,170],{"class":118},[105,1209,173],{"class":122},[105,1211,154],{"class":118},[105,1213,242],{"class":122},[105,1215,1130],{"class":157},[105,1217,248],{"class":122},[105,1219,1220],{"class":157},"fm",[105,1222,254],{"class":122},[105,1224,257],{"class":118},[105,1226,1227],{"class":122}," converted) {\n",[105,1229,1230,1232,1235,1237,1240,1243],{"class":107,"line":151},[105,1231,200],{"class":118},[105,1233,1234],{"class":157}," r",[105,1236,161],{"class":118},[105,1238,1239],{"class":122}," docsSchema.",[105,1241,1242],{"class":184},"safeParse",[105,1244,1245],{"class":122},"(fm);\n",[105,1247,1248,1250,1252,1254,1257,1259,1262,1264,1266,1269,1271,1273,1276,1278,1280,1283,1285,1288,1290,1293,1296,1298,1300,1302,1305,1307],{"class":107,"line":167},[105,1249,952],{"class":118},[105,1251,173],{"class":122},[105,1253,957],{"class":118},[105,1255,1256],{"class":122},"r.success) errors.",[105,1258,963],{"class":184},[105,1260,1261],{"class":122},"({ file, issues: r.error.issues.",[105,1263,428],{"class":184},[105,1265,387],{"class":122},[105,1267,1268],{"class":390},"i",[105,1270,344],{"class":122},[105,1272,400],{"class":118},[105,1274,1275],{"class":129}," `${",[105,1277,1268],{"class":122},[105,1279,51],{"class":129},[105,1281,1282],{"class":122},"path",[105,1284,51],{"class":129},[105,1286,1287],{"class":184},"join",[105,1289,188],{"class":129},[105,1291,1292],{"class":129},"'.'",[105,1294,1295],{"class":129},")",[105,1297,1133],{"class":129},[105,1299,1268],{"class":122},[105,1301,51],{"class":129},[105,1303,1304],{"class":122},"message",[105,1306,973],{"class":129},[105,1308,1309],{"class":122},") });\n",[105,1311,1312],{"class":107,"line":197},[105,1313,364],{"class":122},[14,1315,1316],{},"Validation against the real schema found 23 problems the map missed: descriptions over the 160-character limit the schema enforced, 7 duplicate slugs from files that differed only in case, and tags with characters the site's tag URLs could not represent. Each was fixed in the source content or by a new rule, and the script re-run.",[475,1318,1319,1444],{},[478,1320,486,1325,486,1328,486,1331,486,1334,486,1430],{"viewBox":1321,"role":481,"ariaLabelledBy":1322,"xmlns":485},"0 0 760 270",[1323,1324],"fmc-pipe-title","fmc-pipe-desc",[488,1326,1327],{"id":1323},"Conversion pipeline with validation and review outputs",[492,1329,1330],{"id":1324},"3,140 source files pass through parse, the declarative field map and type coercion. The output is validated with the destination Zod schema. The script produces converted files, a redirects file from collected aliases, and a summary report with counts per rule, 6 manual items and 23 validation errors to fix before re-running.",[496,1332],{"x":498,"y":498,"width":499,"height":1333,"fill":501},"270",[503,1335,506,1336,506,1339,506,1346,506,1352,506,1355,506,1361,506,1365,506,1368,506,1372,506,1376,506,1379,506,1386,506,1391,506,1395,506,1399,506,1402,506,1406,506,1427,486],{"style":505},[457,1337,1338],{"x":509,"y":510,"fill":511,"style":512},"Parse, map, coerce, validate — then review a summary",[496,1340],{"x":529,"y":1341,"width":1342,"height":1343,"rx":1344,"fill":1345,"stroke":519,"style":520},"96","120","60","10","#f8fafc",[457,1347,1351],{"x":1348,"y":1349,"fill":511,"style":1350},"80","122","font-weight:700;text-anchor:middle","3,140 .md",[457,1353,1354],{"x":1348,"y":516,"fill":603,"style":604},"Hugo front matter",[496,1356],{"x":1357,"y":1341,"width":1358,"height":1343,"rx":1344,"fill":1359,"opacity":1360,"stroke":1359,"style":520},"170","150","#6a4c93","0.14",[457,1362,1364],{"x":1363,"y":1349,"fill":511,"style":1350},"245","field map",[457,1366,1367],{"x":1363,"y":516,"fill":603,"style":604},"+ type coercion",[496,1369],{"x":1370,"y":1341,"width":516,"height":1343,"rx":1344,"fill":580,"opacity":1371,"stroke":582,"style":520},"350","0.24",[457,1373,1375],{"x":1374,"y":1349,"fill":511,"style":1350},"420","Zod schema",[457,1377,1378],{"x":1374,"y":516,"fill":603,"style":604},"same as the site",[496,1380],{"x":1381,"y":517,"width":1382,"height":1383,"rx":1384,"fill":531,"opacity":1385,"stroke":533,"style":520},"530","210","44","8","0.18",[457,1387,1390],{"x":1388,"y":579,"fill":511,"style":1389},"635","text-anchor:middle","converted files",[496,1392],{"x":1381,"y":1393,"width":1382,"height":1383,"rx":1384,"fill":1394,"opacity":1360,"stroke":1394,"style":520},"104","#1982c4",[457,1396,1398],{"x":1388,"y":1397,"fill":511,"style":1389},"131","redirects.json (1,203 aliases)",[496,1400],{"x":1381,"y":1401,"width":1382,"height":1383,"rx":1384,"fill":547,"opacity":1360,"stroke":549,"style":520},"162",[457,1403,1405],{"x":1388,"y":1404,"fill":511,"style":1389},"189","report: 6 manual · 23 errors",[503,1407,1410,1411,1410,1415,1410,1418,1410,1421,1410,1424,506],{"stroke":603,"fill":1408,"style":1409},"none","stroke-width:2px","\n      ",[1282,1412],{"d":1413,"style":1414},"M142 126 L166 126","marker-end:url(#fmc-arrow)",[1282,1416],{"d":1417,"style":1414},"M322 126 L346 126",[1282,1419],{"d":1420,"style":1414},"M492 116 L526 70",[1282,1422],{"d":1423,"style":1414},"M492 126 L526 126",[1282,1425],{"d":1426,"style":1414},"M492 136 L526 182",[457,1428,1429],{"x":509,"y":518,"fill":603,"style":604},"Re-run until the report shows no errors; the whole run takes 4 seconds",[1431,1432,506,1433,486],"defs",{},[1434,1435,1410,1441,506],"marker",{"id":1436,"viewBox":1437,"refX":1384,"refY":1438,"markerWidth":1439,"markerHeight":1439,"orient":1440},"fmc-arrow","0 0 10 10","5","7","auto-start-reverse",[1282,1442],{"d":1443,"fill":603},"M0 0 L10 5 L0 10 z",[607,1445,1446],{},"Because the script is fast and idempotent, fixing the source and re-running beats patching output by hand.",[53,1448,1450],{"id":1449},"step-4-make-the-change-reviewable","Step 4: Make the Change Reviewable",[14,1452,1453],{},"A pull request touching 3,140 files cannot be reviewed line by line. Review the map and the summary instead:",[96,1455,1458],{"className":1456,"code":1457,"language":457,"meta":101},[455],"fm-convert summary\n  files processed           3140\n  title→title               3140\n  date→pubDate              3140   (94 parsed from strings: 61 'YYYY-MM-DD', 22 'DD\u002FMM\u002FYYYY', 11 'Month D, YYYY')\n  weight→order              2193   (18 boolean weights dropped)\n  author→authors            498    (410 wrapped string→array)\n  aliases→redirects.json    1203 entries from 614 files\n  manual review             6      (_build)\n  unknown keys              0\n  validation errors         0\n",[18,1459,1457],{"__ignoreMap":101},[14,1461,1462,1463,51],{},"Then spot-check a random sample: 20 files from each transformation category, reading the before and after side by side. Keep the conversion script in the repository until the migration is complete, so a late source-content fix can be re-converted rather than hand-edited in two places. The same approach applies to other constructs covered in ",[42,1464,1466],{"href":1465},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fmigrating-between-static-site-generators\u002Fporting-shortcodes-and-includes-between-generators\u002F","Porting Shortcodes and Includes Between Generators",[53,1468,1470],{"id":1469},"handling-the-manual-cases","Handling the Manual Cases",[14,1472,1473,1474,1476,1477,1479,1480,1482,1483,1486,1487,1490],{},"Every conversion has a residue that rules should not try to cover. Here it was six pages with Hugo ",[18,1475,472],{}," options (pages built but not listed, or listed but not rendered) and a handful of pages whose ",[18,1478,20],{}," had been abused to force ordering across sections. Writing clever rules for six files costs more than handling them by hand, and hides decisions inside code. Instead, the script emitted a checklist file with one line per manual item — file, key, value, and a suggested action — and a person worked through it in twenty minutes. Four ",[18,1481,472],{}," pages became ",[18,1484,1485],{},"draft: false"," with ",[18,1488,1489],{},"unlisted: true"," in the new schema; two were genuinely obsolete and deleted, with redirects added.",[14,1492,1493],{},"Keep the manual list in the pull request description so reviewers see every decision a human made, next to the summary of everything the script did. That split — rules for the patterns, people for the exceptions, both visible — is what makes a bulk conversion trustworthy.",[475,1495,1496,1549],{},[478,1497,486,1502,486,1505,486,1508,486,1511],{"viewBox":1498,"role":481,"ariaLabelledBy":1499,"xmlns":485},"0 0 760 240",[1500,1501],"fmc-cmp-title","fmc-cmp-desc",[488,1503,1504],{"id":1500},"Errors left behind by hand versus scripted conversion",[492,1506,1507],{"id":1501},"Two bars normalised per 1,000 files. Hand conversion of a 200-file pilot left 155 inconsistencies per 1,000 files, discovered later by the build. Scripted conversion of 3,140 files left zero, because every file was validated against the destination schema.",[496,1509],{"x":498,"y":498,"width":499,"height":1510,"fill":501},"240",[503,1512,506,1513,506,1516,506,1522,506,1529,506,1534,506,1538,506,1541,506,1546,486],{"style":505},[457,1514,1515],{"x":509,"y":510,"fill":511,"style":512},"Inconsistencies left per 1,000 files converted",[457,1517,1521],{"x":1518,"y":1519,"fill":511,"style":1520},"30","86","font-weight:700","By hand (pilot)",[496,1523],{"x":1524,"y":1525,"width":1526,"height":1527,"rx":572,"fill":547,"opacity":1528,"stroke":549,"style":534},"200","66","465","32","0.28",[457,1530,1533],{"x":1531,"y":1532,"fill":511,"style":1520},"673","88","155",[457,1535,1537],{"x":1518,"y":1536,"fill":511,"style":1520},"156","Scripted + schema",[496,1539],{"x":1524,"y":1540,"width":530,"height":1527,"fill":531,"stroke":533,"style":534},"136",[457,1542,498],{"x":1543,"y":1544,"fill":1545,"style":1520},"214","157","#3f6410",[457,1547,1548],{"x":509,"y":1543,"fill":603,"style":604},"31 issues in 200 hand-converted files vs 0 in 3,140 scripted ones",[607,1550,1551],{},"The schema check, not the script alone, is what drove the residue to zero.",[53,1553,1555],{"id":1554},"measured-impact","Measured Impact",[373,1557,1558,1574],{},[1559,1560,1561],"thead",{},[1562,1563,1564,1568,1571],"tr",{},[1565,1566,1567],"th",{},"Measure",[1565,1569,1570],{},"Hand conversion (pilot, 200 files)",[1565,1572,1573],{},"Scripted conversion (3,140 files)",[1575,1576,1577,1589,1600,1611],"tbody",{},[1562,1578,1579,1583,1586],{},[1580,1581,1582],"td",{},"Time",[1580,1584,1585],{},"~6 hours",[1580,1587,1588],{},"1 day to write + 4 s per run",[1562,1590,1591,1594,1597],{},[1580,1592,1593],{},"Inconsistencies left in output",[1580,1595,1596],{},"31 (found later by the build)",[1580,1598,1599],{},"0 (schema-validated)",[1562,1601,1602,1605,1608],{},[1580,1603,1604],{},"Aliases carried into redirects",[1580,1606,1607],{},"74 of 81",[1580,1609,1610],{},"1,203 of 1,203",[1562,1612,1613,1616,1619],{},[1580,1614,1615],{},"Review effort",[1580,1617,1618],{},"line-by-line diffs",[1580,1620,1621],{},"map + summary + 140 spot checks",[53,1623,1625],{"id":1624},"pitfalls-rollback","Pitfalls & Rollback",[58,1627,1628,1635,1641,1651,1660,1666],{},[61,1629,1630,1634],{},[1631,1632,1633],"strong",{},"Regex on YAML."," Titles with colons, multi-line strings and quoted values break text replacement. Parse and re-serialise.",[61,1636,1637,1640],{},[1631,1638,1639],{},"Lossy YAML libraries."," Default serialisers drop comments and reorder keys, producing enormous diffs. Use a comment-preserving document API.",[61,1642,1643,1646,1647,1650],{},[1631,1644,1645],{},"Guessing date formats."," ",[18,1648,1649],{},"new Date('03\u002F04\u002F2021')"," is ambiguous. Parse with explicit patterns and fail on anything else.",[61,1652,1653,1656,1657,1659],{},[1631,1654,1655],{},"Leaving aliases in front matter."," Most destination generators ignore Hugo's ",[18,1658,872],{},"; collect them into the redirect system.",[61,1661,1662,1665],{},[1631,1663,1664],{},"Converting while writers keep editing the source."," Freeze content during the final run, or re-run the script right before cutover so late edits are carried across.",[61,1667,1668,1671],{},[1631,1669,1670],{},"Rollback:"," the conversion is a single commit produced by a script from untouched source; revert the commit, fix the map, re-run.",[53,1673,1675],{"id":1674},"conclusion","Conclusion",[14,1677,1678],{},"Front matter conversion is where migrations quietly accumulate errors, because it looks too simple to need tooling. An inventory of real key usage, a declarative field map, explicit type coercion, validation against the destination generator's own schema and a summary-based review turned 3,140 files of inconsistent Hugo front matter into schema-valid Astro content in a four-second, re-runnable script — with every alias preserved as a redirect.",[53,1680,1682],{"id":1681},"faq","FAQ",[1684,1685,1687],"h3",{"id":1686},"why-script-front-matter-conversion-instead-of-using-find-and-replace","Why script front matter conversion instead of using find and replace?",[14,1689,1690],{},"Front matter is structured data. Regex replacements break on quoting, multi-line values, lists and YAML edge cases such as colons in titles. Parsing each file, transforming the data and writing it back with a YAML library is safer and can be validated.",[1684,1692,1694],{"id":1693},"how-do-i-preserve-formatting-and-comments-in-yaml","How do I preserve formatting and comments in YAML?",[14,1696,1697],{},"Use a YAML library that keeps comments and key order, such as the yaml package's Document API in JavaScript or ruamel.yaml in Python. Most libraries drop comments by default, which creates noisy diffs.",[1684,1699,1701],{"id":1700},"what-should-the-conversion-validate","What should the conversion validate?",[14,1703,1704],{},"Required fields present, correct types for dates and booleans, allowed values for enums such as layout or status, unique slugs, and no unknown fields left over. Validate against the destination generator's schema, for example an Astro content collection schema.",[1684,1706,1708],{"id":1707},"how-do-i-review-a-change-to-thousands-of-files","How do I review a change to thousands of files?",[14,1710,1711],{},"Generate a summary instead of reading every diff: counts of files per transformation, a list of files that needed manual rules, and validation errors. Then spot-check a sample of each category.",[53,1713,1715],{"id":1714},"related","Related",[58,1717,1718,1726,1731,1738,1745],{},[61,1719,1720,1646,1723,1725],{},[1631,1721,1722],{},"Parent:",[42,1724,45],{"href":44}," — the migration playbook.",[61,1727,1728,1730],{},[42,1729,50],{"href":49}," — the migration this conversion served.",[61,1732,1733,1737],{},[42,1734,1736],{"href":1735},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fastro-vs-eleventy-for-documentation-sites\u002Fcontent-collections-vs-eleventy-data-cascade\u002F","Content Collections vs Eleventy Data Cascade"," — schema validation on the destination side.",[61,1739,1740,1744],{},[42,1741,1743],{"href":1742},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fdocs-frameworks-docusaurus-starlight-vitepress\u002Fmigrating-from-mkdocs-to-starlight\u002F","Migrating from MkDocs to Starlight"," — the same technique for a Python source.",[61,1746,1747,1751],{},[42,1748,1750],{"href":1749},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fmigrating-between-static-site-generators\u002Fmigrating-wordpress-to-a-static-site-generator\u002F","Migrating WordPress to a Static Site Generator"," — generating front matter from an API.",[1753,1754,1755],"style",{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":101,"searchDepth":115,"depth":115,"links":1757},[1758,1759,1760,1761,1762,1763,1764,1765,1766,1767,1773],{"id":55,"depth":115,"text":56},{"id":90,"depth":115,"text":91},{"id":612,"depth":115,"text":613},{"id":1173,"depth":115,"text":1174},{"id":1449,"depth":115,"text":1450},{"id":1469,"depth":115,"text":1470},{"id":1554,"depth":115,"text":1555},{"id":1624,"depth":115,"text":1625},{"id":1674,"depth":115,"text":1675},{"id":1681,"depth":115,"text":1682,"children":1768},[1769,1770,1771,1772],{"id":1686,"depth":136,"text":1687},{"id":1693,"depth":136,"text":1694},{"id":1700,"depth":136,"text":1701},{"id":1707,"depth":136,"text":1708},{"id":1714,"depth":115,"text":1715},[1775,1778,1781,1782],{"name":1776,"item":1777},"Home","\u002F",{"name":1779,"item":1780},"Choosing the Right Static Site Generator for Production","\u002Fchoosing-the-right-static-site-generator-for-production\u002F",{"name":45,"item":44},{"name":5,"item":1783},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fmigrating-between-static-site-generators\u002Fconverting-front-matter-at-scale-during-migration\u002F","2026-09-18","Script front matter conversion across thousands of Markdown files between generators: field maps, type fixes, validation against a schema, and reviewable diffs.","md",[1788,1789,1790,1791],{"q":1687,"a":1690},{"q":1694,"a":1697},{"q":1701,"a":1704},{"q":1708,"a":1792},{"Generate a summary instead of reading every diff":1793},"counts of files per transformation, a list of files that needed manual rules, and validation errors. Then spot-check a sample of each category.",{},true,"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fmigrating-between-static-site-generators\u002Fconverting-front-matter-at-scale-during-migration",{"title":5,"description":1785},"choosing-the-right-static-site-generator-for-production\u002Fmigrating-between-static-site-generators\u002Fconverting-front-matter-at-scale-during-migration\u002Findex","article","Q32riXjRo808O0kBGZCkcv8HyHX8E8t6BVKcN9TQeZ0",[1802,1805,1808,1811,1814,1817,1820,1823,1826,1829,1832,1834,1837,1840,1843,1846,1849,1852,1855,1858,1860,1863,1866,1869,1872,1875,1878,1879,1881,1884,1887,1890,1893,1895,1897,1900,1903,1906,1909,1912,1915,1918,1921,1924,1927,1930,1933,1936,1939,1942,1945,1948,1951,1954,1957,1960,1963,1966,1969,1972,1975,1978,1981,1984,1987,1990,1993,1996,1999,2002,2005,2008,2011,2014,2017,2020,2023,2026,2029,2032,2035,2038,2041,2044,2047,2050,2053,2056,2059,2062,2065,2068,2071,2074,2077,2080,2083,2086,2089,2092,2095,2098,2101,2104,2107,2110,2113,2116,2119,2122,2125,2128,2131,2134,2137,2140,2143,2146,2149,2152,2155,2158,2161,2164,2167,2170,2173,2176,2179,2182,2185,2188,2191,2194,2197,2200,2203,2206,2209,2212,2215,2218,2221,2224,2227,2230,2233,2236,2239,2242,2245,2248,2251,2254,2257,2260,2263,2266,2269,2272,2275,2278,2281,2284,2287,2290,2293,2296,2299,2302,2305,2308,2311,2314,2317,2320,2323,2326,2329,2332,2335,2338,2341],{"path":1803,"title":1804},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fastro-vs-eleventy-for-documentation-sites\u002Fastro-vs-eleventy-build-times-at-10000-pages","Astro vs Eleventy Build Times at 10,000 Pages",{"path":1806,"title":1807},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fastro-vs-eleventy-for-documentation-sites\u002Fchoosing-between-astro-and-eleventy-for-large-docs","Astro vs Eleventy for Large Docs (1000+ Pages)",{"path":1809,"title":1810},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fastro-vs-eleventy-for-documentation-sites\u002Fcontent-collections-vs-eleventy-data-cascade","Content Collections vs the Eleventy Data Cascade",{"path":1812,"title":1813},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fastro-vs-eleventy-for-documentation-sites","Astro vs Eleventy for Documentation Sites",{"path":1815,"title":1816},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fastro-vs-eleventy-for-documentation-sites\u002Fshortcodes-vs-components-for-docs-authors","Shortcodes vs Components for Docs Authors",{"path":1818,"title":1819},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fastro-vs-eleventy-for-documentation-sites\u002Fsidebar-navigation-in-astro-and-eleventy","Sidebar Navigation in Astro and Eleventy",{"path":1821,"title":1822},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fdocs-frameworks-docusaurus-starlight-vitepress\u002Fcustomizing-starlight-without-forking-the-theme","Customizing Starlight Without Forking the Theme",{"path":1824,"title":1825},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fdocs-frameworks-docusaurus-starlight-vitepress\u002Fdocusaurus-vs-starlight-for-product-documentation","Docusaurus vs Starlight for Product Documentation",{"path":1827,"title":1828},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fdocs-frameworks-docusaurus-starlight-vitepress","Docs Frameworks: Docusaurus, Starlight and VitePress",{"path":1830,"title":1831},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fdocs-frameworks-docusaurus-starlight-vitepress\u002Fmdx-vs-markdoc-for-docs-content","MDX vs Markdoc for Docs Content",{"path":1833,"title":1743},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fdocs-frameworks-docusaurus-starlight-vitepress\u002Fmigrating-from-mkdocs-to-starlight",{"path":1835,"title":1836},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fdocs-frameworks-docusaurus-starlight-vitepress\u002Fversioned-documentation-with-docusaurus","Versioned Documentation with Docusaurus",{"path":1838,"title":1839},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fdocs-frameworks-docusaurus-starlight-vitepress\u002Fvitepress-for-library-documentation","VitePress for Library Documentation",{"path":1841,"title":1842},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fhugo-build-times-for-large-repositories\u002Fhow-to-benchmark-hugo-vs-astro-build-speeds","How to Benchmark Hugo vs Astro Build Speeds",{"path":1844,"title":1845},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fhugo-build-times-for-large-repositories\u002Fhugo-partialcached-for-faster-builds","Hugo partialCached for Faster Builds",{"path":1847,"title":1848},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fhugo-build-times-for-large-repositories","Hugo Build Times for Large Repositories",{"path":1850,"title":1851},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fhugo-build-times-for-large-repositories\u002Fprofiling-hugo-templates-with-template-metrics","Profiling Hugo Templates With Template Metrics",{"path":1853,"title":1854},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fhugo-build-times-for-large-repositories\u002Freducing-hugo-memory-usage-on-ci-runners","Reducing Hugo Memory Usage on CI Runners",{"path":1856,"title":1857},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fhugo-build-times-for-large-repositories\u002Fspeeding-up-hugo-builds-with-render-hooks-and-caching","Speeding Up Hugo Builds with Render Hooks & Caching",{"path":1859,"title":1779},"\u002Fchoosing-the-right-static-site-generator-for-production",{"path":1861,"title":1862},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fjekyll-plugin-ecosystem\u002Feleventy-vs-jekyll-for-markdown-heavy-blogs","Eleventy vs Jekyll for Markdown-Heavy Blogs",{"path":1864,"title":1865},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fjekyll-plugin-ecosystem","Jekyll Plugin Ecosystem",{"path":1867,"title":1868},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fjekyll-plugin-ecosystem\u002Freplacing-jekyll-plugins-when-migrating-to-eleventy","Replacing Jekyll Plugins When Migrating to Eleventy",{"path":1870,"title":1871},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fjekyll-plugin-ecosystem\u002Frunning-jekyll-on-github-pages-without-plugins","Running Jekyll on GitHub Pages Without Plugins",{"path":1873,"title":1874},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fjekyll-plugin-ecosystem\u002Fspeeding-up-slow-jekyll-builds","Speeding Up Slow Jekyll Builds",{"path":1876,"title":1877},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fjekyll-plugin-ecosystem\u002Fupgrading-jekyll-and-ruby-versions-safely","Upgrading Jekyll and Ruby Versions Safely",{"path":1796,"title":5},{"path":1880,"title":45},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fmigrating-between-static-site-generators",{"path":1882,"title":1883},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fmigrating-between-static-site-generators\u002Fkeeping-redirects-working-after-an-ssg-migration","Keeping Redirects Working After an SSG Migration",{"path":1885,"title":1886},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fmigrating-between-static-site-generators\u002Fmigrating-a-docs-site-from-jekyll-to-hugo","Migrating a Docs Site From Jekyll to Hugo",{"path":1888,"title":1889},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fmigrating-between-static-site-generators\u002Fmigrating-from-gatsby-to-astro","Migrating from Gatsby to Astro",{"path":1891,"title":1892},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fmigrating-between-static-site-generators\u002Fmigrating-from-hugo-to-astro-without-breaking-urls","Migrating From Hugo to Astro Without Breaking URLs",{"path":1894,"title":1750},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fmigrating-between-static-site-generators\u002Fmigrating-wordpress-to-a-static-site-generator",{"path":1896,"title":1466},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fmigrating-between-static-site-generators\u002Fporting-shortcodes-and-includes-between-generators",{"path":1898,"title":1899},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fnextjs-static-export-for-content-sites\u002Fhandling-dynamic-routes-in-nextjs-static-export","Handling Dynamic Routes in Next.js Static Export",{"path":1901,"title":1902},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fnextjs-static-export-for-content-sites","Next.js Static Export for Content Sites",{"path":1904,"title":1905},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fnextjs-static-export-for-content-sites\u002Fmigrating-from-gatsby-to-nextjs-static-export","Migrating from Gatsby to Next.js Static Export",{"path":1907,"title":1908},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fnextjs-static-export-for-content-sites\u002Fnextjs-app-router-static-export-limitations","Next.js App Router Static Export Limitations",{"path":1910,"title":1911},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fnextjs-static-export-for-content-sites\u002Fnextjs-static-export-vs-astro-for-marketing-sites","Next.js Static Export vs Astro for Marketing",{"path":1913,"title":1914},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fnextjs-static-export-for-content-sites\u002Foptimizing-images-in-nextjs-static-export","Optimizing Images in Next.js Static Export",{"path":1916,"title":1917},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fsearch-for-static-sites\u002Fadding-pagefind-to-an-astro-site","Adding Pagefind to an Astro Site",{"path":1919,"title":1920},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fsearch-for-static-sites\u002Fbuilding-a-lunr-index-at-build-time-in-eleventy","Building a Lunr Index at Build Time in Eleventy",{"path":1922,"title":1923},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fsearch-for-static-sites","Search for Static Sites",{"path":1925,"title":1926},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fsearch-for-static-sites\u002Findexing-hugo-sites-with-pagefind","Indexing Hugo Sites with Pagefind",{"path":1928,"title":1929},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fsearch-for-static-sites\u002Fmultilingual-search-on-static-sites","Multilingual Search on Static Sites",{"path":1931,"title":1932},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fsearch-for-static-sites\u002Fpagefind-vs-algolia-docsearch","Pagefind vs Algolia DocSearch",{"path":1934,"title":1935},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fsearch-for-static-sites\u002Fsearch-index-size-budgets-for-large-docs","Search Index Size Budgets for Large Docs",{"path":1937,"title":1938},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fssg-framework-selection-matrix\u002Fbest-ssg-for-technical-writers-without-coding-experience","Best SSG for Non-Developer Technical Writers",{"path":1940,"title":1941},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fssg-framework-selection-matrix\u002Fchoosing-an-ssg-for-api-reference-documentation","Choosing an SSG for API Reference Documentation",{"path":1943,"title":1944},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fssg-framework-selection-matrix\u002Fevaluating-ssg-accessibility-defaults","Evaluating SSG Accessibility Defaults",{"path":1946,"title":1947},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fssg-framework-selection-matrix","SSG Framework Selection Matrix",{"path":1949,"title":1950},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fssg-framework-selection-matrix\u002Fpicking-an-ssg-for-a-multi-language-documentation-site","Picking an SSG for a Multi-Language Docs Site",{"path":1952,"title":1953},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fssg-framework-selection-matrix\u002Fssg-selection-checklist-for-engineering-teams","SSG Selection Checklist for Engineering Teams",{"path":1955,"title":1956},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fssg-framework-selection-matrix\u002Ftotal-cost-of-ownership-for-static-site-generators","Total Cost of Ownership for Static Site Generators",{"path":1958,"title":1959},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcdn-caching-rules-for-ssgs\u002Fcache-busting-with-content-hashed-filenames","Cache Busting with Content-Hashed Filenames",{"path":1961,"title":1962},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcdn-caching-rules-for-ssgs","CDN Caching Rules for SSGs",{"path":1964,"title":1965},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcdn-caching-rules-for-ssgs\u002Fpurging-the-cdn-cache-after-a-static-deploy","Purging the CDN Cache After a Static Deploy",{"path":1967,"title":1968},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcdn-caching-rules-for-ssgs\u002Fsetting-cache-control-headers-on-cloudflare-pages","Cache-Control Headers on Cloudflare Pages",{"path":1970,"title":1971},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcdn-caching-rules-for-ssgs\u002Fsetting-up-proper-cache-headers-on-netlify","Proper Cache Headers on Netlify for SSGs",{"path":1973,"title":1974},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcdn-caching-rules-for-ssgs\u002Fstale-while-revalidate-for-static-html","Stale-While-Revalidate for Static HTML",{"path":1976,"title":1977},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcumulative-layout-shift-fixes-for-static-sites\u002Feliminating-layout-shift-from-web-fonts","Eliminating Layout Shift From Web Fonts",{"path":1979,"title":1980},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcumulative-layout-shift-fixes-for-static-sites\u002Ffixing-cls-from-cookie-banners","Fixing CLS from Cookie Banners",{"path":1982,"title":1983},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcumulative-layout-shift-fixes-for-static-sites\u002Ffixing-cls-from-late-loading-embeds","Fixing CLS From Late-Loading Embeds",{"path":1985,"title":1986},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcumulative-layout-shift-fixes-for-static-sites\u002Ffixing-cls-from-sticky-headers-and-anchor-links","Fixing CLS from Sticky Headers and Anchor Links",{"path":1988,"title":1989},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcumulative-layout-shift-fixes-for-static-sites","Cumulative Layout Shift Fixes for Static Sites",{"path":1991,"title":1992},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcumulative-layout-shift-fixes-for-static-sites\u002Fmeasuring-cls-in-the-field-with-web-vitals-js","Measuring CLS in the Field With web-vitals.js",{"path":1994,"title":1995},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcumulative-layout-shift-fixes-for-static-sites\u002Freserving-space-for-images-and-embeds-to-stop-layout-shift","Reserving Space for Images and Embeds",{"path":1997,"title":1998},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Ffont-loading-strategies-for-static-sites\u002Ffont-display-optional-vs-swap","font-display: optional vs swap",{"path":2000,"title":2001},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Ffont-loading-strategies-for-static-sites","Font Loading Strategies for Static Sites",{"path":2003,"title":2004},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Ffont-loading-strategies-for-static-sites\u002Fmetric-matched-fallback-fonts-with-size-adjust","Metric-Matched Fallback Fonts with size-adjust",{"path":2006,"title":2007},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Ffont-loading-strategies-for-static-sites\u002Fpreloading-fonts-without-double-downloads","Preloading Fonts Without Double Downloads",{"path":2009,"title":2010},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Ffont-loading-strategies-for-static-sites\u002Fself-hosting-google-fonts-to-eliminate-layout-shift","Self-Host Google Fonts to Eliminate Layout Shift",{"path":2012,"title":2013},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Ffont-loading-strategies-for-static-sites\u002Fsubsetting-variable-fonts-for-faster-first-render","Subsetting Variable Fonts for Faster First Render",{"path":2015,"title":2016},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fimage-optimization-pipelines-in-astro\u002Fbuilding-an-image-cdn-pipeline-for-static-sites","Building an Image CDN Pipeline for Static Sites",{"path":2018,"title":2019},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fimage-optimization-pipelines-in-astro\u002Fgenerating-open-graph-images-at-build-time","Generating Open Graph Images at Build Time",{"path":2021,"title":2022},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fimage-optimization-pipelines-in-astro","Image Optimization Pipelines in Astro",{"path":2024,"title":2025},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fimage-optimization-pipelines-in-astro\u002Flazy-loading-images-without-hurting-lcp","Lazy-Loading Images Without Hurting LCP",{"path":2027,"title":2028},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fimage-optimization-pipelines-in-astro\u002Foptimizing-webp-images-in-hugo-without-plugins","Optimizing WebP Images in Hugo Without Plugins",{"path":2030,"title":2031},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fimage-optimization-pipelines-in-astro\u002Fresponsive-images-with-srcset-in-eleventy","Responsive Images with srcset in Eleventy",{"path":2033,"title":2034},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fimage-optimization-pipelines-in-astro\u002Fserving-avif-with-fallbacks-on-static-sites","Serving AVIF With Fallbacks on Static Sites",{"path":2036,"title":2037},"\u002Fperformance-optimization-core-web-vitals-for-ssgs","Core Web Vitals Optimization for SSGs",{"path":2039,"title":2040},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fjavascript-hydration-partial-rendering\u002Fastro-islands-vs-full-hydration-performance","Astro Islands vs Full Hydration Performance",{"path":2042,"title":2043},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fjavascript-hydration-partial-rendering\u002Fdeferring-hydration-with-client-visible-in-astro","Deferring Hydration with client:visible in Astro",{"path":2045,"title":2046},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fjavascript-hydration-partial-rendering\u002Fhow-to-reduce-bundle-size-in-eleventy-builds","How to Reduce Bundle Size in Eleventy Builds",{"path":2048,"title":2049},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fjavascript-hydration-partial-rendering","JavaScript Hydration & Partial Rendering",{"path":2051,"title":2052},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fjavascript-hydration-partial-rendering\u002Fmeasuring-inp-on-static-sites-with-real-user-monitoring","Measuring INP on Static Sites with RUM",{"path":2054,"title":2055},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fjavascript-hydration-partial-rendering\u002Freplacing-react-islands-with-web-components","Replacing React Islands with Web Components",{"path":2057,"title":2058},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Flargest-contentful-paint-optimization-for-static-sites\u002Feliminating-render-blocking-css-on-static-sites","Eliminating Render-Blocking CSS on Static Sites",{"path":2060,"title":2061},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Flargest-contentful-paint-optimization-for-static-sites\u002Ffixing-lcp-on-text-heavy-documentation-pages","Fixing LCP on Text-Heavy Documentation Pages",{"path":2063,"title":2064},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Flargest-contentful-paint-optimization-for-static-sites","Largest Contentful Paint Optimization for Static Sites",{"path":2066,"title":2067},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Flargest-contentful-paint-optimization-for-static-sites\u002Fmeasuring-lcp-subparts-with-devtools","Measuring LCP Subparts with DevTools",{"path":2069,"title":2070},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Flargest-contentful-paint-optimization-for-static-sites\u002Foptimizing-lcp-on-astro-with-priority-hints","Optimizing LCP on Astro with Priority Hints",{"path":2072,"title":2073},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Flargest-contentful-paint-optimization-for-static-sites\u002Freducing-lcp-from-hero-images-on-static-sites","Reducing LCP from Hero Images on Static Sites",{"path":2075,"title":2076},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fperformance-budgets-and-lighthouse-ci\u002Fcomparing-lab-and-field-data-with-crux","Comparing Lab and Field Data with CrUX",{"path":2078,"title":2079},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fperformance-budgets-and-lighthouse-ci","Performance Budgets and Lighthouse CI",{"path":2081,"title":2082},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fperformance-budgets-and-lighthouse-ci\u002Freducing-lighthouse-score-variance-in-ci","Reducing Lighthouse Score Variance in CI",{"path":2084,"title":2085},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fperformance-budgets-and-lighthouse-ci\u002Frunning-webpagetest-scripts-against-preview-deploys","Running WebPageTest Scripts Against Preview Deploys",{"path":2087,"title":2088},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fperformance-budgets-and-lighthouse-ci\u002Fsetting-up-lighthouse-ci-for-a-static-site","Setting Up Lighthouse CI for a Static Site",{"path":2090,"title":2091},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fperformance-budgets-and-lighthouse-ci\u002Ftracking-bundle-size-per-pull-request","Tracking Bundle Size per Pull Request",{"path":2093,"title":2094},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fperformance-budgets-and-lighthouse-ci\u002Fwriting-a-performance-budget-that-fails-builds","Writing a Performance Budget That Fails Builds",{"path":2096,"title":2097},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fresource-hints-and-navigation-speed\u002Fauditing-unused-preloads","Auditing Unused Preloads",{"path":2099,"title":2100},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fresource-hints-and-navigation-speed","Resource Hints and Navigation Speed",{"path":2102,"title":2103},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fresource-hints-and-navigation-speed\u002Finstant-navigation-with-speculation-rules","Instant Navigation with Speculation Rules",{"path":2105,"title":2106},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fresource-hints-and-navigation-speed\u002Fpreconnect-vs-dns-prefetch-on-static-sites","Preconnect vs DNS-Prefetch on Static Sites",{"path":2108,"title":2109},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fresource-hints-and-navigation-speed\u002Fprefetching-links-in-astro","Prefetching Links in Astro",{"path":2111,"title":2112},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fresource-hints-and-navigation-speed\u002Fview-transitions-on-multi-page-static-sites","View Transitions on Multi-Page Static Sites",{"path":2114,"title":2115},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fthird-party-script-performance-on-static-sites\u002Fauditing-third-party-scripts-with-lighthouse","Auditing Third-Party Scripts With Lighthouse",{"path":2117,"title":2118},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fthird-party-script-performance-on-static-sites","Third-Party Script Performance on Static Sites",{"path":2120,"title":2121},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fthird-party-script-performance-on-static-sites\u002Flazy-loading-youtube-embeds-on-static-sites","Lazy-Loading YouTube Embeds on Static Sites",{"path":2123,"title":2124},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fthird-party-script-performance-on-static-sites\u002Floading-google-tag-manager-without-hurting-inp","Loading Google Tag Manager Without Hurting INP",{"path":2126,"title":2127},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fthird-party-script-performance-on-static-sites\u002Frunning-third-party-scripts-in-a-web-worker-with-partytown","Running Third-Party Scripts in a Web Worker with Partytown",{"path":2129,"title":2130},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fthird-party-script-performance-on-static-sites\u002Fself-hosting-analytics-to-cut-third-party-requests","Self-Hosting Analytics to Cut Third-Party Requests",{"path":2132,"title":2133},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcloudflare-pages-edge-caching-setup\u002Fautomating-eleventy-deployments-with-cloudflare-pages","Automating Eleventy Deployments on Cloudflare Pages",{"path":2135,"title":2136},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcloudflare-pages-edge-caching-setup\u002Fconfiguring-redirects-on-cloudflare-pages","Configuring Redirects on Cloudflare Pages",{"path":2138,"title":2139},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcloudflare-pages-edge-caching-setup\u002Fcustom-domains-and-tls-on-cloudflare-pages","Custom Domains and TLS on Cloudflare Pages",{"path":2141,"title":2142},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcloudflare-pages-edge-caching-setup\u002Fdeploying-hugo-to-cloudflare-pages-and-workers","Deploying Hugo to Cloudflare Pages and Workers",{"path":2144,"title":2145},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcloudflare-pages-edge-caching-setup","Cloudflare Pages Edge Caching Setup",{"path":2147,"title":2148},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcloudflare-pages-edge-caching-setup\u002Fmigrating-from-cloudflare-pages-to-workers-static-assets","Migrating from Cloudflare Pages to Workers Static Assets",{"path":2150,"title":2151},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcontent-workflows-for-documentation-teams\u002Fchecking-links-in-pull-requests","Checking Links in Pull Requests",{"path":2153,"title":2154},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcontent-workflows-for-documentation-teams\u002Fdocs-as-code-review-workflow-for-writers","Docs-as-Code Review Workflow for Writers",{"path":2156,"title":2157},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcontent-workflows-for-documentation-teams\u002Feditorial-checks-with-vale-in-ci","Editorial Checks with Vale in CI",{"path":2159,"title":2160},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcontent-workflows-for-documentation-teams","Content Workflows for Documentation Teams",{"path":2162,"title":2163},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcontent-workflows-for-documentation-teams\u002Fscheduling-content-publication-with-cron-triggered-builds","Scheduling Content Publication With Cron-Triggered Builds",{"path":2165,"title":2166},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcontent-workflows-for-documentation-teams\u002Fwiring-a-headless-cms-to-a-static-build","Wiring a Headless CMS to a Static Build",{"path":2168,"title":2169},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fgithub-actions-for-automated-ssg-builds\u002Fbuilding-astro-sites-with-github-actions","Building Astro Sites with GitHub Actions",{"path":2171,"title":2172},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fgithub-actions-for-automated-ssg-builds\u002Fcaching-node-modules-in-github-actions-for-faster-ssg-builds","Caching node_modules in GitHub Actions",{"path":2174,"title":2175},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fgithub-actions-for-automated-ssg-builds\u002Fdeploying-to-github-pages-with-actions","Deploying to GitHub Pages with Actions",{"path":2177,"title":2178},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fgithub-actions-for-automated-ssg-builds\u002Fdeploying-to-multiple-environments-from-one-workflow","Deploying to Multiple Environments From One Workflow",{"path":2180,"title":2181},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fgithub-actions-for-automated-ssg-builds\u002Fhow-to-set-up-github-actions-for-hugo-deployments","GitHub Actions for Hugo Deployments",{"path":2183,"title":2184},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fgithub-actions-for-automated-ssg-builds","GitHub Actions for Automated SSG Builds",{"path":2186,"title":2187},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fgithub-actions-for-automated-ssg-builds\u002Fmatrix-builds-for-multi-site-monorepos","Matrix Builds for Multi-Site Monorepos",{"path":2189,"title":2190},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fincremental-builds-and-build-caching-for-ssgs\u002Fcaching-hugo-builds-in-github-actions","Caching Hugo Builds in GitHub Actions",{"path":2192,"title":2193},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fincremental-builds-and-build-caching-for-ssgs\u002Fenabling-incremental-builds-in-eleventy","Enabling Incremental Builds in Eleventy",{"path":2195,"title":2196},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fincremental-builds-and-build-caching-for-ssgs\u002Fincremental-builds-in-astro-with-the-content-layer","Incremental Builds in Astro with the Content Layer",{"path":2198,"title":2199},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fincremental-builds-and-build-caching-for-ssgs","Incremental Builds and Build Caching for SSGs",{"path":2201,"title":2202},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fincremental-builds-and-build-caching-for-ssgs\u002Fmeasuring-build-time-regressions-in-ci","Measuring Build-Time Regressions in CI",{"path":2204,"title":2205},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fincremental-builds-and-build-caching-for-ssgs\u002Fremote-caching-with-turborepo-for-ssg-monorepos","Remote Caching with Turborepo for SSG Monorepos",{"path":2207,"title":2208},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fincremental-builds-and-build-caching-for-ssgs\u002Fsharing-build-cache-across-ci-runners","Sharing Build Cache Across CI Runners",{"path":2210,"title":2211},"\u002Fproduction-ready-deployment-cicd-workflows","Production-Ready Deployment & CI\u002FCD for SSGs",{"path":2213,"title":2214},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fmonitoring-static-sites-in-production\u002Falerting-on-cache-hit-ratio-drops","Alerting on Cache Hit Ratio Drops",{"path":2216,"title":2217},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fmonitoring-static-sites-in-production\u002Fbuilding-a-core-web-vitals-dashboard-from-rum-data","Building a Core Web Vitals Dashboard from RUM Data",{"path":2219,"title":2220},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fmonitoring-static-sites-in-production\u002Fcrawling-for-broken-links-on-a-schedule","Crawling for Broken Links on a Schedule",{"path":2222,"title":2223},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fmonitoring-static-sites-in-production","Monitoring Static Sites in Production",{"path":2225,"title":2226},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fmonitoring-static-sites-in-production\u002Flogging-404s-at-the-edge","Logging 404s at the Edge",{"path":2228,"title":2229},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fmonitoring-static-sites-in-production\u002Fuptime-and-synthetic-checks-for-static-sites","Uptime and Synthetic Checks for Static Sites",{"path":2231,"title":2232},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fnetlify-vs-vercel-deployment-strategies\u002Fconfiguring-vercel-for-hugo-and-eleventy","Configuring Vercel for Hugo and Eleventy",{"path":2234,"title":2235},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fnetlify-vs-vercel-deployment-strategies","Netlify vs Vercel Deployment Strategies",{"path":2237,"title":2238},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fnetlify-vs-vercel-deployment-strategies\u002Fnetlify-build-hooks-for-content-updates","Netlify Build Hooks for Content Updates",{"path":2240,"title":2241},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fnetlify-vs-vercel-deployment-strategies\u002Fnetlify-redirects-and-rewrites-for-static-sites","Netlify Redirects and Rewrites for Static Sites",{"path":2243,"title":2244},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fnetlify-vs-vercel-deployment-strategies\u002Fsetting-up-deploy-previews-on-netlify-for-every-pull-request","Netlify Deploy Previews for Every Pull Request",{"path":2246,"title":2247},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fnetlify-vs-vercel-deployment-strategies\u002Fvercel-isr-vs-static-generation-for-ssgs","Vercel ISR vs Static Generation for SSGs",{"path":2249,"title":2250},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fpreview-environments-for-pull-requests\u002Fautomating-preview-deploy-pipelines-with-github-actions","Automating Preview Deploy Pipelines with GitHub Actions",{"path":2252,"title":2253},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fpreview-environments-for-pull-requests\u002Fcleaning-up-stale-preview-deployments","Cleaning Up Stale Preview Deployments",{"path":2255,"title":2256},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fpreview-environments-for-pull-requests","Preview Environments for Pull Requests",{"path":2258,"title":2259},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fpreview-environments-for-pull-requests\u002Fpassword-protecting-preview-deployments","Password-Protecting Preview Deployments",{"path":2261,"title":2262},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fpreview-environments-for-pull-requests\u002Fpreviewing-headless-cms-drafts","Previewing Headless CMS Drafts",{"path":2264,"title":2265},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fpreview-environments-for-pull-requests\u002Fvisual-regression-testing-on-preview-deploys","Visual Regression Testing on Preview Deploys",{"path":2267,"title":2268},"\u002Fproduction-ready-deployment-cicd-workflows\u002Frollbacks-and-deploy-safety-for-static-sites\u002Fatomic-deploys-vs-incremental-uploads","Atomic Deploys vs Incremental Uploads",{"path":2270,"title":2271},"\u002Fproduction-ready-deployment-cicd-workflows\u002Frollbacks-and-deploy-safety-for-static-sites\u002Fcanary-releases-for-static-sites","Canary Releases for Static Sites",{"path":2273,"title":2274},"\u002Fproduction-ready-deployment-cicd-workflows\u002Frollbacks-and-deploy-safety-for-static-sites\u002Ffeature-flags-on-static-sites","Feature Flags on Static Sites",{"path":2276,"title":2277},"\u002Fproduction-ready-deployment-cicd-workflows\u002Frollbacks-and-deploy-safety-for-static-sites","Rollbacks and Deploy Safety for Static Sites",{"path":2279,"title":2280},"\u002Fproduction-ready-deployment-cicd-workflows\u002Frollbacks-and-deploy-safety-for-static-sites\u002Frolling-back-a-bad-static-deploy-in-under-a-minute","Rolling Back a Bad Static Deploy in Under a Minute",{"path":2282,"title":2283},"\u002Fproduction-ready-deployment-cicd-workflows\u002Frollbacks-and-deploy-safety-for-static-sites\u002Frunning-smoke-tests-against-a-preview-url","Running Smoke Tests Against a Preview URL",{"path":2285,"title":2286},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fsecurity-headers-for-static-sites\u002Fauditing-npm-dependencies-in-ssg-pipelines","Auditing npm Dependencies in SSG Pipelines",{"path":2288,"title":2289},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fsecurity-headers-for-static-sites\u002Fenabling-hsts-and-preload-safely","Enabling HSTS and Preload Safely",{"path":2291,"title":2292},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fsecurity-headers-for-static-sites\u002Fhash-based-csp-for-inline-scripts-in-astro","Hash-Based CSP for Inline Scripts in Astro",{"path":2294,"title":2295},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fsecurity-headers-for-static-sites","Security Headers and Hardening for Static Sites",{"path":2297,"title":2298},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fsecurity-headers-for-static-sites\u002Fsecuring-deploy-credentials-with-github-oidc","Securing Deploy Credentials with GitHub OIDC",{"path":2300,"title":2301},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fsecurity-headers-for-static-sites\u002Fsubresource-integrity-for-third-party-assets","Subresource Integrity for Third-Party Assets",{"path":2303,"title":2304},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fsecurity-headers-for-static-sites\u002Fwriting-a-content-security-policy-for-a-static-site","Writing a Content Security Policy for a Static Site",{"path":2306,"title":2307},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fself-hosting-static-sites\u002Fclean-urls-and-trailing-slashes-on-s3","Clean URLs and Trailing Slashes on S3",{"path":2309,"title":2310},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fself-hosting-static-sites\u002Fcloudfront-functions-for-redirects","CloudFront Functions for Redirects",{"path":2312,"title":2313},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fself-hosting-static-sites\u002Fdeploying-a-static-site-to-s3-and-cloudfront","Deploying a Static Site to S3 and CloudFront",{"path":2315,"title":2316},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fself-hosting-static-sites","Self-Hosting Static Sites on S3, Nginx and Caddy",{"path":2318,"title":2319},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fself-hosting-static-sites\u002Fserving-a-static-site-with-caddy","Serving a Static Site with Caddy",{"path":2321,"title":2322},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fself-hosting-static-sites\u002Fserving-a-static-site-with-nginx","Serving a Static Site with Nginx",{"path":2324,"title":2325},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fself-hosting-static-sites\u002Fzero-downtime-deploys-with-symlink-swaps","Zero-Downtime Deploys with Symlink Swaps",{"path":2327,"title":2328},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fserverless-functions-for-static-sites\u002Fadding-a-contact-form-with-cloudflare-workers","Adding a Contact Form with Cloudflare Workers",{"path":2330,"title":2331},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fserverless-functions-for-static-sites\u002Fhandling-form-submissions-on-a-static-site","Handling Form Submissions on a Static Site",{"path":2333,"title":2334},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fserverless-functions-for-static-sites","Serverless Functions for Static Sites",{"path":2336,"title":2337},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fserverless-functions-for-static-sites\u002Fnetlify-functions-vs-cloudflare-workers","Netlify Functions vs Cloudflare Workers",{"path":2339,"title":2340},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fserverless-functions-for-static-sites\u002Fprotecting-a-static-site-behind-authentication","Protecting a Static Site Behind Authentication",{"path":2342,"title":2343},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fserverless-functions-for-static-sites\u002Fproxying-third-party-apis-from-an-edge-function","Proxying Third-Party APIs from an Edge Function",1789722847228]