@aiquants/html-to-markdown 0.1.3 → 0.1.5

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 (62) hide show
  1. package/README.md +102 -19
  2. package/dist/{core-DUdcm-c8.cjs → core-D2dlEt1l.cjs} +1 -1
  3. package/dist/{core-0q2nkcWH.js → core-DU_246Q7.js} +202 -29
  4. package/dist/index.cjs +1 -1
  5. package/dist/index.js +8 -2
  6. package/dist/main.cjs +1 -1
  7. package/dist/main.js +6 -2
  8. package/dist/mcp-server-CpmnW1p8.js +283 -0
  9. package/dist/mcp-server-D4Mj1IQ9.cjs +1 -0
  10. package/dist/mcp-server-streamable-Bu7QAr3W.cjs +1 -0
  11. package/dist/mcp-server-streamable-CQD5zK30.js +7538 -0
  12. package/dist/mcp-streamable.cjs +2 -0
  13. package/dist/mcp-streamable.js +6 -0
  14. package/dist/mcp.cjs +2 -0
  15. package/dist/mcp.js +13 -0
  16. package/dist/src/core.d.ts +8 -5
  17. package/dist/src/index.d.ts +2 -0
  18. package/dist/src/mcp-server-streamable.d.ts +74 -0
  19. package/dist/src/mcp-server.d.ts +57 -0
  20. package/dist/src/mcp-streamable.d.ts +6 -0
  21. package/dist/src/mcp.d.ts +6 -0
  22. package/dist/src/plugins/index.d.ts +2 -0
  23. package/dist/src/plugins/rehype-named-anchors.d.ts +2 -0
  24. package/dist/src/plugins/rehype-paragraph-wrapper.d.ts +6 -0
  25. package/dist/src/types.d.ts +5 -0
  26. package/dist/src/version.d.ts +9 -0
  27. package/dist/tests/core-unified.test.d.ts +5 -0
  28. package/dist/tests/coverage-complete.test.d.ts +5 -0
  29. package/dist/tests/mcp-unified.test.d.ts +5 -0
  30. package/dist/tests/plugins-unified.test.d.ts +5 -0
  31. package/dist/tests/test-client.d.ts +4 -0
  32. package/dist/version-BRVBmpor.js +11318 -0
  33. package/dist/version-DTaUKNBS.cjs +1 -0
  34. package/mcp.json +14 -0
  35. package/package.json +27 -8
  36. package/dist/tests/100-percent-final.test.d.ts +0 -1
  37. package/dist/tests/absolute-final-coverage.test.d.ts +0 -1
  38. package/dist/tests/browser-100.test.d.ts +0 -1
  39. package/dist/tests/browser-coverage.test.d.ts +0 -1
  40. package/dist/tests/browser.test.d.ts +0 -1
  41. package/dist/tests/cli.test.d.ts +0 -1
  42. package/dist/tests/converter-coverage.test.d.ts +0 -1
  43. package/dist/tests/converter.test.d.ts +0 -1
  44. package/dist/tests/core.test.d.ts +0 -1
  45. package/dist/tests/coverage-edge-cases.test.d.ts +0 -1
  46. package/dist/tests/direct-coverage.test.d.ts +0 -1
  47. package/dist/tests/final-100-percent.test.d.ts +0 -1
  48. package/dist/tests/final-coverage.test.d.ts +0 -1
  49. package/dist/tests/i18n.test.d.ts +0 -1
  50. package/dist/tests/index.test.d.ts +0 -1
  51. package/dist/tests/line-70-coverage.test.d.ts +0 -1
  52. package/dist/tests/main.test.d.ts +0 -1
  53. package/dist/tests/plugins.test.d.ts +0 -1
  54. package/dist/tests/readme-usage.test.d.ts +0 -1
  55. package/dist/tests/rehype-absolute-links-coverage.test.d.ts +0 -1
  56. package/dist/tests/rehype-absolute-links.test.d.ts +0 -1
  57. package/dist/tests/rehype-sanitize-html-coverage.test.d.ts +0 -1
  58. package/dist/tests/rehype-sanitize-html.test.d.ts +0 -1
  59. package/dist/tests/rehype-wikipedia-footnotes.test.d.ts +0 -1
  60. package/dist/tests/types.test.d.ts +0 -1
  61. package/dist/tests/ultimate-coverage.test.d.ts +0 -1
  62. /package/dist/tests/{100-percent-coverage.test.d.ts → entry-points-error.test.d.ts} +0 -0
@@ -19494,9 +19494,39 @@ function bail(error) {
19494
19494
  throw error;
19495
19495
  }
19496
19496
  }
19497
+ var commonjsGlobal = typeof globalThis !== "undefined" ? globalThis : typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : {};
19497
19498
  function getDefaultExportFromCjs(x) {
19498
19499
  return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x["default"] : x;
19499
19500
  }
