@@ -16,6 +16,8 @@ import title from "https://deno.land/x/lume_markdown_plugins@v0.7.0/title.ts";
1616import toc from "https://deno.land/x/lume_markdown_plugins@v0.7.0/toc.ts" ;
1717// See note below about GFM CSS
1818// import { CSS as GFM_CSS } from "https://jsr.io/@deno/gfm/0.11.0/style.ts";
19+ import { walk } from "@std/fs" ;
20+ import { dirname } from "@std/path" ;
1921import { log } from "lume/core/utils/log.ts" ;
2022import anchor from "npm:markdown-it-anchor@9" ;
2123import admonitionPlugin from "./markdown-it/admonition.ts" ;
@@ -28,6 +30,7 @@ import createRoutingMiddleware from "./middleware/functionRoutes.ts";
2830import createGAMiddleware from "./middleware/googleAnalytics.ts" ;
2931import redirectsMiddleware from "./middleware/redirects.ts" ;
3032import createLlmsFilesMiddleware from "./middleware/llmsFiles.ts" ;
33+ import createMarkdownSourceMiddleware from "./middleware/markdownSource.ts" ;
3134import { toFileAndInMemory } from "./utils/redirects.ts" ;
3235import { cliNow } from "./timeUtils.ts" ;
3336
@@ -73,6 +76,7 @@ const site = lume(
7376 server : {
7477 middlewares : [
7578 redirectsMiddleware ,
79+ createMarkdownSourceMiddleware ( { root : "_site" } ) ,
7680 createRoutingMiddleware ( ) ,
7781 createGAMiddleware ( {
7882 addr : { transport : "tcp" , hostname : "localhost" , port : 3000 } ,
@@ -146,7 +150,6 @@ site.copy("runtime/reference/images");
146150site . copy ( "runtime/contributing/images" ) ;
147151site . copy ( "examples/tutorials/images" ) ;
148152site . copy ( "deploy/manual/images" ) ;
149- site . copy ( "deploy/images" ) ;
150153site . copy ( "examples/scripts" ) ;
151154
152155site . use (
@@ -187,6 +190,24 @@ site.addEventListener("afterBuild", async () => {
187190 /* NOTE: we used to get gfm.css from the jsr.io CDN, but now we simply have a local copy. This is because it needs to be placed on a CSS layer, which isn't possible with an imported file. */
188191 // Deno.writeTextFileSync(site.dest("gfm.css"), GFM_CSS);
189192
193+ // Copy lint rule markdown source files to _site so they're served at /lint/rules/*.md.
194+ // These files are excluded from Lume's processing pipeline (site.ignore) because
195+ // lint_rule.page.tsx handles page generation, but we still want the raw .md accessible.
196+ try {
197+ await Deno . mkdir ( site . dest ( "lint/rules" ) , { recursive : true } ) ;
198+ for await ( const entry of Deno . readDir ( "lint/rules" ) ) {
199+ if ( entry . isFile && entry . name . endsWith ( ".md" ) ) {
200+ await Deno . copyFile (
201+ `lint/rules/${ entry . name } ` ,
202+ site . dest ( `lint/rules/${ entry . name } ` ) ,
203+ ) ;
204+ }
205+ }
206+ log . info ( "Copied lint rule markdown files to _site/lint/rules/" ) ;
207+ } catch ( error ) {
208+ log . error ( "Error copying lint rule markdown files: " + error ) ;
209+ }
210+
190211 // Generate LLMs documentation files directly to _site directory
191212 if ( Deno . env . get ( "BUILD_TYPE" ) == "FULL" ) {
192213 try {
@@ -231,6 +252,49 @@ site.addEventListener("afterBuild", async () => {
231252 log . error ( "Error generating LLMs files:" + error ) ;
232253 }
233254 }
255+
256+ // Copy source .md files to _site so AI agents can request them directly.
257+ // Excludes "reference/" (dynamically generated, no static .md source files).
258+ const contentDirs = [
259+ "runtime" ,
260+ "deploy" ,
261+ "sandbox" ,
262+ "subhosting" ,
263+ "examples" ,
264+ ] ;
265+ let mdCopied = 0 ;
266+ let mdErrors = false ;
267+ for ( const dir of contentDirs ) {
268+ // Skip directories that don't exist in this build
269+ try {
270+ await Deno . stat ( dir ) ;
271+ } catch ( error ) {
272+ if ( ! ( error instanceof Deno . errors . NotFound ) ) {
273+ log . error ( `Error accessing content directory ${ dir } : ${ error } ` ) ;
274+ }
275+ continue ;
276+ }
277+ try {
278+ for await (
279+ const entry of walk ( dir , { exts : [ ".md" ] , includeDirs : false } )
280+ ) {
281+ const destPath = site . dest ( entry . path ) ;
282+ await Deno . mkdir ( dirname ( destPath ) , { recursive : true } ) ;
283+ await Deno . copyFile ( entry . path , destPath ) ;
284+ mdCopied ++ ;
285+ }
286+ } catch ( error ) {
287+ log . error ( `Error copying markdown files from ${ dir } : ${ error } ` ) ;
288+ mdErrors = true ;
289+ }
290+ }
291+ if ( mdErrors ) {
292+ log . warn (
293+ `Copied ${ mdCopied } source markdown files to _site (some directories had errors, see above)` ,
294+ ) ;
295+ } else {
296+ log . info ( `Copied ${ mdCopied } source markdown files to _site` ) ;
297+ }
234298} ) ;
235299
236300site . copy ( "reference_gen/gen/deno/page.css" , "/api/deno/page.css" ) ;
@@ -308,7 +372,7 @@ site.data("apiCategories", {
308372} ) ;
309373
310374// Do more expensive operations if we're building the full site
311- if ( Deno . env . get ( "BUILD_TYPE" ) == "FULL" ) {
375+ if ( Deno . env . get ( "BUILD_TYPE" ) == "FULL" && ! Deno . env . has ( "SKIP_OG" ) ) {
312376 // Use Lume's built in date function to get the last modified date of the file
313377 // site.data("date", "Git Last Modified");;
314378
0 commit comments