static-llms-txt 0.1.0 → 0.1.1

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.
Files changed (2) hide show
  1. package/index.cjs +43 -3
  2. package/package.json +26 -4
package/index.cjs CHANGED
@@ -51,7 +51,8 @@ function inline(node) {
51
51
 
52
52
  function codeBlock(node) {
53
53
  const code = node.querySelector("code") || node;
54
- const cls = (code.getAttribute("class") || "") + " " + (node.getAttribute("class") || "") + " " + (node.getAttribute("data-language") || node.getAttribute("data-lang") || "");
54
+ const wrap = node.parentNode && node.parentNode.getAttribute ? node.parentNode.getAttribute("class") || "" : ""; // VitePress: <div class="language-js"><pre>
55
+ const cls = (code.getAttribute("class") || "") + " " + (node.getAttribute("class") || "") + " " + wrap + " " + (node.getAttribute("data-language") || node.getAttribute("data-lang") || "");
55
56
  const lang = (/(?:language|lang|highlight-source)-([\w+#.-]+)/.exec(cls) || [])[1] || (node.getAttribute("data-language") || node.getAttribute("data-lang") || "");
56
57
  // Line-per-element highlighters (Shiki, Expressive Code) put each line in its own block element.
57
58
  const lines = code.querySelectorAll(".line, .ec-line");
@@ -146,9 +147,44 @@ function meta(doc, selector) {
146
147
 
147
148
  // One HTML document → { title, description, canonical, noindex, redirect, sections, text }.
148
149
  // sections: [{ heading, id, text }] where the first one (heading "") is the text before any h2.
150
+ // Minifiers such as html-minifier-terser (removeOptionalTags, used by Docusaurus) drop the table end
151
+ // tags HTML lets you omit. node-html-parser copes with omitted </li> and </p> but not with table parts:
152
+ // one such table and <body> and <main> vanish from the tree. Put the table end tags back first.
153
+ // Well-formed tables come out unchanged; nested tables are left alone.
154
+ function closeTableTags(html) {
155
+ if (!/<table\b/i.test(html)) return html;
156
+ return html.replace(/<table\b[\s\S]*?<\/table>/gi, (table) => {
157
+ if (/<table\b/i.test(table.slice(6))) return table;
158
+ let cell = "", row = false, part = "", out = "", last = 0, m;
159
+ const re = /<(\/?)(td|th|tr|thead|tbody|tfoot|table)\b[^>]*>/gi;
160
+ const shut = (to) => {
161
+ let s = "";
162
+ if (cell) { s += "</" + cell + ">"; cell = ""; }
163
+ if (to !== "cell" && row) { s += "</tr>"; row = false; }
164
+ if (to === "part" && part) { s += "</" + part + ">"; part = ""; }
165
+ return s;
166
+ };
167
+ while ((m = re.exec(table))) {
168
+ out += table.slice(last, m.index);
169
+ last = re.lastIndex;
170
+ const end = m[1] === "/", tag = m[2].toLowerCase();
171
+ if (end) {
172
+ if (tag === "td" || tag === "th") { if (cell) { out += m[0]; cell = ""; } }
173
+ else if (tag === "tr") { out += shut("row").replace(/<\/tr>$/, "") + m[0]; row = false; }
174
+ else if (tag === "table") out += shut("part") + m[0];
175
+ else { out += shut("row") + m[0]; part = ""; }
176
+ } else if (tag === "td" || tag === "th") { out += shut("cell") + m[0]; cell = tag; }
177
+ else if (tag === "tr") { out += shut("row") + m[0]; row = true; }
178
+ else if (tag === "table") out += m[0];
179
+ else { out += shut("part") + m[0]; part = tag; }
180
+ }
181
+ return out + table.slice(last);
182
+ });
183
+ }
184
+
149
185
  function readHtml(html, options) {
150
186
  const o = options || {};
151
- const doc = parse(String(html), { comment: false, blockTextElements: { script: false, style: false, noscript: false } });
187
+ const doc = parse(closeTableTags(String(html)), { comment: false, blockTextElements: { script: false, style: false, noscript: false } });
152
188
  const robots = meta(doc, 'meta[name="robots"]') + " " + meta(doc, 'meta[name="googlebot"]');
153
189
  const refresh = doc.querySelector('meta[http-equiv="refresh"], meta[http-equiv="Refresh"]');
154
190
  const canonicalEl = doc.querySelector('link[rel="canonical"]');
@@ -245,8 +281,12 @@ function scan(dir, options) {
245
281
  for (const rel of walkHtml(root)) {
246
282
  if (/(^|\/)(404|500)(\.html?|\/index\.html?)$/i.test(rel)) continue;
247
283
  if (excludes.some((re) => re.test(rel))) continue;
248
- const info = readHtml(fs.readFileSync(path.join(root, rel), "utf8"), { content: o.content, exclude: o.remove });
284
+ const html = fs.readFileSync(path.join(root, rel), "utf8");
285
+ const info = readHtml(html, { content: o.content, exclude: o.remove });
249
286
  if (info.noindex || info.redirect) continue;
287
+ if (!info.text && html.length > 2000 && o.warn !== false) {
288
+ console.warn("static-llms-txt: found no text in " + rel + ", so it is left out. If the page has text, please report it at https://github.com/fengyiqicoder/onetagai/issues");
289
+ }
250
290
  if (info.text.length < (o.minLength === undefined ? 40 : o.minLength)) continue;
251
291
  const p = urlPath(rel, o.cleanUrls);
252
292
  const url = o.site ? joinUrl(o.site, p) : /^https?:\/\//i.test(info.canonical) ? info.canonical : "";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "static-llms-txt",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Write llms.txt and llms-full.txt for any static site from the HTML it already built. Works after Eleventy, Hugo, Jekyll, Astro, Gatsby, Docusaurus or plain HTML. No API key.",
5
5
  "main": "./index.cjs",
6
6
  "module": "./index.mjs",
@@ -13,14 +13,36 @@
13
13
  }
14
14
  },
15
15
  "bin": {
16
- "static-llms-txt": "./cli.cjs"
16
+ "static-llms-txt": "cli.cjs"
17
17
  },
18
- "files": ["index.cjs", "index.mjs", "index.d.ts", "cli.cjs", "README.md", "LICENSE"],
18
+ "files": [
19
+ "index.cjs",
20
+ "index.mjs",
21
+ "index.d.ts",
22
+ "cli.cjs",
23
+ "README.md",
24
+ "LICENSE"
25
+ ],
19
26
  "scripts": {
20
27
  "test": "node --test test/*.test.mjs",
21
28
  "prepublishOnly": "npm test"
22
29
  },
23
- "keywords": ["llms.txt", "llms-txt", "llms-full.txt", "llmstxt", "static-site", "html-to-markdown", "ssg", "eleventy", "hugo", "jekyll", "ai", "seo", "geo", "cli"],
30
+ "keywords": [
31
+ "llms.txt",
32
+ "llms-txt",
33
+ "llms-full.txt",
34
+ "llmstxt",
35
+ "static-site",
36
+ "html-to-markdown",
37
+ "ssg",
38
+ "eleventy",
39
+ "hugo",
40
+ "jekyll",
41
+ "ai",
42
+ "seo",
43
+ "geo",
44
+ "cli"
45
+ ],
24
46
  "license": "MIT",
25
47
  "author": "OneTag AI (https://onetagai.com)",
26
48
  "homepage": "https://github.com/fengyiqicoder/onetagai/tree/main/packages/static-llms-txt#readme",