19501
+ function getAugmentedNamespace(n) {
19502
+ if (Object.prototype.hasOwnProperty.call(n, "__esModule")) return n;
19503
+ var f = n.default;
19504
+ if (typeof f == "function") {
19505
+ var a2 = function a3() {
19506
+ var isInstance = false;
19507
+ try {
19508
+ isInstance = this instanceof a3;
19509
+ } catch {
19510
+ }
19511
+ if (isInstance) {
19512
+ return Reflect.construct(f, arguments, this.constructor);
19513
+ }
19514
+ return f.apply(this, arguments);
19515
+ };
19516
+ a2.prototype = f.prototype;
19517
+ } else a2 = {};
19518
+ Object.defineProperty(a2, "__esModule", { value: true });
19519
+ Object.keys(n).forEach(function(k) {
19520
+ var d = Object.getOwnPropertyDescriptor(n, k);
19521
+ Object.defineProperty(a2, k, d.get ? d : {
19522
+ enumerable: true,
19523
+ get: function() {
19524
+ return n[k];
19525
+ }
19526
+ });
19527
+ });
19528
+ return a2;
19529
+ }
19500
19530
  var extend$1;
19501
19531
  var hasRequiredExtend;
19502
19532
  function requireExtend() {
@@ -20384,6 +20414,123 @@ const rehypeAbsoluteLinks = (options) => {
20384
20414
  });
20385
20415
  };
20386
20416
  };
