@mdream/nuxt 0.8.1 → 0.8.2
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 +3 -3
- package/dist/module.json +1 -1
- package/dist/module.mjs +51 -69
- package/dist/runtime/nuxt/plugins/prerender.d.ts +2 -0
- package/dist/runtime/nuxt/plugins/prerender.js +20 -0
- package/dist/runtime/server/middleware/mdream.d.ts +1 -1
- package/dist/runtime/server/middleware/mdream.js +42 -71
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -15,11 +15,11 @@ Mdream provides a Nuxt module that enables seamless HTML to Markdown conversion
|
|
|
15
15
|
### Installation
|
|
16
16
|
|
|
17
17
|
```bash
|
|
18
|
-
npm install @mdream/nuxt
|
|
18
|
+
npm install @mdream/nuxt
|
|
19
19
|
# or
|
|
20
|
-
pnpm add @mdream/nuxt
|
|
20
|
+
pnpm add @mdream/nuxt
|
|
21
21
|
# or
|
|
22
|
-
yarn add @mdream/nuxt
|
|
22
|
+
yarn add @mdream/nuxt
|
|
23
23
|
```
|
|
24
24
|
|
|
25
25
|
### Usage
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -1,91 +1,68 @@
|
|
|
1
|
-
import { join,
|
|
2
|
-
import { useNuxt, defineNuxtModule, createResolver, addTypeTemplate, addServerHandler } from '@nuxt/kit';
|
|
1
|
+
import { join, relative, resolve } from 'node:path';
|
|
2
|
+
import { useNuxt, defineNuxtModule, createResolver, addTypeTemplate, addServerHandler, addPlugin } from '@nuxt/kit';
|
|
3
3
|
import { defu } from 'defu';
|
|
4
4
|
import { useSiteConfig, installNuxtSiteConfig } from 'nuxt-site-config/kit';
|
|
5
|
-
import {
|
|
5
|
+
import { writeFile } from 'node:fs/promises';
|
|
6
6
|
import { consola } from 'consola';
|
|
7
|
-
import {
|
|
7
|
+
import { generateLlmsTxtArtifacts } from 'mdream';
|
|
8
8
|
|
|
9
9
|
const name = "@mdream/nuxt";
|
|
10
|
-
const version = "0.8.
|
|
10
|
+
const version = "0.8.1";
|
|
11
11
|
|
|
12
12
|
const logger = consola.withTag("nuxt-mdream");
|
|
13
|
-
function
|
|
14
|
-
const
|
|
15
|
-
if (robotsMatch) {
|
|
16
|
-
const content = String(robotsMatch[1]).toLowerCase();
|
|
17
|
-
return !content.includes("noindex");
|
|
18
|
-
}
|
|
19
|
-
return true;
|
|
20
|
-
}
|
|
21
|
-
function setupPrerenderHandler(config, nuxt = useNuxt()) {
|
|
13
|
+
function setupPrerenderHandler() {
|
|
14
|
+
const nuxt = useNuxt();
|
|
22
15
|
const pages = [];
|
|
23
16
|
nuxt.hooks.hook("nitro:init", async (nitro) => {
|
|
24
17
|
nitro.hooks.hook("prerender:generate", async (route) => {
|
|
25
|
-
if (
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
return;
|
|
30
|
-
}
|
|
31
|
-
const html = route.contents;
|
|
32
|
-
if (!isIndexable(html)) {
|
|
33
|
-
return;
|
|
34
|
-
}
|
|
35
|
-
try {
|
|
36
|
-
const markdown = htmlToMarkdown(html, {
|
|
37
|
-
origin: route.route,
|
|
38
|
-
...config.mdreamOptions
|
|
39
|
-
});
|
|
40
|
-
const titleMatch = html.match(/<title[^>]*>([^<]+)<\/title>/i);
|
|
41
|
-
const title = titleMatch ? String(titleMatch[1]).trim() : route.route;
|
|
18
|
+
if (route.fileName?.endsWith(".md")) {
|
|
19
|
+
const markdown = route.contents;
|
|
20
|
+
const titleMatch = markdown.match(/title:\s*(.+)/);
|
|
21
|
+
const extractedTitle = titleMatch?.[1]?.replace(/"/g, "") || route.route;
|
|
42
22
|
pages.push({
|
|
43
23
|
url: route.route,
|
|
44
|
-
title,
|
|
24
|
+
title: extractedTitle,
|
|
45
25
|
markdown
|
|
46
26
|
});
|
|
47
|
-
const mdPath = route.route === "/" ? "/index.md" : `${route.route}.md`;
|
|
48
|
-
const outputPath = join(nitro.options.output.publicDir, mdPath);
|
|
49
|
-
await mkdir(dirname(outputPath), { recursive: true });
|
|
50
|
-
await writeFile(outputPath, markdown, "utf-8");
|
|
51
|
-
} catch (error) {
|
|
52
|
-
logger.warn(`Failed to convert ${route.route} to markdown:`, error);
|
|
53
27
|
}
|
|
54
28
|
});
|
|
55
29
|
nitro.hooks.hook("prerender:done", async () => {
|
|
56
30
|
if (pages.length === 0) {
|
|
57
31
|
return;
|
|
58
32
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
33
|
+
const processedFiles = pages.map((page) => ({
|
|
34
|
+
title: page.title,
|
|
35
|
+
content: page.markdown,
|
|
36
|
+
url: page.url === "/" ? "/index.md" : `${page.url}.md`
|
|
37
|
+
}));
|
|
38
|
+
const siteConfig = useSiteConfig();
|
|
39
|
+
const artifacts = await generateLlmsTxtArtifacts({
|
|
40
|
+
origin: siteConfig.url,
|
|
41
|
+
files: processedFiles,
|
|
42
|
+
generateFull: true,
|
|
43
|
+
siteName: siteConfig.name || siteConfig.url,
|
|
44
|
+
description: siteConfig.description
|
|
45
|
+
});
|
|
46
|
+
logger.success(`Generated markdown for ${pages.length} pages`);
|
|
47
|
+
if (artifacts.llmsTxt) {
|
|
48
|
+
const llmsTxtPath = join(nitro.options.output.publicDir, "llms.txt");
|
|
49
|
+
await writeFile(llmsTxtPath, artifacts.llmsTxt, "utf-8");
|
|
50
|
+
nitro._prerenderedRoutes.push({
|
|
51
|
+
route: "/llms.txt",
|
|
52
|
+
fileName: llmsTxtPath,
|
|
53
|
+
generateTimeMS: 0
|
|
71
54
|
});
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
}
|
|
84
|
-
if (artifacts.llmsFullTxt) {
|
|
85
|
-
logger.info("Generated llms-full.txt");
|
|
86
|
-
}
|
|
87
|
-
} catch (error) {
|
|
88
|
-
logger.error("Failed to generate llms.txt artifacts:", error);
|
|
55
|
+
logger.info("Generated llms.txt");
|
|
56
|
+
}
|
|
57
|
+
if (artifacts.llmsFullTxt) {
|
|
58
|
+
const llmsFullTxtPath = join(nitro.options.output.publicDir, "llms-full.txt");
|
|
59
|
+
await writeFile(llmsFullTxtPath, artifacts.llmsFullTxt, "utf-8");
|
|
60
|
+
nitro._prerenderedRoutes.push({
|
|
61
|
+
route: "/llms-full.txt",
|
|
62
|
+
fileName: llmsFullTxtPath,
|
|
63
|
+
generateTimeMS: 0
|
|
64
|
+
});
|
|
65
|
+
logger.info("Generated llms-full.txt");
|
|
89
66
|
}
|
|
90
67
|
});
|
|
91
68
|
});
|
|
@@ -102,7 +79,9 @@ const module = defineNuxtModule({
|
|
|
102
79
|
},
|
|
103
80
|
defaults: {
|
|
104
81
|
enabled: true,
|
|
105
|
-
mdreamOptions: {
|
|
82
|
+
mdreamOptions: {
|
|
83
|
+
preset: "minimal"
|
|
84
|
+
},
|
|
106
85
|
cache: {
|
|
107
86
|
maxAge: 3600,
|
|
108
87
|
// 1 hour
|
|
@@ -168,9 +147,12 @@ export {}
|
|
|
168
147
|
middleware: true,
|
|
169
148
|
handler: resolver.resolve("./runtime/server/middleware/mdream")
|
|
170
149
|
});
|
|
150
|
+
if (nuxt.options.build) {
|
|
151
|
+
addPlugin({ mode: "server", src: resolver.resolve("./runtime/nuxt/plugins/prerender") });
|
|
152
|
+
}
|
|
171
153
|
const isStatic = nuxt.options.nitro.static || nuxt.options._generate || false;
|
|
172
154
|
if (isStatic || nuxt.options.nitro.prerender?.routes?.length) {
|
|
173
|
-
setupPrerenderHandler(
|
|
155
|
+
setupPrerenderHandler();
|
|
174
156
|
}
|
|
175
157
|
}
|
|
176
158
|
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { defineNuxtPlugin, prerenderRoutes } from "nuxt/app";
|
|
2
|
+
export default defineNuxtPlugin({
|
|
3
|
+
setup(nuxtApp) {
|
|
4
|
+
if (!import.meta.prerender) {
|
|
5
|
+
return;
|
|
6
|
+
}
|
|
7
|
+
nuxtApp.hooks.hook("app:rendered", (ctx) => {
|
|
8
|
+
let url = ctx.ssrContext?.url || "";
|
|
9
|
+
if (url.endsWith(".md")) {
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
if (url.endsWith("/")) {
|
|
13
|
+
url = `${url}index.md`;
|
|
14
|
+
} else {
|
|
15
|
+
url = `${url}.md`;
|
|
16
|
+
}
|
|
17
|
+
prerenderRoutes(url);
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
});
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<string | undefined>>;
|
|
1
|
+
declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<string | import("h3").H3Error<unknown> | undefined>>;
|
|
2
2
|
export default _default;
|
|
@@ -1,18 +1,12 @@
|
|
|
1
|
+
import { withSiteUrl } from "#site-config/server/composables/utils";
|
|
1
2
|
import { consola } from "consola";
|
|
2
|
-
import { createError, defineEventHandler,
|
|
3
|
+
import { createError, defineEventHandler, setHeader } from "h3";
|
|
3
4
|
import { htmlToMarkdown } from "mdream";
|
|
5
|
+
import { extractionPlugin } from "mdream/plugins";
|
|
4
6
|
import { withMinimalPreset } from "mdream/preset/minimal";
|
|
5
7
|
import { useNitroApp, useRuntimeConfig } from "nitropack/runtime";
|
|
6
8
|
const logger = consola.withTag("nuxt-mdream");
|
|
7
|
-
function
|
|
8
|
-
const robotsMatch = html.match(/<meta\s+name=["']robots["']\s+content=["']([^"']+)["']/i);
|
|
9
|
-
if (robotsMatch) {
|
|
10
|
-
const content = String(robotsMatch[1]).toLowerCase();
|
|
11
|
-
return !content.includes("noindex");
|
|
12
|
-
}
|
|
13
|
-
return true;
|
|
14
|
-
}
|
|
15
|
-
async function convertHtmlToMarkdown(html, url, config, route, title) {
|
|
9
|
+
async function convertHtmlToMarkdown(html, url, config, route) {
|
|
16
10
|
let options = {
|
|
17
11
|
origin: url,
|
|
18
12
|
...config.mdreamOptions
|
|
@@ -20,83 +14,60 @@ async function convertHtmlToMarkdown(html, url, config, route, title) {
|
|
|
20
14
|
if (config.mdreamOptions?.preset === "minimal") {
|
|
21
15
|
options = withMinimalPreset(options);
|
|
22
16
|
}
|
|
23
|
-
let
|
|
17
|
+
let title = "";
|
|
18
|
+
let markdown = htmlToMarkdown(html, {
|
|
19
|
+
...options,
|
|
20
|
+
plugins: [
|
|
21
|
+
...options.plugins || [],
|
|
22
|
+
// Add any additional plugins here if needed
|
|
23
|
+
extractionPlugin({
|
|
24
|
+
title(html2) {
|
|
25
|
+
title = html2.textContent;
|
|
26
|
+
}
|
|
27
|
+
})
|
|
28
|
+
]
|
|
29
|
+
});
|
|
24
30
|
const context = {
|
|
25
31
|
html,
|
|
26
32
|
markdown,
|
|
27
33
|
route,
|
|
28
34
|
title,
|
|
29
|
-
isPrerender:
|
|
35
|
+
isPrerender: Boolean(import.meta.prerender)
|
|
30
36
|
};
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
markdown = context.markdown;
|
|
36
|
-
}
|
|
37
|
-
} catch (error) {
|
|
38
|
-
logger.debug("Could not call mdream:markdown hook:", error);
|
|
37
|
+
const nitroApp = useNitroApp();
|
|
38
|
+
if (nitroApp?.hooks) {
|
|
39
|
+
await nitroApp.hooks.callHook("mdream:markdown", context);
|
|
40
|
+
markdown = context.markdown;
|
|
39
41
|
}
|
|
40
42
|
return markdown;
|
|
41
43
|
}
|
|
42
44
|
export default defineEventHandler(async (event) => {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
if (!htmlPath.endsWith(".md")) {
|
|
45
|
+
let path = event.path;
|
|
46
|
+
if (!path.endsWith(".md")) {
|
|
46
47
|
return;
|
|
47
48
|
}
|
|
48
49
|
const config = useRuntimeConfig(event).mdream;
|
|
49
|
-
|
|
50
|
-
if (
|
|
51
|
-
|
|
50
|
+
path = path.slice(0, -3);
|
|
51
|
+
if (path === "/index") {
|
|
52
|
+
path = "/";
|
|
52
53
|
}
|
|
54
|
+
let html;
|
|
53
55
|
try {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
"user-agent": event.headers.get("user-agent") || "",
|
|
59
|
-
"accept-language": event.headers.get("accept-language") || ""
|
|
60
|
-
}
|
|
61
|
-
});
|
|
62
|
-
if (!response.ok) {
|
|
63
|
-
throw createError({
|
|
64
|
-
statusCode: response.status,
|
|
65
|
-
statusMessage: response.statusText
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
|
-
const html = await response.text();
|
|
69
|
-
if (!isIndexable(html)) {
|
|
70
|
-
throw createError({
|
|
71
|
-
statusCode: 404,
|
|
72
|
-
statusMessage: "Page not indexable"
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
const titleMatch = html.match(/<title[^>]*>([^<]+)<\/title>/i);
|
|
76
|
-
const title = titleMatch ? String(titleMatch[1]).trim() : htmlPath;
|
|
77
|
-
const markdown = await convertHtmlToMarkdown(
|
|
78
|
-
html,
|
|
79
|
-
requestUrl.origin + htmlPath,
|
|
80
|
-
config,
|
|
81
|
-
htmlPath,
|
|
82
|
-
title
|
|
83
|
-
);
|
|
84
|
-
setHeader(event, "content-type", "text/markdown; charset=utf-8");
|
|
85
|
-
return markdown;
|
|
86
|
-
} catch (error) {
|
|
87
|
-
if (error.statusCode) {
|
|
88
|
-
throw error;
|
|
89
|
-
}
|
|
90
|
-
if (error.status === 404) {
|
|
91
|
-
throw createError({
|
|
92
|
-
statusCode: 404,
|
|
93
|
-
statusMessage: "Page not found"
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
|
-
logger.error("Failed to generate markdown for", htmlPath, error);
|
|
97
|
-
throw createError({
|
|
56
|
+
html = await globalThis.$fetch(path);
|
|
57
|
+
} catch (e) {
|
|
58
|
+
logger.error(`Failed to fetch HTML for ${path}`, e);
|
|
59
|
+
return createError({
|
|
98
60
|
statusCode: 500,
|
|
99
|
-
statusMessage:
|
|
61
|
+
statusMessage: "Internal Server Error",
|
|
62
|
+
message: `Failed to fetch HTML for ${path}`
|
|
100
63
|
});
|
|
101
64
|
}
|
|
65
|
+
const markdown = await convertHtmlToMarkdown(
|
|
66
|
+
html,
|
|
67
|
+
withSiteUrl(event, path),
|
|
68
|
+
config,
|
|
69
|
+
path
|
|
70
|
+
);
|
|
71
|
+
setHeader(event, "content-type", "text/markdown; charset=utf-8");
|
|
72
|
+
return markdown;
|
|
102
73
|
});
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mdream/nuxt",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.8.
|
|
4
|
+
"version": "0.8.2",
|
|
5
5
|
"description": "Nuxt module for converting HTML pages to Markdown using mdream",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"exports": {
|
|
@@ -26,25 +26,25 @@
|
|
|
26
26
|
"nuxt": "^3.0.0"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@nuxt/kit": "^4.0.
|
|
29
|
+
"@nuxt/kit": "^4.0.1",
|
|
30
30
|
"consola": "^3.4.2",
|
|
31
31
|
"defu": "^6.1.4",
|
|
32
32
|
"nuxt-site-config": "^3.2.2",
|
|
33
33
|
"pathe": "^2.0.3",
|
|
34
|
-
"mdream": "0.8.
|
|
34
|
+
"mdream": "0.8.2"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"@antfu/eslint-config": "^4.17.0",
|
|
38
38
|
"@arethetypeswrong/cli": "^0.18.2",
|
|
39
39
|
"@nuxt/devtools": "latest",
|
|
40
40
|
"@nuxt/module-builder": "^1.0.1",
|
|
41
|
-
"@nuxt/schema": "^4.0.
|
|
41
|
+
"@nuxt/schema": "^4.0.1",
|
|
42
42
|
"@nuxt/test-utils": "^3.19.2",
|
|
43
43
|
"@types/node": "latest",
|
|
44
44
|
"bumpp": "^10.2.0",
|
|
45
45
|
"changelogen": "^0.6.2",
|
|
46
46
|
"eslint": "^9.31.0",
|
|
47
|
-
"nuxt": "^4.0.
|
|
47
|
+
"nuxt": "^4.0.1",
|
|
48
48
|
"typescript": "^5.8.3",
|
|
49
49
|
"vitest": "^3.2.4"
|
|
50
50
|
},
|