[{"data":1,"prerenderedAt":1642},["ShallowReactive",2],{"footer-links":3,"content-\u002Fdevelop":12},[4,9],{"path":5,"title":6,"type":7,"isExternal":8},"https:\u002F\u002Fgithub.com\u002Flife-and-dev\u002Fmdsite","GitHub Repo","link",true,{"path":10,"title":11,"type":7,"isExternal":8},"https:\u002F\u002Fwww.npmjs.com\u002Fpackage\u002F@life-and-dev\u002Fmdsite","NPM Registry",{"id":13,"title":14,"body":15,"description":1635,"extension":1636,"meta":1637,"navigation":8,"path":1638,"seo":1639,"stem":1640,"__hash__":1641},"content\u002Fdevelop.md","Developing mdsite",{"type":16,"value":17,"toc":1612},"minimark",[18,22,37,42,67,70,98,101,115,120,131,152,191,203,207,395,418,422,436,458,467,510,513,586,589,595,600,603,607,614,642,659,663,784,788,802,844,914,928,947,951,970,974,980,1043,1052,1056,1059,1081,1092,1096,1113,1117,1128,1132,1137,1232,1236,1411,1442,1459,1497,1540,1549,1555,1567,1571,1608],[19,20,14],"h1",{"id":21},"developing-mdsite",[23,24,25,26,30,31,36],"p",{},"This page is for contributors working on ",[27,28,29],"code",{},"mdsite"," itself. End users do not need to read this; they should follow ",[32,33,35],"a",{"href":34},"","Install"," and the user-facing tutorials.",[23,38,39,41],{},[27,40,29],{}," is a small repo made of two parts:",[43,44,45,58],"ol",{},[46,47,48,49,53,54,57],"li",{},"A ",[50,51,52],"strong",{},"TypeScript CLI"," (",[27,55,56],{},"src\u002F",") that orchestrates a content directory.",[46,59,48,60,53,63,66],{},[50,61,62],{},"Nuxt renderer",[27,64,65],{},"mdsite-nuxt\u002F","), pulled in as a git submodule, that turns Markdown into a static site.",[23,68,69],{},"The rest of this page maps the repository, explains how the two parts integrate, and links to deeper topics.",[71,72,73,80,87],"ul",{},[46,74,75,79],{},[32,76,78],{"href":77},"develop\u002Fnuxt","Renderer (mdsite-nuxt submodule)"," — what the submodule is, how to customize it, how to add Nuxt components.",[46,81,82,86],{},[32,83,85],{"href":84},"develop\u002Ftests","Testing"," — how to run the test suite and how it is configured.",[46,88,89,93,94,97],{},[32,90,92],{"href":91},"develop\u002Frelease","Release"," — how to publish a new version of ",[27,95,96],{},"@life-and-dev\u002Fmdsite",".",[99,100],"hr",{},[102,103,105],"markdown-alert",{"type":104},"tip",[23,106,107,108,110,111,114],{},"MDsite is intentionally a thin CLI on top of Nuxt. If you find yourself adding rendering logic inside ",[27,109,56],{},", it probably belongs in the ",[32,112,113],{"href":77},"mdsite-nuxt submodule"," instead.",[116,117,119],"h2",{"id":118},"_0-first-time-setup","0. First-time setup",[23,121,122,123,126,127,130],{},"After a fresh clone, run ",[27,124,125],{},"npm ci"," once at the repo root before any ",[27,128,129],{},"tsc","\u002Ftypecheck or test task:",[132,133,137],"pre",{"className":134,"code":135,"language":136,"meta":34,"style":34},"language-sh shiki shiki-themes github-light github-dark","npm ci\n","sh",[27,138,139],{"__ignoreMap":34},[140,141,144,148],"span",{"class":142,"line":143},"line",1,[140,145,147],{"class":146},"sScJk","npm",[140,149,151],{"class":150},"sZZnC"," ci\n",[23,153,154,157,158,161,162,165,166,168,169,172,173,176,177,180,181,184,185,187,188,190],{},[27,155,156],{},"package.json"," lists devDeps such as ",[27,159,160],{},"@types\u002Fnode",", but a fresh clone has an empty ",[27,163,164],{},"node_modules\u002F",". Without ",[27,167,125],{},", ",[27,170,171],{},"npx tsc -p tsconfig.scripts.json --noEmit"," fails on Node built-in imports like ",[27,174,175],{},"node:fs"," with errors such as ",[27,178,179],{},"Cannot find name 'node:fs'"," or ",[27,182,183],{},"Cannot find module 'node:fs'",". Re-run ",[27,186,125],{}," whenever the lockfile or ",[27,189,156],{}," devDeps change.",[192,193,194],"blockquote",{},[23,195,196,197,199,200,202],{},"The CLI orchestrates ",[27,198,125],{}," for the renderer (",[27,201,65],{},") on its own; the step above is only for the CLI's own root-level devDeps used by typecheck and tests.",[116,204,206],{"id":205},"_1-repository-layout","1. Repository layout",[132,208,210],{"className":134,"code":209,"language":136,"meta":34,"style":34},"mdsite\u002F\n├── bin\u002F                # npm \"bin\" entry — thin launcher that imports dist\u002Findex.js\n├── src\u002F                # CLI source (TypeScript)\n│   ├── index.ts        # CLI entrypoint + command dispatch\n│   ├── commands\u002F       # One module per CLI command (init, start, generate, ...)\n│   ├── config\u002F         # mdsite.yml schema, defaults, and menu parsing\n│   ├── process\u002F        # Foreground\u002Fbackground child-process and runtime-state helpers\n│   └── renderer\u002F       # Renderer preparation: env, compat files, npm install, run\n├── scripts\u002F            # Repo scripts in TypeScript: dev alias, release versioning, package verification\n├── tsconfig.scripts.json # Type-checks scripts\u002F*.ts (extends tsconfig.json, noEmit)\n├── mdsite-nuxt\u002F        # git submodule — the Nuxt renderer (see below)\n├── docs\u002F               # This documentation, served as the demo content\n├── mdsite.yml          # Config for the docs\u002F content directory\n├── package.json        # CLI package (@life-and-dev\u002Fmdsite)\n├── tsconfig.json\n└── vitest.config.ts    # CLI test config (includes src\u002F**\u002F*.test.ts)\n",[27,211,212,217,230,241,256,269,282,295,309,320,331,342,353,364,375,383],{"__ignoreMap":34},[140,213,214],{"class":142,"line":143},[140,215,216],{"class":146},"mdsite\u002F\n",[140,218,220,223,226],{"class":142,"line":219},2,[140,221,222],{"class":146},"├──",[140,224,225],{"class":150}," bin\u002F",[140,227,229],{"class":228},"sJ8bj","                # npm \"bin\" entry — thin launcher that imports dist\u002Findex.js\n",[140,231,233,235,238],{"class":142,"line":232},3,[140,234,222],{"class":146},[140,236,237],{"class":150}," src\u002F",[140,239,240],{"class":228},"                # CLI source (TypeScript)\n",[140,242,244,247,250,253],{"class":142,"line":243},4,[140,245,246],{"class":146},"│",[140,248,249],{"class":150},"   ├──",[140,251,252],{"class":150}," index.ts",[140,254,255],{"class":228},"        # CLI entrypoint + command dispatch\n",[140,257,259,261,263,266],{"class":142,"line":258},5,[140,260,246],{"class":146},[140,262,249],{"class":150},[140,264,265],{"class":150}," commands\u002F",[140,267,268],{"class":228},"       # One module per CLI command (init, start, generate, ...)\n",[140,270,272,274,276,279],{"class":142,"line":271},6,[140,273,246],{"class":146},[140,275,249],{"class":150},[140,277,278],{"class":150}," config\u002F",[140,280,281],{"class":228},"         # mdsite.yml schema, defaults, and menu parsing\n",[140,283,285,287,289,292],{"class":142,"line":284},7,[140,286,246],{"class":146},[140,288,249],{"class":150},[140,290,291],{"class":150}," process\u002F",[140,293,294],{"class":228},"        # Foreground\u002Fbackground child-process and runtime-state helpers\n",[140,296,298,300,303,306],{"class":142,"line":297},8,[140,299,246],{"class":146},[140,301,302],{"class":150},"   └──",[140,304,305],{"class":150}," renderer\u002F",[140,307,308],{"class":228},"       # Renderer preparation: env, compat files, npm install, run\n",[140,310,312,314,317],{"class":142,"line":311},9,[140,313,222],{"class":146},[140,315,316],{"class":150}," scripts\u002F",[140,318,319],{"class":228},"            # Repo scripts in TypeScript: dev alias, release versioning, package verification\n",[140,321,323,325,328],{"class":142,"line":322},10,[140,324,222],{"class":146},[140,326,327],{"class":150}," tsconfig.scripts.json",[140,329,330],{"class":228}," # Type-checks scripts\u002F*.ts (extends tsconfig.json, noEmit)\n",[140,332,334,336,339],{"class":142,"line":333},11,[140,335,222],{"class":146},[140,337,338],{"class":150}," mdsite-nuxt\u002F",[140,340,341],{"class":228},"        # git submodule — the Nuxt renderer (see below)\n",[140,343,345,347,350],{"class":142,"line":344},12,[140,346,222],{"class":146},[140,348,349],{"class":150}," docs\u002F",[140,351,352],{"class":228},"               # This documentation, served as the demo content\n",[140,354,356,358,361],{"class":142,"line":355},13,[140,357,222],{"class":146},[140,359,360],{"class":150}," mdsite.yml",[140,362,363],{"class":228},"          # Config for the docs\u002F content directory\n",[140,365,367,369,372],{"class":142,"line":366},14,[140,368,222],{"class":146},[140,370,371],{"class":150}," package.json",[140,373,374],{"class":228},"        # CLI package (@life-and-dev\u002Fmdsite)\n",[140,376,378,380],{"class":142,"line":377},15,[140,379,222],{"class":146},[140,381,382],{"class":150}," tsconfig.json\n",[140,384,386,389,392],{"class":142,"line":385},16,[140,387,388],{"class":146},"└──",[140,390,391],{"class":150}," vitest.config.ts",[140,393,394],{"class":228},"    # CLI test config (includes src\u002F**\u002F*.test.ts)\n",[23,396,397,398,168,401,168,404,406,407,410,411,414,415,417],{},"The published npm package contains only ",[27,399,400],{},"bin\u002F",[27,402,403],{},"dist\u002F",[27,405,65],{},", and ",[27,408,409],{},"README.md"," (see the ",[27,412,413],{},"files"," field in ",[27,416,156],{},"). Everything else is repo-only.",[116,419,421],{"id":420},"_2-the-mdsite-nuxt-git-submodule","2. The mdsite-nuxt git submodule",[23,423,424,426,427,435],{},[27,425,65],{}," is a git submodule pinned in ",[32,428,432],{"href":429,"rel":430},"https:\u002F\u002Fgithub.com\u002Flife-and-dev\u002Fmdsite\u002Fblob\u002Fmain\u002F.gitmodules",[431],"nofollow",[27,433,434],{},".gitmodules",":",[132,437,441],{"className":438,"code":439,"language":440,"meta":34,"style":34},"language-ini shiki shiki-themes github-light github-dark","[submodule \"mdsite-nuxt\"]\n    path = mdsite-nuxt\n    url = https:\u002F\u002Fgithub.com\u002Flife-and-dev\u002Fmdsite-nuxt\n","ini",[27,442,443,448,453],{"__ignoreMap":34},[140,444,445],{"class":142,"line":143},[140,446,447],{},"[submodule \"mdsite-nuxt\"]\n",[140,449,450],{"class":142,"line":219},[140,451,452],{},"    path = mdsite-nuxt\n",[140,454,455],{"class":142,"line":232},[140,456,457],{},"    url = https:\u002F\u002Fgithub.com\u002Flife-and-dev\u002Fmdsite-nuxt\n",[23,459,460,461,466],{},"The submodule lives in its own repository at ",[32,462,465],{"href":463,"rel":464},"https:\u002F\u002Fgithub.com\u002Flife-and-dev\u002Fmdsite-nuxt",[431],"life-and-dev\u002Fmdsite-nuxt"," and is pinned to a specific commit by the parent repo. The CLI never clones or pulls it at runtime — it expects the submodule to already be checked out in the working tree (or vendored into the published package).",[132,468,472],{"className":469,"code":470,"language":471,"meta":34,"style":34},"language-bash shiki shiki-themes github-light github-dark","git clone --recurse-submodules https:\u002F\u002Fgithub.com\u002Flife-and-dev\u002Fmdsite.git\n# or, if already cloned:\ngit submodule update --init --recursive\n","bash",[27,473,474,489,494],{"__ignoreMap":34},[140,475,476,479,482,486],{"class":142,"line":143},[140,477,478],{"class":146},"git",[140,480,481],{"class":150}," clone",[140,483,485],{"class":484},"sj4cs"," --recurse-submodules",[140,487,488],{"class":150}," https:\u002F\u002Fgithub.com\u002Flife-and-dev\u002Fmdsite.git\n",[140,490,491],{"class":142,"line":219},[140,492,493],{"class":228},"# or, if already cloned:\n",[140,495,496,498,501,504,507],{"class":142,"line":232},[140,497,478],{"class":146},[140,499,500],{"class":150}," submodule",[140,502,503],{"class":150}," update",[140,505,506],{"class":484}," --init",[140,508,509],{"class":484}," --recursive\n",[23,511,512],{},"To update the renderer to a newer version:",[132,514,516],{"className":469,"code":515,"language":471,"meta":34,"style":34},"cd mdsite-nuxt\ngit fetch origin\ngit checkout \u003Cdesired-commit-or-tag>\ncd ..\ngit add mdsite-nuxt\ngit commit -m \"chore: bump mdsite-nuxt submodule\"\n",[27,517,518,526,536,557,564,573],{"__ignoreMap":34},[140,519,520,523],{"class":142,"line":143},[140,521,522],{"class":484},"cd",[140,524,525],{"class":150}," mdsite-nuxt\n",[140,527,528,530,533],{"class":142,"line":219},[140,529,478],{"class":146},[140,531,532],{"class":150}," fetch",[140,534,535],{"class":150}," origin\n",[140,537,538,540,543,547,550,554],{"class":142,"line":232},[140,539,478],{"class":146},[140,541,542],{"class":150}," checkout",[140,544,546],{"class":545},"szBVR"," \u003C",[140,548,549],{"class":150},"desired-commit-or-ta",[140,551,553],{"class":552},"sVt8B","g",[140,555,556],{"class":545},">\n",[140,558,559,561],{"class":142,"line":243},[140,560,522],{"class":484},[140,562,563],{"class":150}," ..\n",[140,565,566,568,571],{"class":142,"line":258},[140,567,478],{"class":146},[140,569,570],{"class":150}," add",[140,572,525],{"class":150},[140,574,575,577,580,583],{"class":142,"line":271},[140,576,478],{"class":146},[140,578,579],{"class":150}," commit",[140,581,582],{"class":484}," -m",[140,584,585],{"class":150}," \"chore: bump mdsite-nuxt submodule\"\n",[23,587,588],{},"The parent repo records the submodule by commit SHA, so this bump is a normal commit on the parent.",[23,590,591,592,594],{},"See ",[32,593,78],{"href":77}," for how to customize the renderer or extend it with custom Nuxt components.",[596,597,599],"h3",{"id":598},"working-with-the-submodule","Working with the submodule",[23,601,602],{},"After a fresh clone of this repo, initialise the submodule:",[116,604,606],{"id":605},"_3-cli-architecture","3. CLI architecture",[23,608,609,610,613],{},"The CLI is a thin orchestrator. It does not contain any rendering logic — all rendering happens inside ",[27,611,612],{},"mdsite-nuxt",". The CLI's job is to:",[43,615,616,623,626,629],{},[46,617,618,619,622],{},"Read ",[27,620,621],{},"mdsite.yml"," from the content directory.",[46,624,625],{},"Resolve which renderer directory to use.",[46,627,628],{},"Write compatibility artifacts into that renderer directory.",[46,630,631,632,168,635,168,638,641],{},"Spawn the renderer's npm scripts (",[27,633,634],{},"dev",[27,636,637],{},"generate",[27,639,640],{},"preview",") with the right environment.",[23,643,644,645,648,649,168,652,655,656,658],{},"Each module ships next to a ",[27,646,647],{},"*.test.ts"," file (for example ",[27,650,651],{},"src\u002Findex.test.ts",[27,653,654],{},"src\u002Fcommands\u002Fprepare.test.ts","). See ",[32,657,85],{"href":84}," for the full picture.",[596,660,662],{"id":661},"module-breakdown","Module breakdown",[664,665,666,679],"table",{},[667,668,669],"thead",{},[670,671,672,676],"tr",{},[673,674,675],"th",{},"Path",[673,677,678],{},"Responsibility",[680,681,682,697,707,743,764],"tbody",{},[670,683,684,690],{},[685,686,687],"td",{},[27,688,689],{},"src\u002Findex.ts",[685,691,692,693,696],{},"CLI entrypoint. Parses ",[27,694,695],{},"process.argv",", dispatches to a command handler.",[670,698,699,704],{},[685,700,701],{},[27,702,703],{},"src\u002Fcommands\u002F",[685,705,706],{},"One file per command. Each handler takes a content directory (and options) and returns a status string.",[670,708,709,714],{},[685,710,711],{},[27,712,713],{},"src\u002Fconfig\u002F",[685,715,716,717,720,721,724,725,728,729,732,733,735,736,728,739,742],{},"Defines the ",[27,718,719],{},"MdsiteConfig"," schema, default values produced by ",[27,722,723],{},"init",", and the ",[27,726,727],{},"menu"," and ",[27,730,731],{},"footer"," parsers for ",[27,734,621],{},"'s ",[27,737,738],{},"menu:",[27,740,741],{},"footer:"," sections.",[670,744,745,750],{},[685,746,747],{},[27,748,749],{},"src\u002Fprocess\u002F",[685,751,752,755,756,759,760,763],{},[27,753,754],{},"child-process.ts"," wraps foreground and background spawning. ",[27,757,758],{},"runtime-state.ts"," writes tracked-detached runtime state (PIDs\u002Flogs) into the renderer working dir (",[27,761,762],{},"\u003Cpaths.build>",").",[670,765,766,771],{},[685,767,768],{},[27,769,770],{},"src\u002Frenderer\u002F",[685,772,773,776,777,728,780,783],{},[27,774,775],{},"mdsite-nuxt.ts"," is the bridge: it resolves the renderer directory, installs its dependencies if missing, writes ",[27,778,779],{},".env",[27,781,782],{},"content.config.yml",", and invokes the right npm script.",[116,785,787],{"id":786},"_4-how-the-cli-and-nuxt-integrate","4. How the CLI and Nuxt integrate",[23,789,790,791,168,794,797,798,801],{},"When a user runs ",[27,792,793],{},"mdsite live",[27,795,796],{},"mdsite generate",", or ",[27,799,800],{},"mdsite static"," from a content directory, the CLI performs the same preparation phase before invoking the renderer:",[132,803,807],{"className":804,"code":805,"language":806,"meta":34,"style":34},"language-mermaid shiki shiki-themes github-light github-dark","graph TB\n    A[mdsite.yml + Markdown] --> B[Read MdsiteConfig]\n    B --> C[Resolve renderer dir]\n    C --> D[Write content.config.yml + .env]\n    D --> E[Install renderer deps if missing]\n    E --> F[Run npm script: dev \u002F generate \u002F preview]\n    F --> G[Browser or .output\u002Fpublic]\n","mermaid",[27,808,809,814,819,824,829,834,839],{"__ignoreMap":34},[140,810,811],{"class":142,"line":143},[140,812,813],{},"graph TB\n",[140,815,816],{"class":142,"line":219},[140,817,818],{},"    A[mdsite.yml + Markdown] --> B[Read MdsiteConfig]\n",[140,820,821],{"class":142,"line":232},[140,822,823],{},"    B --> C[Resolve renderer dir]\n",[140,825,826],{"class":142,"line":243},[140,827,828],{},"    C --> D[Write content.config.yml + .env]\n",[140,830,831],{"class":142,"line":258},[140,832,833],{},"    D --> E[Install renderer deps if missing]\n",[140,835,836],{"class":142,"line":271},[140,837,838],{},"    E --> F[Run npm script: dev \u002F generate \u002F preview]\n",[140,840,841],{"class":142,"line":284},[140,842,843],{},"    F --> G[Browser or .output\u002Fpublic]\n",[664,845,846,862],{},[667,847,848],{},[670,849,850,853,856,859],{},[673,851,852],{},"CLI command",[673,854,855],{},"Renderer script",[673,857,858],{},"Foreground",[673,860,861],{},"Background",[680,863,864,883,898],{},[670,865,866,870,874,877],{},[685,867,868],{},[27,869,793],{},[685,871,872],{},[27,873,634],{},[685,875,876],{},"yes",[685,878,879,880],{},"with ",[27,881,882],{},"-d",[670,884,885,889,893,895],{},[685,886,887],{},[27,888,796],{},[685,890,891],{},[27,892,637],{},[685,894,876],{},[685,896,897],{},"—",[670,899,900,904,908,910],{},[685,901,902],{},[27,903,800],{},[685,905,906],{},[27,907,640],{},[685,909,876],{},[685,911,879,912],{},[27,913,882],{},[23,915,916,917,919,920,923,924,927],{},"Background runs are tracked via state files in the renderer working dir (",[27,918,762],{},", e.g. ",[27,921,922],{},".mdsite\u002F",") so ",[27,925,926],{},"mdsite stop"," can find and terminate them.",[23,929,930,931,934,935,938,939,942,943,946],{},"When the configured port is occupied, Nuxt falls back to the next free one. The CLI detects the actual port by tailing ",[27,932,933],{},"live.log","\u002F",[27,936,937],{},"static.log"," for the ",[27,940,941],{},"Local:"," (dev) or ",[27,944,945],{},"Listening on"," (Nitro preview) URL line, then waits for and opens that port instead of the configured one. If the log line never appears before the readiness timeout, the CLI falls back to the configured port.",[596,948,950],{"id":949},"step-1-read-config","Step 1: Read config",[23,952,953,956,957,959,960,962,963,966,967,969],{},[27,954,955],{},"src\u002Fconfig\u002Fmdsite-config.ts"," parses ",[27,958,621],{}," into a typed ",[27,961,719],{},". Defaults come from ",[27,964,965],{},"src\u002Fconfig\u002Fdefault-mdsite-config.ts",", which ",[27,968,723],{}," uses to seed a new file.",[596,971,973],{"id":972},"step-2-resolve-the-renderer-directory","Step 2: Resolve the renderer directory",[23,975,976,977,435],{},"Renderer resolution is ",[50,978,979],{},"dev-aware",[71,981,982,1002,1034],{},[46,983,984,987,988,990,991,994,995,998,999,1001],{},[50,985,986],{},"Dev repo \u002F submodule in place"," — when the bundled renderer is the local ",[27,989,65],{}," submodule and is NOT inside ",[27,992,993],{},"node_modules"," (the normal case in this repo), the CLI runs it ",[50,996,997],{},"in place",", so live-editing of ",[27,1000,65],{}," keeps working.",[46,1003,1004,1010,1011,1013,1014,1017,1018,1021,1022,1025,1026,1029,1030,1033],{},[50,1005,1006,1007,1009],{},"End users (",[27,1008,993],{},")"," — when the bundled renderer lives inside ",[27,1012,993],{}," (an ",[27,1015,1016],{},"npm install"," \u002F ",[27,1019,1020],{},"npx"," \u002F CI run), the CLI ",[50,1023,1024],{},"materializes"," a copy into ",[27,1027,1028],{},"\u003Ccontent-dir>\u002F\u003Cpaths.build>"," (default ",[27,1031,1032],{},".mdsite",") and runs there.",[46,1035,1036,1037,1039,1040,1042],{},"If the resolved renderer directory has no ",[27,1038,993],{},", the CLI runs ",[27,1041,1016],{}," in it.",[23,1044,1045,1048,1049,1051],{},[27,1046,1047],{},"mdsite prepare github"," never clones or pulls the renderer itself; the generated workflow is self-adapting and still expects ",[27,1050,762],{}," to be materializable in CI.",[596,1053,1055],{"id":1054},"step-3-write-compatibility-artifacts","Step 3: Write compatibility artifacts",[23,1057,1058],{},"The CLI writes two files into the resolved renderer directory so the renderer can find the content:",[71,1060,1061,1066],{},[46,1062,1063,1065],{},[27,1064,782],{}," — serialized site name, canonical URL, content path, git repo, features, and themes.",[46,1067,1068,1070,1071,168,1074,406,1077,1080],{},[27,1069,779],{}," — ",[27,1072,1073],{},"NUXT_CONTENT_PATH",[27,1075,1076],{},"CONTENT_DIR",[27,1078,1079],{},"MDSITE_CONFIG_PATH"," so the renderer reads directly from the user's content directory without copying files.",[23,1082,1083,1084,1086,1087,1089,1090,763],{},"The renderer reads the ",[27,1085,738],{}," section directly from ",[27,1088,621],{}," (via ",[27,1091,1079],{},[596,1093,1095],{"id":1094},"step-4-ensure-dependencies","Step 4: Ensure dependencies",[23,1097,1098,1099,1039,1101,1103,1104,1106,1107,1109,1110,1112],{},"If the renderer directory has no ",[27,1100,993],{},[27,1102,125],{}," (when a lockfile is present) or ",[27,1105,1016],{}," inside it. This is why the published package ships ",[27,1108,65],{}," without ",[27,1111,993],{}," — the install happens on first run.",[596,1114,1116],{"id":1115},"step-5-run-the-renderer-script","Step 5: Run the renderer script",[23,1118,1119,1120,1123,1124,1127],{},"Finally, the CLI spawns ",[27,1121,1122],{},"npm run \u003Cscript>"," inside the renderer directory, where ",[27,1125,1126],{},"\u003Cscript>"," is one of:",[116,1129,1131],{"id":1130},"_5-configuration-model","5. Configuration model",[23,1133,1134,1136],{},[27,1135,621],{}," is the only configuration file end users edit. The full schema is documented in the user-facing docs. From a developer perspective, the interesting pieces are:",[71,1138,1139,1144,1152,1175],{},[46,1140,1141,1143],{},[27,1142,955],{}," — the TypeScript schema and parser.",[46,1145,1146,1148,1149,1151],{},[27,1147,965],{}," — defaults applied by ",[27,1150,723],{},", including theme palette and feature flags.",[46,1153,1154,1157,1158,180,1161,1164,1165,1167,1168,1171,1172,97],{},[27,1155,1156],{},"src\u002Fconfig\u002Fmenu.ts"," — the menu parser. Menu items are markdown file references; submenu keys (like ",[27,1159,1160],{},"features:",[27,1162,1163],{},"develop:",") double as folder prefixes, so ",[27,1166,1160],{}," with item ",[27,1169,1170],{},"bible-tooltips"," resolves to ",[27,1173,1174],{},"docs\u002Ffeatures\u002Fbible-tooltips.md",[46,1176,1177,1179,1180,1183,1184,1187,1188,1190,1191,1194,1195,1197,1198,1201,1202,1205,1206,1209,1210,1213,1214,1217,1218,1220,1221,1224,1225,728,1228,1231],{},[27,1178,955],{}," ",[27,1181,1182],{},"features.footer"," — the footer parser. The footer lives under the ",[27,1185,1186],{},"features"," block in ",[27,1189,621],{}," (i.e. ",[27,1192,1193],{},"features.footer: [...]",") and mirrors the ",[27,1196,727],{}," item shape (",[27,1199,1200],{},"FooterItem = string | null | { [key: string]: string | null }",") but ",[50,1203,1204],{},"without"," the recursive sub-menu case — values inside an object entry are always a ",[27,1207,1208],{},"string"," (file path or ",[27,1211,1212],{},"http(s):\u002F\u002F"," URL) or ",[27,1215,1216],{},"null"," (separator). Internal files listed here are also removed from the generated ",[27,1219,727],{}," tree by ",[27,1222,1223],{},"mdsite-nuxt\u002Fscripts\u002Fgenerate-indices.ts"," (see ",[27,1226,1227],{},"filterTreeByExcludedPaths",[27,1229,1230],{},"extractInternalPath","); the two parsers must therefore stay in sync. External URLs and separators are ignored by the exclusion pass.",[116,1233,1235],{"id":1234},"_6-build-artifacts-and-runtime-state","6. Build artifacts and runtime state",[664,1237,1238,1250],{},[667,1239,1240],{},[670,1241,1242,1244,1247],{},[673,1243,675],{},[673,1245,1246],{},"Written by",[673,1248,1249],{},"Purpose",[680,1251,1252,1270,1302,1331,1366,1380,1399],{},[670,1253,1254,1258,1264],{},[685,1255,1256],{},[27,1257,403],{},[685,1259,1260,1263],{},[27,1261,1262],{},"npm run build"," (tsc)",[685,1265,1266,1267,97],{},"Compiled CLI loaded by ",[27,1268,1269],{},"bin\u002Fmdsite.js",[670,1271,1272,1280,1288],{},[685,1273,1274,1277,1278,1009],{},[27,1275,1276],{},".\u003Cpaths.build>\u002F"," (e.g. ",[27,1279,922],{},[685,1281,1282,1017,1285],{},[27,1283,1284],{},"start -d",[27,1286,1287],{},"preview -d",[685,1289,1290,1291,934,1294,1297,1298,934,1300,97],{},"Tracked-detached runtime state: ",[27,1292,1293],{},"live.json",[27,1295,1296],{},"static.json"," (PIDs, ports) + ",[27,1299,933],{},[27,1301,937],{},[670,1303,1304,1312,1318],{},[685,1305,1306,1277,1309,1009],{},[27,1307,1308],{},".\u003Cpaths.build>\u002F.output\u002F",[27,1310,1311],{},".mdsite\u002F.output\u002F",[685,1313,1314,1017,1316],{},[27,1315,796],{},[27,1317,800],{},[685,1319,1320,1321,1323,1324,1327,1328,97],{},"Nitro's build output (the prerendered site + assets). ",[27,1322,796],{}," then copies ",[27,1325,1326],{},".\u003Cpaths.build>\u002F.output\u002Fpublic\u002F"," to ",[27,1329,1330],{},".\u003Cpaths.output>\u002Fpublic\u002F",[670,1332,1333,1341,1344],{},[685,1334,1335,1277,1338,1009],{},[27,1336,1337],{},".\u003Cpaths.build>\u002Fpublic\u002F",[27,1339,1340],{},".mdsite\u002Fpublic\u002F",[685,1342,1343],{},"renderer favicon pipeline",[685,1345,1346,1347,934,1350,934,1353,934,1356,934,1359,1362,1363,97],{},"Generated ",[27,1348,1349],{},"favicon.svg",[27,1351,1352],{},"favicon.ico",[27,1354,1355],{},"apple-touch-icon.png",[27,1357,1358],{},"icon-192.png",[27,1360,1361],{},"icon-512.png"," + ",[27,1364,1365],{},"site.webmanifest",[670,1367,1368,1372,1377],{},[685,1369,1370],{},[27,1371,1330],{},[685,1373,1374,1376],{},[27,1375,796],{}," (via renderer)",[685,1378,1379],{},"The static site, ready to deploy.",[670,1381,1382,1387,1390],{},[685,1383,1384],{},[27,1385,1386],{},"mdsite-nuxt\u002F.env",[685,1388,1389],{},"every CLI run",[685,1391,1392,1393,1396,1397,97],{},"Points the renderer at the active content directory; in dev also sets ",[27,1394,1395],{},"MDSITE_NITRO_OUTPUT_DIR"," to redirect the build output to ",[27,1398,1308],{},[670,1400,1401,1406,1408],{},[685,1402,1403],{},[27,1404,1405],{},"mdsite-nuxt\u002Fcontent.config.yml",[685,1407,1389],{},[685,1409,1410],{},"Serialized site config consumed by the renderer.",[23,1412,1413,728,1415,1418,1419,1422,1423,1425,1426,1429,1430,728,1432,1435,1436,1438,1439,97],{},[27,1414,762],{},[27,1416,1417],{},"\u003Cpaths.output>"," are resolved relative to the ",[50,1420,1421],{},"project root"," (the directory containing ",[27,1424,621],{},"), not the markdown content directory or the cwd. With ",[27,1427,1428],{},"paths.input: docs",", the build artifacts live in ",[27,1431,922],{},[27,1433,1434],{},".output\u002F"," at the project root alongside ",[27,1437,621],{},"; only the markdown sources live in ",[27,1440,1441],{},"docs\u002F",[23,1443,1444,1445,1447,1448,728,1450,1452,1453,1455,1456,1458],{},"In dev mode (when the CLI is run from this repo), the renderer IS the checked-in ",[27,1446,65],{}," submodule, so ",[27,1449,1386],{},[27,1451,1405],{}," are written there. The CLI sets ",[27,1454,1395],{}," in the env so Nitro writes to ",[27,1457,1308],{}," instead of inside the submodule — the submodule itself stays free of build artifacts.",[23,1460,1461,1462,1464,1465,1467,1468,180,1470,1473,1474,1477,1478,1480,1481,1483,1484,1029,1486,728,1488,1490,1491,1493,1494,763],{},"Most of these are gitignored. ",[27,1463,922],{}," is fully gitignored and the renderer source is materialized here from the bundled ",[27,1466,65],{}," on every CLI run; nothing under ",[27,1469,1276],{},[27,1471,1472],{},".\u003Cpaths.output>\u002F"," should be committed. ",[27,1475,1476],{},"mdsite clean"," is the user-facing way to delete ",[27,1479,1276],{}," (which includes its ",[27,1482,1434],{}," and runtime state files) and ",[27,1485,1472],{},[27,1487,922],{},[27,1489,1434],{},"). It refuses to run while a tracked start\u002Fpreview process is alive, so the usual pre-cleanup is ",[27,1492,926],{}," (or the foreground ",[27,1495,1496],{},"Ctrl+C",[71,1498,1499,1513,1525],{},[46,1500,1501,1502,1179,1505,1508,1509,1512],{},"No ",[27,1503,1504],{},"actions\u002Fsetup-node",[27,1506,1507],{},"cache: npm"," and no ",[27,1510,1511],{},"actions\u002Fcache@v5"," step.",[46,1514,1515,1516,1518,1519,1521,1522,1512],{},"Every CI run does a clean ",[27,1517,1016],{}," via ",[27,1520,796],{},"'s built-in ",[27,1523,1524],{},"ensureRendererDependencies",[46,1526,1527,1529,1530,1532,1533,1536,1537,1539],{},[27,1528,796],{}," materializes the renderer from the bundled ",[27,1531,65],{}," (creating ",[27,1534,1535],{},".mdsite\u002Fpackage-lock.json"," from the CLI's own lockfile), then runs ",[27,1538,125],{}," against it.",[23,1541,1542,1545,1546,1548],{},[50,1543,1544],{},"Why no cache:"," The ",[27,1547,29],{}," CLI bundles the renderer, so the dependency tree is determined by the CLI release, not by anything the user commits. A cache keyed on a non-existent user-controlled lockfile would either fail (Setup Node throws when the file is missing) or be shared across unrelated repos on the same CLI version (serving stale dependencies after a CLI upgrade). The CLI does not optimize CI install performance — that's the user's CI tuning concern. The default workflow is simple, correct, and version-agnostic.",[596,1550,1552,1553,1009],{"id":1551},"generated-ci-workflow-mdsite-prepare-github","Generated CI workflow (",[27,1554,1047],{},[23,1556,1557,1559,1560,1563,1564,435],{},[27,1558,1047],{}," writes ",[27,1561,1562],{},".github\u002Fworkflows\u002Fdeploy.yml",". The generated workflow is ",[50,1565,1566],{},"deliberately cache-free",[116,1568,1570],{"id":1569},"_7-technology-stack","7. Technology stack",[71,1572,1573,1583,1593,1599],{},[46,1574,1575,1578,1579,1582],{},[50,1576,1577],{},"CLI",": TypeScript, Node.js standard library, ",[27,1580,1581],{},"yaml"," for config parsing.",[46,1584,1585,1588,1589,1592],{},[50,1586,1587],{},"Renderer",": Nuxt 4 (Vue 3), Vuetify (Material Design), ",[27,1590,1591],{},"@nuxt\u002Fcontent",", Vite.",[46,1594,1595,1598],{},[50,1596,1597],{},"Tests",": Vitest for the CLI; the renderer has its own Vitest and Playwright configs.",[46,1600,1601,1604,1605,1607],{},[50,1602,1603],{},"Build",": ",[27,1606,129],{}," for the CLI; Nuxt\u002FVite for the renderer.",[1609,1610,1611],"style",{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}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);}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}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}",{"title":34,"searchDepth":219,"depth":219,"links":1613},[1614,1615,1616,1619,1622,1629,1630,1634],{"id":118,"depth":219,"text":119},{"id":205,"depth":219,"text":206},{"id":420,"depth":219,"text":421,"children":1617},[1618],{"id":598,"depth":232,"text":599},{"id":605,"depth":219,"text":606,"children":1620},[1621],{"id":661,"depth":232,"text":662},{"id":786,"depth":219,"text":787,"children":1623},[1624,1625,1626,1627,1628],{"id":949,"depth":232,"text":950},{"id":972,"depth":232,"text":973},{"id":1054,"depth":232,"text":1055},{"id":1094,"depth":232,"text":1095},{"id":1115,"depth":232,"text":1116},{"id":1130,"depth":219,"text":1131},{"id":1234,"depth":219,"text":1235,"children":1631},[1632],{"id":1551,"depth":232,"text":1633},"Generated CI workflow (mdsite prepare github)",{"id":1569,"depth":219,"text":1570},"This page is for contributors working on mdsite itself. End users do not need to read this; they should follow Install and the user-facing tutorials.","md",{},"\u002Fdevelop",{"title":14,"description":1635},"develop","tOopLp5h33VbLCbpqV6N_Q566zoJMq7qVVPHo2fL6P4",1784017753069]