20417
+ const blockLevelTags = /* @__PURE__ */ new Set([
20418
+ "address",
20419
+ "article",
20420
+ "aside",
20421
+ "blockquote",
20422
+ "br",
20423
+ "details",
20424
+ "dialog",
20425
+ "dd",
20426
+ "div",
20427
+ "dl",
20428
+ "dt",
20429
+ "fieldset",
20430
+ "figcaption",
20431
+ "figure",
20432
+ "footer",
20433
+ "form",
20434
+ // "h1", // # 見出し<span id="custom-id"></span> となってしまうのを回避
20435
+ // "h2", // ## 見出し<span id="custom-id"></span> となってしまうのを回避
20436
+ // "h3", // ### 見出し<span id="custom-id"></span> となってしまうのを回避
20437
+ // "h4", // #### 見出し<span id="custom-id"></span> となってしまうのを回避
20438
+ // "h5", // ##### 見出し<span id="custom-id"></span> となってしまうのを回避
20439
+ // "h6", // ###### 見出し<span id="custom-id"></span> となってしまうのを回避
20440
+ "header",
20441
+ "hgroup",
20442
+ "hr",
20443
+ "li",
20444
+ "main",
20445
+ "nav",
20446
+ "ol",
20447
+ "p",
20448
+ "pre",
20449
+ "section",
20450
+ "table",
20451
+ "ul"
20452
+ ]);
20453
+ function hasBlockElement(node2) {
20454
+ let found = false;
20455
+ visit(node2, "element", (el) => {
20456
+ if (blockLevelTags.has(el.tagName)) {
20457
+ found = true;
20458
+ return EXIT;
20459
+ }
20460
+ });
20461
+ return found;
20462
+ }
20463
+ const slugger = new BananaSlug();
20464
+ const rehypeNamedAnchors = () => (tree) => {
20465
+ visit(tree, "element", (node2, index2, parent) => {
20466
+ if (parent && typeof index2 === "number" && node2.properties && node2.properties.id) {
20467
+ const id = String(node2.properties.id);
20468
+ if (id && ["h1", "h2", "h3", "h4", "h5", "h6"].includes(node2.tagName)) {
20469
+ const customId = slugger.slug(id);
20470
+ node2.children.push({
20471
+ type: "text",
20472
+ value: ` {#${customId}}`
20473
+ });
20474
+ return;
20475
+ }
20476
+ if (node2.tagName === "span" && id && Object.keys(node2.properties || {}).length === 1) {
20477
+ return;
20478
+ }
20479
+ const anchorNode = {
20480
+ type: "element",
20481
+ tagName: "span",
20482
+ properties: { id },
20483
+ children: []
20484
+ };
20485
+ if (hasBlockElement(node2)) {
20486
+ let firstInlineInfo = null;
20487
+ visit(node2, (child, childIndex, childParent) => {
20488
+ if (childParent && typeof childIndex === "number") {
20489
+ if (child.type === "element" && !blockLevelTags.has(child.tagName) || child.type === "text" && child.value.trim() !== "") {
20490
+ firstInlineInfo = {
20491
+ node: node2,
20492
+ parent: childParent,
20493
+ index: childIndex
20494
+ };
20495
+ return EXIT;
20496
+ }
20497
+ }
20498
+ });
20499
+ if (firstInlineInfo) {
20500
+ const inline = firstInlineInfo;
20501
+ inline.parent.children.splice(inline.index + 1, 0, anchorNode);
20502
+ delete node2.properties.id;
20503
+ }
20504
+ return;
20505
+ } else {
20506
+ parent.children.splice(index2 + 1, 0, anchorNode);
20507
+ delete node2.properties.id;
20508
+ return index2 + 1;
20509
+ }
20510
+ }
20511
+ });
20512
+ };
20513
+ const rehypeParagraphWrapper = () => (tree) => {
20514
+ visit(tree, "element", (node2, index2, parent) => {
20515
+ if (parent && typeof index2 === "number") {
20516
+ const nextNode = parent.children[index2 + 1];
20517
+ if (nextNode && nextNode.type === "element" && nextNode.tagName === "span" && nextNode.properties?.id && Object.keys(nextNode.properties).length === 1) {
20518
+ const parentIsBlock = !parent || parent.type === "root" || ["body", "div", "section", "article", "aside", "footer", "header", "nav", "td", "th", "li"].includes(parent.tagName);
20519
+ if (parentIsBlock) {
20520
+ const paragraph2 = {
20521
+ type: "element",
20522
+ tagName: "p",
20523
+ properties: {},
20524
+ children: [node2, nextNode]
20525
+ // 現在のノードと次の span ノードを子として含める
20526
+ };
20527
+ parent.children.splice(index2, 2, paragraph2);
20528
+ return index2 + 1;
20529
+ }
20530
+ }
20531
+ }
20532
+ });
20533
+ };
20387
20534
  const rehypeSanitizeHtml = () => {
20388
20535
  return (tree) => {
20389
20536
  visit(tree, (node2, index2, parent) => {
@@ -20477,7 +20624,7 @@ const rehypeWikipediaFootnotes = () => {
20477
20624
  };
20478
20625
  };
20479
20626
  const handleTableCell = (_state, node2, _parent) => {
20480
- const allowedTags = ["br", "hr", "h1", "h2", "h3", "h4", "h5", "h6", "strong", "em", "b", "i", "u", "s", "strike", "del", "ins", "code", "ul", "ol", "li", "a", "img", "blockquote", "pre", "table", "thead", "tbody", "tfoot", "tr", "th", "td"];
20627
+ const allowedTags = ["br", "hr", "h1", "h2", "h3", "h4", "h5", "h6", "strong", "em", "b", "i", "u", "s", "strike", "del", "ins", "code", "ul", "ol", "li", "a", "img", "blockquote", "pre", "table", "thead", "tbody", "tfoot", "tr", "th", "td", "style"];
20481
20628
  const allowedAttributes = ["style", "class", "id", "colspan", "rowspan", "width", "height", "align", "href", "src", "title", "alt"];
20482
20629
  visit(node2, "element", (elementNode, index2, parent) => {
20483
20630
  if (!parent || typeof index2 !== "number") {
@@ -20535,22 +20682,33 @@ const handleTableCell = (_state, node2, _parent) => {
20535
20682
  };
20536
20683
  const convertHtmlToMarkdown = async (html2, baseUrl, locale) => {
20537
20684
  console.log(getMessage(locale, "convert_start"));
20538
- const slugger = new BananaSlug();
20539
- const file = await unified().use(rehypeParse, { fragment: true }).use(rehypeRaw).use(rehypeSanitizeHtml).use(rehypeSlug).use(rehypeAbsoluteLinks, { baseUrl }).use(rehypeWikipediaFootnotes).use(rehypeRemark, {
20685
+ const slugger2 = new BananaSlug();
20686
+ const file = await unified().use(rehypeParse, { fragment: true }).use(rehypeRaw).use(rehypeSanitizeHtml).use(rehypeWikipediaFootnotes).use(rehypeNamedAnchors).use(rehypeSlug).use(rehypeParagraphWrapper).use(rehypeAbsoluteLinks, { baseUrl }).use(rehypeRemark, {
20540
20687
  handlers: {
20688
+ span(state, node2) {
20689
+ const id = node2.properties?.id;
20690
+ if (id && Object.keys(node2.properties || {}).length === 1) {
20691
+ const children = state.all(node2);
20692
+ if (children.length === 0) {
20693
+ return { type: "html", value: `<span id="${String(id)}"></span>` };
20694
+ }
20695
+ return [{ type: "html", value: `<span id="${String(id)}">` }, ...children, { type: "html", value: "</span>" }];
20696
+ }
20697
+ return state.all(node2);
20698
+ },
20541
20699
  a(state, node2) {
20542
20700
  const hasComplexChildren = node2.children.some((child) => child.type === "element" && ["dl", "ul", "ol", "table", "div"].includes(child.tagName));
20543
20701
  if (hasComplexChildren && node2.properties?.href) {
20544
20702
  const linkText2 = String(node2.properties.ariaLabel || "");
20545
20703
  if (linkText2) {
20546
- const url2 = String(node2.properties.href);
20547
- const linkNode = {
20704
+ const url = String(node2.properties.href);
20705
+ const linkNode2 = {
20548
20706
  type: "link",
20549
- url: url2,
20707
+ url,
20550
20708
  children: [{ type: "text", value: linkText2 }]
20551
20709
  };
20552
20710
  const otherContent = state.all(node2);
20553
- return [linkNode, { type: "text", value: "\n" }, ...otherContent];
20711
+ return [linkNode2, { type: "text", value: "\n" }, ...otherContent];
20554
20712
  }
20555
20713
  }
20556
20714
  const extractTextFromHast = (n) => {
@@ -20584,24 +20742,27 @@ const convertHtmlToMarkdown = async (html2, baseUrl, locale) => {
20584
20742
  return false;
20585
20743
  });
20586
20744
  };
20745
+ let linkNode;
20587
20746
  if (containsImage(node2)) {
20588
- return {
20747
+ linkNode = {
20589
20748
  type: "link",
20590
20749
  url: String(node2.properties?.href || ""),
20591
20750
  title: node2.properties?.title ? String(node2.properties.title) : null,
20592
20751
  children: state.all(node2)
20593
20752
  };
20753
+ } else {
20754
+ let url = String(node2.properties?.href || "");
20755
+ if (url.startsWith("#")) {
20756
+ url = `#${slugger2.slug(url.substring(1))}`;
20757
+ }
20758
+ linkNode = {
20759
+ type: "link",
20760
+ url,
20761
+ title: node2.properties?.title ? String(node2.properties.title) : null,
20762
+ children: [{ type: "text", value: linkText }]
20763
+ };
20594
20764
  }
20595
- let url = String(node2.properties?.href || "");
20596
- if (url.startsWith("#")) {
20597
- url = `#${slugger.slug(url.substring(1))}`;
20598
- }
20599
- return {
20600
- type: "link",
20601
- url,
20602
- title: node2.properties?.title ? String(node2.properties.title) : null,
20603
- children: [{ type: "text", value: linkText }]
20604
- };
20765
+ return linkNode;
20605
20766
  },
20606
20767
  td(state, node2, parent) {
20607
20768
  return handleTableCell(state, node2);
@@ -20625,23 +20786,35 @@ const convertHtmlToMarkdown = async (html2, baseUrl, locale) => {
20625
20786
  console.log(getMessage(locale, "convert_success"));
20626
20787
  return correctedMarkdown;
20627
20788
  };
20628
- const htmlToMarkdown = async (url, options = {}) => {
20629
- const { locale = "en-US" } = options;
20789
+ const htmlToMarkdown = async (urlOrHtml, options = {}) => {
20790
+ const { locale = "en-US", htmlContent } = options;
20630
20791
  try {
20631
- new URL(url);
20632
- const htmlContent = await getHtmlWithPlaywright(url, locale);
20633
- if (!htmlContent) {
20634
- console.error(`No HTML content found for URL: ${url}`);
20635
- return "";
20792
+ let htmlContentToProcess;
20793
+ let sourceUrl;
20794
+ if (htmlContent) {
20795
+ htmlContentToProcess = htmlContent;
20796
+ sourceUrl = urlOrHtml || "direct-html-input";
20797
+ } else {
20798
+ new URL(urlOrHtml);
20799
+ htmlContentToProcess = await getHtmlWithPlaywright(urlOrHtml, locale);
20800
+ sourceUrl = urlOrHtml;
20636
20801
  }
20637
- const markdown = await convertHtmlToMarkdown(htmlContent, url, locale);
20638
- return markdown;
20802
+ if (!htmlContentToProcess) {
20803
+ console.error(`No HTML content found for source: ${sourceUrl}`);
20804
+ return { html: "", markdown: "" };
20805
+ }
20806
+ const markdown = await convertHtmlToMarkdown(htmlContentToProcess, sourceUrl, locale);
20807
+ return { html: htmlContentToProcess, markdown };
20639
20808
  } catch (error) {
20640
- console.error(`An error occurred during the conversion process for URL: ${url}`, error);
20641
- return "";
20809
+ const source = htmlContent ? "HTML string" : `URL: ${urlOrHtml}`;
20810
+ console.error(`An error occurred during the conversion process for ${source}`, error);
20811
+ return { html: "", markdown: "" };
20642
20812
  }
20643
20813
  };
20644
20814
  export {
20815
+ getAugmentedNamespace as a,
20816
+ getDefaultExportFromCjs as b,
20817
+ commonjsGlobal as c,
20645
20818
  getMessage as g,
20646
20819
  htmlToMarkdown as h
20647
20820
  };
package/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- const e=new(require("happy-dom").Window);Object.assign(global,{document:e.document,window:e,self:e}),Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const o=require("./core-DUdcm-c8.cjs");exports.getMessage=o.getMessage,exports.htmlToMarkdown=o.htmlToMarkdown;
1
+ const e=new(require("happy-dom").Window);Object.assign(global,{document:e.document,window:e,self:e}),Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const r=require("./core-D2dlEt1l.cjs"),t=require("./mcp-server-D4Mj1IQ9.cjs"),c=require("./mcp-server-streamable-Bu7QAr3W.cjs");exports.getMessage=r.getMessage,exports.htmlToMarkdown=r.htmlToMarkdown,exports.createMcpServer=t.createMcpServer,exports.runMcpServer=t.runMcpServer,exports.createStreamableMcpServer=c.createStreamableMcpServer,exports.runStreamableMcpServer=c.runStreamableMcpServer;
package/dist/index.js CHANGED
@@ -5,8 +5,14 @@ Object.assign(global, {
5
5
  window,
6
6
  self: window
7
7
  });
8
- import { g, h } from "./core-0q2nkcWH.js";
8
+ import { g, h } from "./core-DU_246Q7.js";
9
+ import { c, r } from "./mcp-server-CpmnW1p8.js";
10
+ import { c as c2, r as r2 } from "./mcp-server-streamable-CQD5zK30.js";
9
11
  export {
12
+ c as createMcpServer,
13
+ c2 as createStreamableMcpServer,
10
14
  g as getMessage,
11
- h as htmlToMarkdown
15
+ h as htmlToMarkdown,
16
+ r as runMcpServer,
17
+ r2 as runStreamableMcpServer
12
18
  };
package/dist/main.cjs CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- Object.create,Object.defineProperty,Object.getOwnPropertyDescriptor,Object.getOwnPropertyNames,Object.getPrototypeOf,Object.prototype.hasOwnProperty;const e=new(require("happy-dom").Window);Object.assign(global,{document:e.document,window:e,self:e});const t=require("./core-DUdcm-c8.cjs");var r="undefined"!=typeof document?document.currentScript:null;(async()=>{(("undefined"==typeof document?require("url").pathToFileURL(__filename).href:r&&"SCRIPT"===r.tagName.toUpperCase()&&r.src||new URL("main.cjs",document.baseURI).href).endsWith(process.argv[1])||process.argv[1]&&process.argv[1].includes("vite-node"))&&(async()=>{const e=(await import("yargs-parser")).default,r=await import("node:fs"),o=await import("node:path"),c=e(process.argv.slice(2),{string:["locale","output"],alias:{output:["o"]},default:{locale:"en-US"}}),s=c._[0],n=c.locale,a=c.output;s||process.exit(1);try{new URL(s)}catch(i){process.exit(1)}try{const e=await t.htmlToMarkdown(s,{locale:n});let c;if(a){c=o.resolve(process.cwd(),a);const e=o.dirname(c);r.existsSync(e)||r.mkdirSync(e,{recursive:!0})}else{const e=`${s.replace(/https?:\/\//,"").replace(/[^a-zA-Z0-9]/g,"_")}_${Date.now()}.md`,t=o.join(process.cwd(),".outputs","raw");r.existsSync(t)||r.mkdirSync(t,{recursive:!0}),c=o.join(t,e)}r.writeFileSync(c,e)}catch(i){process.exit(1)}})()})();
2
+ Object.create,Object.defineProperty,Object.getOwnPropertyDescriptor,Object.getOwnPropertyNames,Object.getPrototypeOf,Object.prototype.hasOwnProperty;const e=new(require("happy-dom").Window);Object.assign(global,{document:e.document,window:e,self:e});const t=require("./core-D2dlEt1l.cjs");var r="undefined"!=typeof document?document.currentScript:null;(async()=>{(("undefined"==typeof document?require("url").pathToFileURL(__filename).href:r&&"SCRIPT"===r.tagName.toUpperCase()&&r.src||new URL("main.cjs",document.baseURI).href).endsWith(process.argv[1])||process.argv[1]&&process.argv[1].includes("vite-node"))&&(async()=>{const e=(await import("yargs-parser")).default,r=await import("node:fs"),o=await import("node:path"),c=e(process.argv.slice(2),{string:["locale","output"],alias:{output:["o"]},default:{locale:"en-US"}}),s=c._[0],n=c.locale,a=c.output;s||process.exit(1);try{new URL(s)}catch(i){process.exit(1)}try{const{markdown:e,html:c}=await t.htmlToMarkdown(s,{locale:n});let i;if(a){i=o.resolve(process.cwd(),a);const e=o.dirname(i);r.existsSync(e)||r.mkdirSync(e,{recursive:!0})}else{const e=`${s.replace(/https?:\/\//,"").replace(/[^a-zA-Z0-9]/g,"_")}_${Date.now()}.md`,t=o.join(process.cwd(),".outputs","raw");r.existsSync(t)||r.mkdirSync(t,{recursive:!0}),i=o.join(t,e)}r.writeFileSync(i,e);const p=o.parse(i),l=o.join(p.dir,`${p.name}.html`);r.writeFileSync(l,c)}catch(i){process.exit(1)}})()})();
package/dist/main.js CHANGED
@@ -6,7 +6,7 @@ Object.assign(global, {
6
6
  window,
7
7
  self: window
8
8
  });
9
- import { g as getMessage, h as htmlToMarkdown } from "./core-0q2nkcWH.js";
9
+ import { g as getMessage, h as htmlToMarkdown } from "./core-DU_246Q7.js";
10
10
  const runCli = async () => {
11
11
  const yargsParser = (await import("yargs-parser")).default;
12
12
  const fs = await import("node:fs");
@@ -44,7 +44,7 @@ ${getMessage(locale, "cli_start")}`);
44
44
  if (outputFile) {
45
45
  console.info(getMessage(locale, "cli_output", { path: outputFile }));
46
46
  }
47
- const markdown = await htmlToMarkdown(targetUrl, { locale });
47
+ const { markdown, html } = await htmlToMarkdown(targetUrl, { locale });
48
48
  let filePath;
49
49
  if (outputFile) {
50
50
  filePath = path.resolve(process.cwd(), outputFile);
@@ -63,9 +63,13 @@ ${getMessage(locale, "cli_start")}`);
63
63
  filePath = path.join(outputDir, fileName);
64
64
  }
65
65
  fs.writeFileSync(filePath, markdown);
66
+ const parsedPath = path.parse(filePath);
67
+ const htmlFilePath = path.join(parsedPath.dir, `${parsedPath.name}.html`);
68
+ fs.writeFileSync(htmlFilePath, html);
66
69
  console.info(`
67
70
  ${getMessage(locale, "success_save")}`);
68
71
  console.info(getMessage(locale, "success_path", { path: filePath }));
72
+ console.info(getMessage(locale, "success_path", { path: htmlFilePath }));
69
73
  } catch (error) {
70
74
  console.error(`
71
75
  ${getMessage(locale, "error_app")}`, error);
@@ -0,0 +1,283 @@
1
+ import { Window } from "happy-dom";
2
+ const window = new Window();
3
+ Object.assign(global, {
4
+ document: window.document,
5
+ window,
6
+ self: window
7
+ });
8
+ import { J as JSONRPCMessageSchema, g as getPackageVersion, S as Server, L as ListToolsRequestSchema, C as CallToolRequestSchema } from "./version-BRVBmpor.js";
9
+ import process from "node:process";
10
+ import { h as htmlToMarkdown } from "./core-DU_246Q7.js";
11
+ class ReadBuffer {
12
+ append(chunk) {
13
+ this._buffer = this._buffer ? Buffer.concat([this._buffer, chunk]) : chunk;
14
+ }
15
+ readMessage() {
16
+ if (!this._buffer) {
17
+ return null;
18
+ }
19
+ const index = this._buffer.indexOf("\n");
20
+ if (index === -1) {
21
+ return null;
22
+ }
23
+ const line = this._buffer.toString("utf8", 0, index).replace(/\r$/, "");
24
+ this._buffer = this._buffer.subarray(index + 1);
25
+ return deserializeMessage(line);
26
+ }
27
+ clear() {
28
+ this._buffer = void 0;
29
+ }
30
+ }
31
+ function deserializeMessage(line) {
32
+ return JSONRPCMessageSchema.parse(JSON.parse(line));
33
+ }
34
+ function serializeMessage(message) {
35
+ return JSON.stringify(message) + "\n";
36
+ }
37
+ class StdioServerTransport {
38
+ constructor(_stdin = process.stdin, _stdout = process.stdout) {
39
+ this._stdin = _stdin;
40
+ this._stdout = _stdout;
41
+ this._readBuffer = new ReadBuffer();
42
+ this._started = false;
43
+ this._ondata = (chunk) => {
44
+ this._readBuffer.append(chunk);
45
+ this.processReadBuffer();
46
+ };
47
+ this._onerror = (error) => {
48
+ var _a;
49
+ (_a = this.onerror) === null || _a === void 0 ? void 0 : _a.call(this, error);
50
+ };
51
+ }
52
+ /**
53
+ * Starts listening for messages on stdin.
54
+ */
55
+ async start() {
56
+ if (this._started) {
57
+ throw new Error("StdioServerTransport already started! If using Server class, note that connect() calls start() automatically.");
58
+ }
59
+ this._started = true;
60
+ this._stdin.on("data", this._ondata);
61
+ this._stdin.on("error", this._onerror);
62
+ }
63
+ processReadBuffer() {
64
+ var _a, _b;
65
+ while (true) {
66
+ try {
67
+ const message = this._readBuffer.readMessage();
68
+ if (message === null) {
69
+ break;
70
+ }
71
+ (_a = this.onmessage) === null || _a === void 0 ? void 0 : _a.call(this, message);
72
+ } catch (error) {
73
+ (_b = this.onerror) === null || _b === void 0 ? void 0 : _b.call(this, error);
74
+ }
75
+ }
76
+ }
77
+ async close() {
78
+ var _a;
79
+ this._stdin.off("data", this._ondata);
80
+ this._stdin.off("error", this._onerror);
81
+ const remainingDataListeners = this._stdin.listenerCount("data");
82
+ if (remainingDataListeners === 0) {
83
+ this._stdin.pause();
84
+ }
85
+ this._readBuffer.clear();
86
+ (_a = this.onclose) === null || _a === void 0 ? void 0 : _a.call(this);
87
+ }
88
+ send(message) {
89
+ return new Promise((resolve) => {
90
+ const json = serializeMessage(message);
91
+ if (this._stdout.write(json)) {
92
+ resolve();
93
+ } else {
94
+ this._stdout.once("drain", resolve);
95
+ }
96
+ });
97
+ }
98
+ }
99
+ function parseHtmlToMarkdownArgs(args) {
100
+ if (!args || typeof args !== "object") {
101
+ throw new Error("Arguments are required");
102
+ }
103
+ const { url, html_content, locale = "en-US", output_format = "markdown" } = args;
104
+ if (!url && !html_content) {
105
+ throw new Error("Either 'url' or 'html_content' is required");
106
+ }
107
+ if (url && typeof url !== "string") {
108
+ throw new Error("'url' must be a string");
109
+ }
110
+ if (html_content && typeof html_content !== "string") {
111
+ throw new Error("'html_content' must be a string");
112
+ }
113
+ if (locale && !["en-US", "ja-JP"].includes(locale)) {
114
+ throw new Error("'locale' must be 'en-US' or 'ja-JP'");
115
+ }
116
+ if (output_format && !["markdown", "html", "both"].includes(output_format)) {
117
+ throw new Error("'output_format' must be 'markdown', 'html', or 'both'");
118
+ }
119
+ return {
120
+ url,
121
+ html_content,
122
+ locale,
123
+ output_format
124
+ };
125
+ }
126
+ class HtmlToMarkdownMcpServer {
127
+ constructor() {
128
+ this.version = getPackageVersion();
129
+ this.server = new Server(
130
+ {
131
+ name: "@aiquants/html-to-markdown",
132
+ version: this.version,
133
+ description: "A tool to dynamically fetch a web page from a given URL and convert it to Markdown."
134
+ },
135
+ {
136
+ capabilities: {
137
+ tools: {}
138
+ }
139
+ }
140
+ );
141
+ this.setupToolHandlers();
142
+ }
143
+ /**
144
+ * Setup tool handlers for the MCP server.
145
+ * MCP サーバーのツールハンドラーを設定
146
+ */
147
+ setupToolHandlers() {
148
+ this.server.setRequestHandler(ListToolsRequestSchema, async () => {
149
+ return {
150
+ tools: [
151
+ {
152
+ name: "html_to_markdown",
153
+ description: "Convert HTML content from a URL or HTML string to Markdown format. Supports dynamic content loading and preserves table structures.",
154
+ inputSchema: {
155
+ type: "object",
156
+ properties: {
157
+ url: {
158
+ type: "string",
159
+ description: "The URL of the web page to convert to Markdown"
160
+ },
161
+ html_content: {
162
+ type: "string",
163
+ description: "HTML content as a string to convert to Markdown (alternative to URL)"
164
+ },
165
+ locale: {
166
+ type: "string",
167
+ description: "The locale to use for browser context (en-US or ja-JP)",
168
+ enum: ["en-US", "ja-JP"],
169
+ default: "en-US"
170
+ },
171
+ output_format: {
172
+ type: "string",
173
+ description: "Output format: 'markdown' (default), 'html', or 'both'",
174
+ enum: ["markdown", "html", "both"],
175
+ default: "markdown"
176
+ }
177
+ },
178
+ anyOf: [{ required: ["url"] }, { required: ["html_content"] }]
179
+ }
180
+ }
181
+ ]
182
+ };
183
+ });
184
+ this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
185
+ switch (request.params.name) {
186
+ case "html_to_markdown": {
187
+ return await this.handleHtmlToMarkdown(request.params.arguments);
188
+ }
189
+ default:
190
+ throw new Error(`Unknown tool: ${request.params.name}`);
191
+ }
192
+ });
193
+ }
194
+ /**
195
+ * Handle HTML to Markdown conversion tool call.
196
+ * HTML から Markdown への変換ツール呼び出しを処理
197
+ */
198
+ async handleHtmlToMarkdown(args) {
199
+ try {
200
+ const validatedArgs = parseHtmlToMarkdownArgs(args);
201
+ const { url, html_content, locale = "en-US", output_format = "markdown" } = validatedArgs;
202
+ if (url) {
203
+ try {
204
+ new URL(url);
205
+ } catch {
206
+ throw new Error(`Invalid URL: ${url}`);
207
+ }
208
+ }
209
+ const result = await htmlToMarkdown(url || "direct-html-input", {
210
+ locale,
211
+ htmlContent: html_content
212
+ });
213
+ let content;
214
+ switch (output_format) {
215
+ case "markdown":
216
+ content = [
217
+ {
218
+ type: "text",
219
+ text: result.markdown
220
+ }
221
+ ];
222
+ break;
223
+ case "html":
224
+ content = [
225
+ {
226
+ type: "text",
227
+ text: result.html
228
+ }
229
+ ];
230
+ break;
231
+ case "both":
232
+ content = [
233
+ {
234
+ type: "text",
235
+ text: result.markdown
236
+ },
237
+ {
238
+ type: "text",
239
+ text: result.html
240
+ }
241
+ ];
242
+ break;
243
+ default:
244
+ throw new Error(`Invalid output format: ${output_format}`);
245
+ }
246
+ return {
247
+ content,
248
+ isError: false
249
+ };
250
+ } catch (error) {
251
+ const errorMessage = error instanceof Error ? error.message : String(error);
252
+ return {
253
+ content: [
254
+ {
255
+ type: "text",
256
+ text: `Error: ${errorMessage}`
257
+ }
258
+ ],
259
+ isError: true
260
+ };
261
+ }
262
+ }
263
+ /**
264
+ * Start the MCP server.
265
+ * MCP サーバーを開始
266
+ */
267
+ async start() {
268
+ const transport = new StdioServerTransport();
269
+ await this.server.connect(transport);
270
+ console.error("HTML to Markdown MCP server started");
271
+ }
272
+ }
273
+ const createMcpServer = () => {
274
+ return new HtmlToMarkdownMcpServer();
275
+ };
276
+ const runMcpServer = async () => {
277
+ const server = createMcpServer();
278
+ await server.start();
279
+ };
280
+ export {
281
+ createMcpServer as c,
282
+ runMcpServer as r
283
+ };
@@ -0,0 +1 @@
1
+ const t=new(require("happy-dom").Window);Object.assign(global,{document:t.document,window:t,self:t});const r=require("./version-DTaUKNBS.cjs"),e=require("node:process"),o=require("./core-D2dlEt1l.cjs");class n{append(t){this._buffer=this._buffer?Buffer.concat([this._buffer,t]):t}readMessage(){if(!this._buffer)return null;const t=this._buffer.indexOf("\n");if(-1===t)return null;const e=this._buffer.toString("utf8",0,t).replace(/\r$/,"");return this._buffer=this._buffer.subarray(t+1),function(t){return r.JSONRPCMessageSchema.parse(JSON.parse(t))}(e)}clear(){this._buffer=void 0}}class s{constructor(t=e.stdin,r=e.stdout){this._stdin=t,this._stdout=r,this._readBuffer=new n,this._started=!1,this._ondata=t=>{this._readBuffer.append(t),this.processReadBuffer()},this._onerror=t=>{var r;null===(r=this.onerror)||void 0===r||r.call(this,t)}}async start(){if(this._started)throw new Error("StdioServerTransport already started! If using Server class, note that connect() calls start() automatically.");this._started=!0,this._stdin.on("data",this._ondata),this._stdin.on("error",this._onerror)}processReadBuffer(){for(var t,r;;)try{const r=this._readBuffer.readMessage();if(null===r)break;null===(t=this.onmessage)||void 0===t||t.call(this,r)}catch(e){null===(r=this.onerror)||void 0===r||r.call(this,e)}}async close(){var t;this._stdin.off("data",this._ondata),this._stdin.off("error",this._onerror);0===this._stdin.listenerCount("data")&&this._stdin.pause(),this._readBuffer.clear(),null===(t=this.onclose)||void 0===t||t.call(this)}send(t){return new Promise(r=>{const e=function(t){return JSON.stringify(t)+"\n"}(t);this._stdout.write(e)?r():this._stdout.once("drain",r)})}}class a{constructor(){this.version=r.getPackageVersion(),this.server=new r.Server({name:"@aiquants/html-to-markdown",version:this.version,description:"A tool to dynamically fetch a web page from a given URL and convert it to Markdown."},{capabilities:{tools:{}}}),this.setupToolHandlers()}setupToolHandlers(){this.server.setRequestHandler(r.ListToolsRequestSchema,async()=>({tools:[{name:"html_to_markdown",description:"Convert HTML content from a URL or HTML string to Markdown format. Supports dynamic content loading and preserves table structures.",inputSchema:{type:"object",properties:{url:{type:"string",description:"The URL of the web page to convert to Markdown"},html_content:{type:"string",description:"HTML content as a string to convert to Markdown (alternative to URL)"},locale:{type:"string",description:"The locale to use for browser context (en-US or ja-JP)",enum:["en-US","ja-JP"],default:"en-US"},output_format:{type:"string",description:"Output format: 'markdown' (default), 'html', or 'both'",enum:["markdown","html","both"],default:"markdown"}},anyOf:[{required:["url"]},{required:["html_content"]}]}}]})),this.server.setRequestHandler(r.CallToolRequestSchema,async t=>{if("html_to_markdown"===t.params.name)return await this.handleHtmlToMarkdown(t.params.arguments);throw new Error(`Unknown tool: ${t.params.name}`)})}async handleHtmlToMarkdown(t){try{const r=function(t){if(!t||"object"!=typeof t)throw new Error("Arguments are required");const{url:r,html_content:e,locale:o="en-US",output_format:n="markdown"}=t;if(!r&&!e)throw new Error("Either 'url' or 'html_content' is required");if(r&&"string"!=typeof r)throw new Error("'url' must be a string");if(e&&"string"!=typeof e)throw new Error("'html_content' must be a string");if(o&&!["en-US","ja-JP"].includes(o))throw new Error("'locale' must be 'en-US' or 'ja-JP'");if(n&&!["markdown","html","both"].includes(n))throw new Error("'output_format' must be 'markdown', 'html', or 'both'");return{url:r,html_content:e,locale:o,output_format:n}}(t),{url:e,html_content:n,locale:s="en-US",output_format:a="markdown"}=r;if(e)try{new URL(e)}catch{throw new Error(`Invalid URL: ${e}`)}const i=await o.htmlToMarkdown(e||"direct-html-input",{locale:s,htmlContent:n});let c;switch(a){case"markdown":c=[{type:"text",text:i.markdown}];break;case"html":c=[{type:"text",text:i.html}];break;case"both":c=[{type:"text",text:i.markdown},{type:"text",text:i.html}];break;default:throw new Error(`Invalid output format: ${a}`)}return{content:c,isError:!1}}catch(r){return{content:[{type:"text",text:`Error: ${r instanceof Error?r.message:String(r)}`}],isError:!0}}}async start(){const t=new s;await this.server.connect(t)}}const i=()=>new a;exports.createMcpServer=i,exports.runMcpServer=async()=>{const t=i();await t.start()};