next-agent-md 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +166 -0
- package/dist/cli.cjs +274 -0
- package/dist/cli.cjs.map +1 -0
- package/dist/cli.d.cts +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +251 -0
- package/dist/cli.js.map +1 -0
- package/dist/config-plugin.cjs +86 -0
- package/dist/config-plugin.cjs.map +1 -0
- package/dist/config-plugin.d.cts +31 -0
- package/dist/config-plugin.d.ts +31 -0
- package/dist/config-plugin.js +51 -0
- package/dist/config-plugin.js.map +1 -0
- package/dist/index.cjs +7163 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +73 -0
- package/dist/index.d.ts +73 -0
- package/dist/index.js +7158 -0
- package/dist/index.js.map +1 -0
- package/package.json +74 -0
package/README.md
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# next-agent-md
|
|
2
|
+
|
|
3
|
+
Next.js Edge Middleware that serves Markdown to AI agents — bringing [Cloudflare's Markdown for Agents](https://blog.cloudflare.com/markdown-for-agents/) pattern to any Next.js app.
|
|
4
|
+
|
|
5
|
+
When an AI agent sends `Accept: text/markdown`, the middleware intercepts the request, converts the page HTML to clean Markdown, and returns it with an `x-markdown-tokens` header. This reduces token usage by ~80%.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install next-agent-md
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick start
|
|
14
|
+
|
|
15
|
+
The fastest way to get started:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npx next-agent-md init
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
This scaffolds the correct file for your Next.js version and that's it.
|
|
22
|
+
|
|
23
|
+
> **Next.js version note**
|
|
24
|
+
> Next.js 16+ renamed the middleware file convention from `middleware.ts` to `proxy.ts`.
|
|
25
|
+
> The `init` command detects your version automatically and creates the right file.
|
|
26
|
+
> All code examples below use `proxy.ts` — replace with `middleware.ts` if you're on Next.js ≤ 15.
|
|
27
|
+
|
|
28
|
+
## Manual setup
|
|
29
|
+
|
|
30
|
+
### 1. Add the proxy/middleware file
|
|
31
|
+
|
|
32
|
+
**Next.js 16+** (`proxy.ts`):
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
// proxy.ts
|
|
36
|
+
import { withMarkdownForAgents } from 'next-agent-md'
|
|
37
|
+
|
|
38
|
+
export default withMarkdownForAgents()
|
|
39
|
+
|
|
40
|
+
export const config = {
|
|
41
|
+
matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
**Next.js ≤ 15** (`middleware.ts`):
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
// middleware.ts
|
|
49
|
+
import { withMarkdownForAgents } from 'next-agent-md'
|
|
50
|
+
|
|
51
|
+
export default withMarkdownForAgents()
|
|
52
|
+
|
|
53
|
+
export const config = {
|
|
54
|
+
matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
**Wrapping existing middleware:**
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
// proxy.ts (or middleware.ts on Next.js ≤ 15)
|
|
62
|
+
import { withMarkdownForAgents } from 'next-agent-md'
|
|
63
|
+
import { myAuthMiddleware } from './lib/auth'
|
|
64
|
+
|
|
65
|
+
export default withMarkdownForAgents(myAuthMiddleware, {
|
|
66
|
+
contentSignal: { aiTrain: true, search: true, aiInput: true },
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
export const config = {
|
|
70
|
+
matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### 2. Add the next.config plugin (optional but recommended)
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
// next.config.ts
|
|
78
|
+
import { withAgentMd } from 'next-agent-md/config'
|
|
79
|
+
|
|
80
|
+
export default withAgentMd()(nextConfig)
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
The config plugin:
|
|
84
|
+
|
|
85
|
+
- Injects `Vary: accept` on all non-static routes so CDNs cache HTML and Markdown separately
|
|
86
|
+
- Warns at startup if no `proxy.ts` or `middleware.ts` is found, with a pointer to `npx next-agent-md init`
|
|
87
|
+
|
|
88
|
+
**Composing with other plugins:**
|
|
89
|
+
|
|
90
|
+
```ts
|
|
91
|
+
import { withAgentMd } from 'next-agent-md/config'
|
|
92
|
+
import bundleAnalyzer from '@next/bundle-analyzer'
|
|
93
|
+
|
|
94
|
+
const withAnalyzer = bundleAnalyzer({ enabled: process.env.ANALYZE === 'true' })
|
|
95
|
+
|
|
96
|
+
export default withAgentMd()(withAnalyzer(nextConfig))
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## How it works
|
|
100
|
+
|
|
101
|
+
1. Incoming request has `Accept: text/markdown`
|
|
102
|
+
2. Middleware self-fetches the same URL with `Accept: text/html` + an internal skip header (loop prevention)
|
|
103
|
+
3. Strips boilerplate HTML: `<nav>`, `<header>`, `<footer>`, `<aside>`, `<script>`, `<style>`, etc.
|
|
104
|
+
4. Converts cleaned HTML to Markdown via [`node-html-markdown`](https://github.com/crosstype/node-html-markdown)
|
|
105
|
+
5. Returns a `text/markdown` response with:
|
|
106
|
+
- `x-markdown-tokens` — estimated token count (`chars / 4`)
|
|
107
|
+
- `vary: accept` — tells CDNs to cache HTML and Markdown versions separately
|
|
108
|
+
- `content-signal` — optional AI usage permissions header (see options)
|
|
109
|
+
|
|
110
|
+
## Options
|
|
111
|
+
|
|
112
|
+
```ts
|
|
113
|
+
interface MarkdownAgentsOptions {
|
|
114
|
+
/** Header used to prevent infinite self-fetch loops. Default: 'x-markdown-skip' */
|
|
115
|
+
skipHeader?: string
|
|
116
|
+
|
|
117
|
+
/** Add `Vary: accept` to markdown responses. Default: true */
|
|
118
|
+
varyHeader?: boolean
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Controls the Content-Signal response header.
|
|
122
|
+
* Pass true to enable all signals, or an object for fine-grained control.
|
|
123
|
+
* Default: false (header not sent)
|
|
124
|
+
*
|
|
125
|
+
* @example contentSignal: true
|
|
126
|
+
* // → Content-Signal: ai-train=yes, search=yes, ai-input=yes
|
|
127
|
+
*
|
|
128
|
+
* @example contentSignal: { aiTrain: false, search: true, aiInput: true }
|
|
129
|
+
* // → Content-Signal: ai-train=no, search=yes, ai-input=yes
|
|
130
|
+
*/
|
|
131
|
+
contentSignal?: boolean | {
|
|
132
|
+
aiTrain?: boolean // Default: true
|
|
133
|
+
search?: boolean // Default: true
|
|
134
|
+
aiInput?: boolean // Default: true
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** Extra HTML tag names to strip before conversion. Default: [] */
|
|
138
|
+
stripSelectors?: string[]
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Testing
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
# Test Accept header
|
|
146
|
+
curl -H "Accept: text/markdown" http://localhost:3000/
|
|
147
|
+
|
|
148
|
+
# Check response headers
|
|
149
|
+
curl -sI -H "Accept: text/markdown" http://localhost:3000/ \
|
|
150
|
+
| grep -E "content-type|x-markdown-tokens|vary|content-signal"
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## Requirements
|
|
154
|
+
|
|
155
|
+
- Next.js ≥ 13.0.0 (tested on 15 and 16)
|
|
156
|
+
- Node.js ≥ 18.0.0
|
|
157
|
+
- Works in **Edge Runtime** (default for Next.js middleware/proxy)
|
|
158
|
+
|
|
159
|
+
| Next.js version | File convention |
|
|
160
|
+
| --- | --- |
|
|
161
|
+
| ≤ 15 | `middleware.ts` |
|
|
162
|
+
| 16+ | `proxy.ts` |
|
|
163
|
+
|
|
164
|
+
## License
|
|
165
|
+
|
|
166
|
+
MIT
|
package/dist/cli.cjs
ADDED
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
18
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
19
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
20
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
21
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
22
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
23
|
+
mod
|
|
24
|
+
));
|
|
25
|
+
|
|
26
|
+
// src/build.ts
|
|
27
|
+
var import_node_fs = __toESM(require("fs"), 1);
|
|
28
|
+
var import_node_path = __toESM(require("path"), 1);
|
|
29
|
+
|
|
30
|
+
// src/strip-boilerplate.ts
|
|
31
|
+
var DEFAULT_STRIP_TAGS = [
|
|
32
|
+
"nav",
|
|
33
|
+
"header",
|
|
34
|
+
"footer",
|
|
35
|
+
"aside",
|
|
36
|
+
"script",
|
|
37
|
+
"style",
|
|
38
|
+
"noscript",
|
|
39
|
+
"iframe",
|
|
40
|
+
"svg"
|
|
41
|
+
];
|
|
42
|
+
var STRIP_ROLES = ["navigation", "banner", "contentinfo", "complementary"];
|
|
43
|
+
function stripBoilerplate(html, extraTags = []) {
|
|
44
|
+
let result = html;
|
|
45
|
+
const allTags = [...DEFAULT_STRIP_TAGS, ...extraTags];
|
|
46
|
+
for (const tag of allTags) {
|
|
47
|
+
const tagRegex = new RegExp(`<${tag}[\\s>][\\s\\S]*?<\\/${tag}>`, "gi");
|
|
48
|
+
result = result.replace(tagRegex, "");
|
|
49
|
+
const selfClosingRegex = new RegExp(`<${tag}[^>]*\\/>`, "gi");
|
|
50
|
+
result = result.replace(selfClosingRegex, "");
|
|
51
|
+
}
|
|
52
|
+
for (const role of STRIP_ROLES) {
|
|
53
|
+
const roleRegex = new RegExp(
|
|
54
|
+
`<[a-z][^>]+role=["']${role}["'][\\s\\S]*?>[\\s\\S]*?<\\/[a-z]+>`,
|
|
55
|
+
"gi"
|
|
56
|
+
);
|
|
57
|
+
result = result.replace(roleRegex, "");
|
|
58
|
+
}
|
|
59
|
+
result = result.replace(/<!--[\s\S]*?-->/g, "");
|
|
60
|
+
return result;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// src/convert.ts
|
|
64
|
+
var import_node_html_markdown = require("node-html-markdown");
|
|
65
|
+
var nhm = new import_node_html_markdown.NodeHtmlMarkdown({
|
|
66
|
+
// Use fenced code blocks (```) for better AI readability
|
|
67
|
+
codeBlockStyle: "fenced",
|
|
68
|
+
// Limit consecutive blank lines — saves tokens
|
|
69
|
+
maxConsecutiveNewlines: 2,
|
|
70
|
+
// Skip data: URIs — they're noise for AI agents
|
|
71
|
+
keepDataImages: false,
|
|
72
|
+
// Inline links are cleaner for AI consumption
|
|
73
|
+
useInlineLinks: true
|
|
74
|
+
});
|
|
75
|
+
function htmlToMarkdown(html) {
|
|
76
|
+
return nhm.translate(html);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// src/build.ts
|
|
80
|
+
async function buildStaticMarkdown(options = {}) {
|
|
81
|
+
const cwd = options.cwd ?? process.cwd();
|
|
82
|
+
const distDir = options.distDir ?? ".next";
|
|
83
|
+
const outDir = options.outDir ?? import_node_path.default.join("public", ".well-known", "markdown");
|
|
84
|
+
const stripSelectors = options.stripSelectors ?? [];
|
|
85
|
+
const distPath = import_node_path.default.join(cwd, distDir);
|
|
86
|
+
const outPath = import_node_path.default.join(cwd, outDir);
|
|
87
|
+
if (!import_node_fs.default.existsSync(distPath)) {
|
|
88
|
+
throw new Error(
|
|
89
|
+
`Build output not found at ${distDir}/. Run \`next build\` first.`
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
const manifestPath = import_node_path.default.join(distPath, "prerender-manifest.json");
|
|
93
|
+
if (!import_node_fs.default.existsSync(manifestPath)) {
|
|
94
|
+
return { generated: [], skipped: [] };
|
|
95
|
+
}
|
|
96
|
+
const manifest = JSON.parse(
|
|
97
|
+
import_node_fs.default.readFileSync(manifestPath, "utf8")
|
|
98
|
+
);
|
|
99
|
+
const staticRoutes = Object.keys(manifest.routes);
|
|
100
|
+
if (staticRoutes.length === 0) {
|
|
101
|
+
return { generated: [], skipped: [] };
|
|
102
|
+
}
|
|
103
|
+
const generated = [];
|
|
104
|
+
const skipped = [];
|
|
105
|
+
import_node_fs.default.mkdirSync(outPath, { recursive: true });
|
|
106
|
+
for (const route of staticRoutes) {
|
|
107
|
+
const html = findHtmlForRoute(distPath, route);
|
|
108
|
+
if (!html) {
|
|
109
|
+
skipped.push(route);
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
const markdown = htmlToMarkdown(stripBoilerplate(html, stripSelectors));
|
|
113
|
+
const slug = route === "/" ? "index" : route.replace(/^\//, "");
|
|
114
|
+
const mdFile = import_node_path.default.join(outPath, `${slug}.md`);
|
|
115
|
+
import_node_fs.default.mkdirSync(import_node_path.default.dirname(mdFile), { recursive: true });
|
|
116
|
+
import_node_fs.default.writeFileSync(mdFile, markdown, "utf8");
|
|
117
|
+
generated.push(route);
|
|
118
|
+
}
|
|
119
|
+
return { generated, skipped };
|
|
120
|
+
}
|
|
121
|
+
function findHtmlForRoute(distPath, route) {
|
|
122
|
+
const slug = route === "/" ? "index" : route.replace(/^\//, "");
|
|
123
|
+
const candidates = [
|
|
124
|
+
// App Router: .next/server/app/[route]/page.html or .next/server/app/[route].html
|
|
125
|
+
import_node_path.default.join(distPath, "server", "app", slug, "page.html"),
|
|
126
|
+
import_node_path.default.join(distPath, "server", "app", `${slug}.html`),
|
|
127
|
+
import_node_path.default.join(distPath, "server", "app", slug, "index.html"),
|
|
128
|
+
// Pages Router: .next/server/pages/[route].html
|
|
129
|
+
import_node_path.default.join(distPath, "server", "pages", `${slug}.html`),
|
|
130
|
+
import_node_path.default.join(distPath, "server", "pages", slug, "index.html")
|
|
131
|
+
];
|
|
132
|
+
for (const candidate of candidates) {
|
|
133
|
+
if (import_node_fs.default.existsSync(candidate)) {
|
|
134
|
+
return import_node_fs.default.readFileSync(candidate, "utf8");
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// src/cli.ts
|
|
141
|
+
var import_node_fs2 = __toESM(require("fs"), 1);
|
|
142
|
+
var import_node_path2 = __toESM(require("path"), 1);
|
|
143
|
+
var command = process.argv[2];
|
|
144
|
+
switch (command) {
|
|
145
|
+
case "init":
|
|
146
|
+
runInit();
|
|
147
|
+
break;
|
|
148
|
+
case "build":
|
|
149
|
+
runBuild();
|
|
150
|
+
break;
|
|
151
|
+
default:
|
|
152
|
+
printHelp();
|
|
153
|
+
}
|
|
154
|
+
var MIDDLEWARE_CONTENT = `import { withMarkdownForAgents } from 'next-agent-md'
|
|
155
|
+
|
|
156
|
+
export default withMarkdownForAgents()
|
|
157
|
+
|
|
158
|
+
export const config = {
|
|
159
|
+
matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
|
|
160
|
+
}
|
|
161
|
+
`;
|
|
162
|
+
function detectNextVersion(cwd) {
|
|
163
|
+
try {
|
|
164
|
+
const pkg = JSON.parse(import_node_fs2.default.readFileSync(import_node_path2.default.join(cwd, "package.json"), "utf8"));
|
|
165
|
+
const version = pkg.dependencies?.next ?? pkg.devDependencies?.next ?? "";
|
|
166
|
+
const major = parseInt(version.replace(/[^0-9]/, ""), 10);
|
|
167
|
+
return isNaN(major) ? 15 : major;
|
|
168
|
+
} catch {
|
|
169
|
+
return 15;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
function runInit() {
|
|
173
|
+
const cwd = process.cwd();
|
|
174
|
+
const nextMajor = detectNextVersion(cwd);
|
|
175
|
+
const filename = nextMajor >= 16 ? "proxy.ts" : "middleware.ts";
|
|
176
|
+
const useSrcDir = import_node_fs2.default.existsSync(import_node_path2.default.join(cwd, "src"));
|
|
177
|
+
const targetDir = useSrcDir ? import_node_path2.default.join(cwd, "src") : cwd;
|
|
178
|
+
const targetFile = import_node_path2.default.join(targetDir, filename);
|
|
179
|
+
const displayPath = useSrcDir ? `src/${filename}` : filename;
|
|
180
|
+
const existing = [
|
|
181
|
+
"proxy.ts",
|
|
182
|
+
"proxy.js",
|
|
183
|
+
"middleware.ts",
|
|
184
|
+
"middleware.js",
|
|
185
|
+
import_node_path2.default.join("src", "proxy.ts"),
|
|
186
|
+
import_node_path2.default.join("src", "proxy.js"),
|
|
187
|
+
import_node_path2.default.join("src", "middleware.ts"),
|
|
188
|
+
import_node_path2.default.join("src", "middleware.js")
|
|
189
|
+
].map((f) => import_node_path2.default.join(cwd, f)).find((f) => import_node_fs2.default.existsSync(f));
|
|
190
|
+
if (existing) {
|
|
191
|
+
const rel = import_node_path2.default.relative(cwd, existing);
|
|
192
|
+
console.log(`
|
|
193
|
+
\x1B[33m\u26A0 Skipped:\x1B[0m ${rel} already exists.
|
|
194
|
+
`);
|
|
195
|
+
console.log(` To use next-agent-md, update it to:
|
|
196
|
+
`);
|
|
197
|
+
console.log(` \x1B[90m${MIDDLEWARE_CONTENT}\x1B[0m`);
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
import_node_fs2.default.writeFileSync(targetFile, MIDDLEWARE_CONTENT, "utf8");
|
|
201
|
+
console.log(
|
|
202
|
+
[
|
|
203
|
+
"",
|
|
204
|
+
` \x1B[32m\u2714 Created ${displayPath}\x1B[0m`,
|
|
205
|
+
"",
|
|
206
|
+
" AI agents can now request Markdown from any page:",
|
|
207
|
+
"",
|
|
208
|
+
' \x1B[36mcurl -H "Accept: text/markdown" http://localhost:3000/\x1B[0m',
|
|
209
|
+
"",
|
|
210
|
+
" Optional \u2014 also add the config plugin to next.config.ts:",
|
|
211
|
+
"",
|
|
212
|
+
" \x1B[90mimport { withAgentMd } from 'next-agent-md/config'\x1B[0m",
|
|
213
|
+
" \x1B[90mexport default withAgentMd()(nextConfig)\x1B[0m",
|
|
214
|
+
"",
|
|
215
|
+
" The config plugin auto-generates pre-built .md files after next build,",
|
|
216
|
+
" eliminating self-fetch overhead for static pages.",
|
|
217
|
+
""
|
|
218
|
+
].join("\n")
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
async function runBuild() {
|
|
222
|
+
console.log("\n \x1B[90mnext-agent-md\x1B[0m Generating static markdown files\u2026\n");
|
|
223
|
+
try {
|
|
224
|
+
const { generated, skipped } = await buildStaticMarkdown();
|
|
225
|
+
if (generated.length === 0 && skipped.length === 0) {
|
|
226
|
+
console.log(" No static pages found in build output. Skipping.\n");
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
for (const route of generated) {
|
|
230
|
+
console.log(` \x1B[32m\u2714\x1B[0m ${route}`);
|
|
231
|
+
}
|
|
232
|
+
if (skipped.length > 0) {
|
|
233
|
+
console.log(`
|
|
234
|
+
\x1B[33m\u26A0 ${skipped.length} route(s) skipped\x1B[0m (HTML not found in build output):`);
|
|
235
|
+
for (const route of skipped) {
|
|
236
|
+
console.log(` ${route}`);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
console.log(
|
|
240
|
+
[
|
|
241
|
+
"",
|
|
242
|
+
` \x1B[32m\u2714 ${generated.length} file(s) written to public/.well-known/markdown/\x1B[0m`,
|
|
243
|
+
"",
|
|
244
|
+
" Static pages will be served from pre-built .md files.",
|
|
245
|
+
" Dynamic pages fall back to on-demand conversion.",
|
|
246
|
+
""
|
|
247
|
+
].join("\n")
|
|
248
|
+
);
|
|
249
|
+
} catch (err) {
|
|
250
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
251
|
+
console.error(`
|
|
252
|
+
\x1B[31m\u2716 Error:\x1B[0m ${message}
|
|
253
|
+
`);
|
|
254
|
+
process.exit(1);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
function printHelp() {
|
|
258
|
+
console.log(
|
|
259
|
+
[
|
|
260
|
+
"",
|
|
261
|
+
" \x1B[1mnext-agent-md\x1B[0m",
|
|
262
|
+
"",
|
|
263
|
+
" Commands:",
|
|
264
|
+
" \x1B[36minit\x1B[0m Scaffold middleware.ts in your Next.js project",
|
|
265
|
+
" \x1B[36mbuild\x1B[0m Pre-generate markdown for static pages after next build",
|
|
266
|
+
"",
|
|
267
|
+
" Examples:",
|
|
268
|
+
" npx next-agent-md init",
|
|
269
|
+
" npx next-agent-md build",
|
|
270
|
+
""
|
|
271
|
+
].join("\n")
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
//# sourceMappingURL=cli.cjs.map
|
package/dist/cli.cjs.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/build.ts","../src/strip-boilerplate.ts","../src/convert.ts","../src/cli.ts"],"sourcesContent":["/**\n * Static markdown pre-generation.\n *\n * Reads the Next.js build output, finds all statically pre-rendered HTML files,\n * converts them to Markdown, and writes them to public/.well-known/markdown/\n * so the middleware can serve them directly without a self-fetch round-trip.\n *\n * Run automatically after `next build` via the withAgentMd() config plugin,\n * or manually with `npx next-agent-md build`.\n */\nimport fs from 'node:fs'\nimport path from 'node:path'\nimport { stripBoilerplate } from './strip-boilerplate'\nimport { htmlToMarkdown } from './convert'\n\nexport interface BuildOptions {\n /** Next.js project root. Default: process.cwd() */\n cwd?: string\n /** Next.js build output directory. Default: '.next' */\n distDir?: string\n /** Where to write .md files, relative to cwd. Default: 'public/.well-known/markdown' */\n outDir?: string\n /** Extra tags to strip (passed through to stripBoilerplate). Default: [] */\n stripSelectors?: string[]\n}\n\ninterface PrerenderManifest {\n version: number\n routes: Record<string, unknown>\n dynamicRoutes: Record<string, unknown>\n}\n\nexport async function buildStaticMarkdown(options: BuildOptions = {}): Promise<BuildResult> {\n const cwd = options.cwd ?? process.cwd()\n const distDir = options.distDir ?? '.next'\n const outDir = options.outDir ?? path.join('public', '.well-known', 'markdown')\n const stripSelectors = options.stripSelectors ?? []\n\n const distPath = path.join(cwd, distDir)\n const outPath = path.join(cwd, outDir)\n\n // ── Validate build output exists ──────────────────────────────────────────\n if (!fs.existsSync(distPath)) {\n throw new Error(\n `Build output not found at ${distDir}/. Run \\`next build\\` first.`\n )\n }\n\n // ── Read prerender manifest to find static routes ─────────────────────────\n const manifestPath = path.join(distPath, 'prerender-manifest.json')\n if (!fs.existsSync(manifestPath)) {\n return { generated: [], skipped: [] }\n }\n\n const manifest: PrerenderManifest = JSON.parse(\n fs.readFileSync(manifestPath, 'utf8')\n )\n\n const staticRoutes = Object.keys(manifest.routes)\n if (staticRoutes.length === 0) {\n return { generated: [], skipped: [] }\n }\n\n // ── Find HTML files for each static route ─────────────────────────────────\n const generated: string[] = []\n const skipped: string[] = []\n\n fs.mkdirSync(outPath, { recursive: true })\n\n for (const route of staticRoutes) {\n const html = findHtmlForRoute(distPath, route)\n if (!html) {\n skipped.push(route)\n continue\n }\n\n const markdown = htmlToMarkdown(stripBoilerplate(html, stripSelectors))\n\n // Write to outDir — nested paths become nested dirs\n // e.g. /blog/hello → public/.well-known/markdown/blog/hello.md\n const slug = route === '/' ? 'index' : route.replace(/^\\//, '')\n const mdFile = path.join(outPath, `${slug}.md`)\n\n fs.mkdirSync(path.dirname(mdFile), { recursive: true })\n fs.writeFileSync(mdFile, markdown, 'utf8')\n generated.push(route)\n }\n\n return { generated, skipped }\n}\n\nexport interface BuildResult {\n generated: string[]\n skipped: string[]\n}\n\n/**\n * Finds the pre-rendered HTML for a given route from the Next.js build output.\n * Checks App Router paths first, then Pages Router paths.\n */\nfunction findHtmlForRoute(distPath: string, route: string): string | null {\n const slug = route === '/' ? 'index' : route.replace(/^\\//, '')\n\n const candidates = [\n // App Router: .next/server/app/[route]/page.html or .next/server/app/[route].html\n path.join(distPath, 'server', 'app', slug, 'page.html'),\n path.join(distPath, 'server', 'app', `${slug}.html`),\n path.join(distPath, 'server', 'app', slug, 'index.html'),\n // Pages Router: .next/server/pages/[route].html\n path.join(distPath, 'server', 'pages', `${slug}.html`),\n path.join(distPath, 'server', 'pages', slug, 'index.html'),\n ]\n\n for (const candidate of candidates) {\n if (fs.existsSync(candidate)) {\n return fs.readFileSync(candidate, 'utf8')\n }\n }\n\n return null\n}\n\n/**\n * Returns the public URL path for a pre-built markdown file.\n * Used by the middleware to check if a fast-path file exists.\n *\n * e.g. pathname '/blog/hello' → '/.well-known/markdown/blog/hello.md'\n */\nexport function markdownPublicPath(pathname: string): string {\n const slug = pathname === '/' ? 'index' : pathname.replace(/^\\//, '')\n return `/.well-known/markdown/${slug}.md`\n}\n","/**\n * HTML tags whose content is typically boilerplate (navigation, chrome, scripts).\n * These are stripped before markdown conversion to reduce noise for AI agents.\n */\nconst DEFAULT_STRIP_TAGS = [\n 'nav',\n 'header',\n 'footer',\n 'aside',\n 'script',\n 'style',\n 'noscript',\n 'iframe',\n 'svg',\n]\n\n/**\n * ARIA roles that indicate navigation or structural boilerplate (not content).\n */\nconst STRIP_ROLES = ['navigation', 'banner', 'contentinfo', 'complementary']\n\n/**\n * Strips boilerplate HTML elements from a page before markdown conversion.\n *\n * Uses regex rather than a DOM parser to remain compatible with the Edge Runtime\n * (jsdom and similar are not available). Handles the common case well; deeply\n * nested elements of the same tag type may not be fully removed.\n *\n * @param html - Raw HTML string to clean\n * @param extraTags - Additional tag names to strip (e.g. ['cookie-banner'])\n */\nexport function stripBoilerplate(html: string, extraTags: string[] = []): string {\n let result = html\n\n const allTags = [...DEFAULT_STRIP_TAGS, ...extraTags]\n\n for (const tag of allTags) {\n // Match the full element including content — non-greedy to avoid over-matching\n const tagRegex = new RegExp(`<${tag}[\\\\s>][\\\\s\\\\S]*?<\\\\/${tag}>`, 'gi')\n result = result.replace(tagRegex, '')\n\n // Also handle self-closing variants: <tag ... />\n const selfClosingRegex = new RegExp(`<${tag}[^>]*\\\\/>`, 'gi')\n result = result.replace(selfClosingRegex, '')\n }\n\n // Remove elements with ARIA roles indicating structural boilerplate\n for (const role of STRIP_ROLES) {\n const roleRegex = new RegExp(\n `<[a-z][^>]+role=[\"']${role}[\"'][\\\\s\\\\S]*?>[\\\\s\\\\S]*?<\\\\/[a-z]+>`,\n 'gi'\n )\n result = result.replace(roleRegex, '')\n }\n\n // Remove HTML comments — often contain CMS/build tool artifacts\n result = result.replace(/<!--[\\s\\S]*?-->/g, '')\n\n return result\n}\n","import { NodeHtmlMarkdown } from 'node-html-markdown'\n\n/**\n * Shared converter instance — reusing avoids repeated option parsing overhead.\n */\nconst nhm = new NodeHtmlMarkdown({\n // Use fenced code blocks (```) for better AI readability\n codeBlockStyle: 'fenced',\n // Limit consecutive blank lines — saves tokens\n maxConsecutiveNewlines: 2,\n // Skip data: URIs — they're noise for AI agents\n keepDataImages: false,\n // Inline links are cleaner for AI consumption\n useInlineLinks: true,\n})\n\n/**\n * Converts an HTML string to Markdown.\n */\nexport function htmlToMarkdown(html: string): string {\n return nhm.translate(html)\n}\n","#!/usr/bin/env node\n/**\n * next-agent-md CLI\n *\n * Commands:\n * init — scaffold middleware.ts in the current Next.js project\n * build — pre-generate markdown files from static pages after next build\n */\nimport { buildStaticMarkdown } from './build'\n\nconst command = process.argv[2]\n\nswitch (command) {\n case 'init':\n runInit()\n break\n case 'build':\n runBuild()\n break\n default:\n printHelp()\n}\n\n// ── init ──────────────────────────────────────────────────────────────────────\n\nimport fs from 'node:fs'\nimport path from 'node:path'\n\nconst MIDDLEWARE_CONTENT = `import { withMarkdownForAgents } from 'next-agent-md'\n\nexport default withMarkdownForAgents()\n\nexport const config = {\n matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],\n}\n`\n\nfunction detectNextVersion(cwd: string): number {\n try {\n const pkg = JSON.parse(fs.readFileSync(path.join(cwd, 'package.json'), 'utf8'))\n const version: string = pkg.dependencies?.next ?? pkg.devDependencies?.next ?? ''\n const major = parseInt(version.replace(/[^0-9]/, ''), 10)\n return isNaN(major) ? 15 : major\n } catch {\n return 15\n }\n}\n\nfunction runInit(): void {\n const cwd = process.cwd()\n const nextMajor = detectNextVersion(cwd)\n\n // Next.js 16+ uses proxy.ts; older versions use middleware.ts\n const filename = nextMajor >= 16 ? 'proxy.ts' : 'middleware.ts'\n\n const useSrcDir = fs.existsSync(path.join(cwd, 'src'))\n const targetDir = useSrcDir ? path.join(cwd, 'src') : cwd\n const targetFile = path.join(targetDir, filename)\n const displayPath = useSrcDir ? `src/${filename}` : filename\n\n const existing = [\n 'proxy.ts', 'proxy.js',\n 'middleware.ts', 'middleware.js',\n path.join('src', 'proxy.ts'), path.join('src', 'proxy.js'),\n path.join('src', 'middleware.ts'), path.join('src', 'middleware.js'),\n ].map(f => path.join(cwd, f)).find((f) => fs.existsSync(f))\n\n if (existing) {\n const rel = path.relative(cwd, existing)\n console.log(`\\n \\x1b[33m⚠ Skipped:\\x1b[0m ${rel} already exists.\\n`)\n console.log(` To use next-agent-md, update it to:\\n`)\n console.log(` \\x1b[90m${MIDDLEWARE_CONTENT}\\x1b[0m`)\n return\n }\n\n fs.writeFileSync(targetFile, MIDDLEWARE_CONTENT, 'utf8')\n\n console.log(\n [\n '',\n ` \\x1b[32m✔ Created ${displayPath}\\x1b[0m`,\n '',\n ' AI agents can now request Markdown from any page:',\n '',\n ' \\x1b[36mcurl -H \"Accept: text/markdown\" http://localhost:3000/\\x1b[0m',\n '',\n ' Optional — also add the config plugin to next.config.ts:',\n '',\n \" \\x1b[90mimport { withAgentMd } from 'next-agent-md/config'\\x1b[0m\",\n ' \\x1b[90mexport default withAgentMd()(nextConfig)\\x1b[0m',\n '',\n ' The config plugin auto-generates pre-built .md files after next build,',\n ' eliminating self-fetch overhead for static pages.',\n '',\n ].join('\\n')\n )\n}\n\n// ── build ─────────────────────────────────────────────────────────────────────\n\nasync function runBuild(): Promise<void> {\n console.log('\\n \\x1b[90mnext-agent-md\\x1b[0m Generating static markdown files…\\n')\n\n try {\n const { generated, skipped } = await buildStaticMarkdown()\n\n if (generated.length === 0 && skipped.length === 0) {\n console.log(' No static pages found in build output. Skipping.\\n')\n return\n }\n\n for (const route of generated) {\n console.log(` \\x1b[32m✔\\x1b[0m ${route}`)\n }\n\n if (skipped.length > 0) {\n console.log(`\\n \\x1b[33m⚠ ${skipped.length} route(s) skipped\\x1b[0m (HTML not found in build output):`)\n for (const route of skipped) {\n console.log(` ${route}`)\n }\n }\n\n console.log(\n [\n '',\n ` \\x1b[32m✔ ${generated.length} file(s) written to public/.well-known/markdown/\\x1b[0m`,\n '',\n ' Static pages will be served from pre-built .md files.',\n ' Dynamic pages fall back to on-demand conversion.',\n '',\n ].join('\\n')\n )\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n console.error(`\\n \\x1b[31m✖ Error:\\x1b[0m ${message}\\n`)\n process.exit(1)\n }\n}\n\n// ── help ──────────────────────────────────────────────────────────────────────\n\nfunction printHelp(): void {\n console.log(\n [\n '',\n ' \\x1b[1mnext-agent-md\\x1b[0m',\n '',\n ' Commands:',\n ' \\x1b[36minit\\x1b[0m Scaffold middleware.ts in your Next.js project',\n ' \\x1b[36mbuild\\x1b[0m Pre-generate markdown for static pages after next build',\n '',\n ' Examples:',\n ' npx next-agent-md init',\n ' npx next-agent-md build',\n '',\n ].join('\\n')\n )\n}\n"],"mappings":"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","names":["path","fs","import_node_fs","import_node_path","fs","path"]}
|
package/dist/cli.d.cts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|