@aiquants/html-to-markdown 0.1.2 → 0.1.4

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.
@@ -9,11 +9,12 @@ import { chromium } from "playwright";
9
9
  const messages = {
10
10
  "ja-JP": {
11
11
  error_url_required: "エラー: 変換対象のURLを引数で指定してください。",
12
- usage: "使用法: node dist/main.js <URL> [--locale <ロケール>]",
13
- example: "例: node dist/main.js https://ja.wikipedia.org/wiki/Node.js --locale ja-JP",
12
+ usage: "使用法: node dist/main.js <URL> [--output <ファイルパス>] [--locale <ロケール>]",
13
+ example: "例: node dist/main.js https://ja.wikipedia.org/wiki/Node.js -o output.md --locale ja-JP",
14
14
  cli_start: "🚀 コマンドラインから処理を開始します...",
15
15
  cli_url: " - URL: {url}",
16
16
  cli_locale: " - Locale: {locale}",
17
+ cli_output: " - Output: {path}",
17
18
  success_save: "✅ Markdownファイルが正常に保存されました。",
18
19
  success_path: " -> {path}",
19
20
  error_app: "❌ アプリケーションの実行中にエラーが発生しました。",
@@ -28,11 +29,12 @@ const messages = {
28
29
  },
29
30
  "en-US": {
30
31
  error_url_required: "Error: Please specify the URL to convert as an argument.",
31
- usage: "Usage: node dist/main.js <URL> [--locale <locale>]",
32
- example: "Example: node dist/main.js https://en.wikipedia.org/wiki/Node.js --locale en-US",
32
+ usage: "Usage: node dist/main.js <URL> [--output <file path>] [--locale <locale>]",
33
+ example: "Example: node dist/main.js https://en.wikipedia.org/wiki/Node.js -o output.md --locale en-US",
33
34
  cli_start: "🚀 Starting process from command line...",
34
35
  cli_url: " - URL: {url}",
35
36
  cli_locale: " - Locale: {locale}",
37
+ cli_output: " - Output: {path}",
36
38
  success_save: "✅ Markdown file saved successfully.",
37
39
  success_path: " -> {path}",
38
40
  error_app: "❌ An error occurred during application execution.",
@@ -20382,6 +20384,123 @@ const rehypeAbsoluteLinks = (options) => {
20382
20384
  });
20383
20385
  };
20384
20386
  };
20387
+ const blockLevelTags = /* @__PURE__ */ new Set([
20388
+ "address",
20389
+ "article",
20390
+ "aside",
20391
+ "blockquote",
20392
+ "br",
20393
+ "details",
20394
+ "dialog",
20395
+ "dd",
20396
+ "div",
20397
+ "dl",
20398
+ "dt",
20399
+ "fieldset",
20400
+ "figcaption",
20401
+ "figure",
20402
+ "footer",
20403
+ "form",
20404
+ // "h1", // # 見出し<span id="custom-id"></span> となってしまうのを回避
20405
+ // "h2", // ## 見出し<span id="custom-id"></span> となってしまうのを回避
20406
+ // "h3", // ### 見出し<span id="custom-id"></span> となってしまうのを回避
20407
+ // "h4", // #### 見出し<span id="custom-id"></span> となってしまうのを回避
20408
+ // "h5", // ##### 見出し<span id="custom-id"></span> となってしまうのを回避
20409
+ // "h6", // ###### 見出し<span id="custom-id"></span> となってしまうのを回避
20410
+ "header",
20411
+ "hgroup",
20412
+ "hr",
20413
+ "li",
20414
+ "main",
20415
+ "nav",
20416
+ "ol",
20417
+ "p",
20418
+ "pre",
20419
+ "section",
20420
+ "table",
20421
+ "ul"
20422
+ ]);
20423
+ function hasBlockElement(node2) {
20424
+ let found = false;
20425
+ visit(node2, "element", (el) => {
20426
+ if (blockLevelTags.has(el.tagName)) {
20427
+ found = true;
20428
+ return EXIT;
20429
+ }
20430
+ });
20431
+ return found;
20432
+ }
20433
+ const slugger = new BananaSlug();
20434
+ const rehypeNamedAnchors = () => (tree) => {
20435
+ visit(tree, "element", (node2, index2, parent) => {
20436
+ if (parent && typeof index2 === "number" && node2.properties && node2.properties.id) {
20437
+ const id = String(node2.properties.id);
20438
+ if (id && ["h1", "h2", "h3", "h4", "h5", "h6"].includes(node2.tagName)) {
20439
+ const customId = slugger.slug(id);
20440
+ node2.children.push({
20441
+ type: "text",
20442
+ value: ` {#${customId}}`
20443
+ });
20444
+ return;
20445
+ }
20446
+ if (node2.tagName === "span" && id && Object.keys(node2.properties || {}).length === 1) {
20447
+ return;
20448
+ }
20449
+ const anchorNode = {
20450
+ type: "element",
20451
+ tagName: "span",
20452
+ properties: { id },
20453
+ children: []
20454
+ };
20455
+ if (hasBlockElement(node2)) {
20456
+ let firstInlineInfo = null;
20457
+ visit(node2, (child, childIndex, childParent) => {
20458
+ if (childParent && typeof childIndex === "number") {
20459
+ if (child.type === "element" && !blockLevelTags.has(child.tagName) || child.type === "text" && child.value.trim() !== "") {
20460
+ firstInlineInfo = {
20461
+ node: node2,
20462
+ parent: childParent,
20463
+ index: childIndex
20464
+ };
20465
+ return EXIT;
20466
+ }
20467
+ }
20468
+ });
20469
+ if (firstInlineInfo) {
20470
+ const inline = firstInlineInfo;
20471
+ inline.parent.children.splice(inline.index + 1, 0, anchorNode);
20472
+ delete node2.properties.id;
20473
+ }
20474
+ return;
20475
+ } else {
20476
+ parent.children.splice(index2 + 1, 0, anchorNode);
20477
+ delete node2.properties.id;
20478
+ return index2 + 1;
20479
+ }
20480
+ }
20481
+ });
20482
+ };
20483
+ const rehypeParagraphWrapper = () => (tree) => {
20484
+ visit(tree, "element", (node2, index2, parent) => {
20485
+ if (parent && typeof index2 === "number") {
20486
+ const nextNode = parent.children[index2 + 1];
20487
+ if (nextNode && nextNode.type === "element" && nextNode.tagName === "span" && nextNode.properties?.id && Object.keys(nextNode.properties).length === 1) {
20488
+ const parentIsBlock = !parent || parent.type === "root" || ["body", "div", "section", "article", "aside", "footer", "header", "nav", "td", "th", "li"].includes(parent.tagName);
20489
+ if (parentIsBlock) {
20490
+ const paragraph2 = {
20491
+ type: "element",
20492
+ tagName: "p",
20493
+ properties: {},
20494
+ children: [node2, nextNode]
20495
+ // 現在のノードと次の span ノードを子として含める
20496
+ };
20497
+ parent.children.splice(index2, 2, paragraph2);
20498
+ return index2 + 1;
20499
+ }
20500
+ }
20501
+ }
20502
+ });
20503
+ };
20385
20504
  const rehypeSanitizeHtml = () => {
20386
20505
  return (tree) => {
20387
20506
  visit(tree, (node2, index2, parent) => {
@@ -20475,7 +20594,7 @@ const rehypeWikipediaFootnotes = () => {
20475
20594
  };
20476
20595
  };
20477
20596
  const handleTableCell = (_state, node2, _parent) => {
20478
- 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"];
20597
+ 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"];
20479
20598
  const allowedAttributes = ["style", "class", "id", "colspan", "rowspan", "width", "height", "align", "href", "src", "title", "alt"];
20480
20599
  visit(node2, "element", (elementNode, index2, parent) => {
20481
20600
  if (!parent || typeof index2 !== "number") {
@@ -20533,22 +20652,37 @@ const handleTableCell = (_state, node2, _parent) => {
20533
20652
  };
20534
20653
  const convertHtmlToMarkdown = async (html2, baseUrl, locale) => {
20535
20654
  console.log(getMessage(locale, "convert_start"));
20536
- const slugger = new BananaSlug();
20537
- const file = await unified().use(rehypeParse, { fragment: true }).use(rehypeRaw).use(rehypeSanitizeHtml).use(rehypeSlug).use(rehypeAbsoluteLinks, { baseUrl }).use(rehypeWikipediaFootnotes).use(rehypeRemark, {
20655
+ const slugger2 = new BananaSlug();
20656
+ 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, {
20538
20657
  handlers: {
20658
+ span(state, node2) {
20659
+ const id = node2.properties?.id;
20660
+ if (id && Object.keys(node2.properties || {}).length === 1) {
20661
+ const children = state.all(node2);
20662
+ if (children.length === 0) {
20663
+ return { type: "html", value: `<span id="${String(id)}"></span>` };
20664
+ }
20665
+ return [
20666
+ { type: "html", value: `<span id="${String(id)}">` },
20667
+ ...children,
20668
+ { type: "html", value: "</span>" }
20669
+ ];
20670
+ }
20671
+ return state.all(node2);
20672
+ },
20539
20673
  a(state, node2) {
20540
20674
  const hasComplexChildren = node2.children.some((child) => child.type === "element" && ["dl", "ul", "ol", "table", "div"].includes(child.tagName));
20541
20675
  if (hasComplexChildren && node2.properties?.href) {
20542
20676
  const linkText2 = String(node2.properties.ariaLabel || "");
20543
20677
  if (linkText2) {
20544
- const url2 = String(node2.properties.href);
20545
- const linkNode = {
20678
+ const url = String(node2.properties.href);
20679
+ const linkNode2 = {
20546
20680
  type: "link",
20547
- url: url2,
20681
+ url,
20548
20682
  children: [{ type: "text", value: linkText2 }]
20549
20683
  };
20550
20684
  const otherContent = state.all(node2);
20551
- return [linkNode, { type: "text", value: "\n" }, ...otherContent];
20685
+ return [linkNode2, { type: "text", value: "\n" }, ...otherContent];
20552
20686
  }
20553
20687
  }
20554
20688
  const extractTextFromHast = (n) => {
@@ -20582,24 +20716,27 @@ const convertHtmlToMarkdown = async (html2, baseUrl, locale) => {
20582
20716
  return false;
20583
20717
  });
20584
20718
  };
20719
+ let linkNode;
20585
20720
  if (containsImage(node2)) {
20586
- return {
20721
+ linkNode = {
20587
20722
  type: "link",
20588
20723
  url: String(node2.properties?.href || ""),
20589
20724
  title: node2.properties?.title ? String(node2.properties.title) : null,
20590
20725
  children: state.all(node2)
20591
20726
  };
20727
+ } else {
20728
+ let url = String(node2.properties?.href || "");
20729
+ if (url.startsWith("#")) {
20730
+ url = `#${slugger2.slug(url.substring(1))}`;
20731
+ }
20732
+ linkNode = {
20733
+ type: "link",
20734
+ url,
20735
+ title: node2.properties?.title ? String(node2.properties.title) : null,
20736
+ children: [{ type: "text", value: linkText }]
20737
+ };
20592
20738
  }
20593
- let url = String(node2.properties?.href || "");
20594
- if (url.startsWith("#")) {
20595
- url = `#${slugger.slug(url.substring(1))}`;
20596
- }
20597
- return {
20598
- type: "link",
20599
- url,
20600
- title: node2.properties?.title ? String(node2.properties.title) : null,
20601
- children: [{ type: "text", value: linkText }]
20602
- };
20739
+ return linkNode;
20603
20740
  },
20604
20741
  td(state, node2, parent) {
20605
20742
  return handleTableCell(state, node2);
@@ -20630,13 +20767,13 @@ const htmlToMarkdown = async (url, options = {}) => {
20630
20767
  const htmlContent = await getHtmlWithPlaywright(url, locale);
20631
20768
  if (!htmlContent) {
20632
20769
  console.error(`No HTML content found for URL: ${url}`);
20633
- return "";
20770
+ return { html: "", markdown: "" };
20634
20771
  }
20635
20772
  const markdown = await convertHtmlToMarkdown(htmlContent, url, locale);
20636
- return markdown;
20773
+ return { html: htmlContent, markdown };
20637
20774
  } catch (error) {
20638
20775
  console.error(`An error occurred during the conversion process for URL: ${url}`, error);
20639
- return "";
20776
+ return { html: "", markdown: "" };
20640
20777
  }
20641
20778
  };
20642
20779
  export {
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-C3IjbMzg.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 o=require("./core-CVMYjtlI.cjs");exports.getMessage=o.getMessage,exports.htmlToMarkdown=o.htmlToMarkdown;
package/dist/index.js CHANGED
@@ -5,7 +5,7 @@ Object.assign(global, {
5
5
  window,
6
6
  self: window
7
7
  });
8
- import { g, h } from "./core-D07ieSmY.js";
8
+ import { g, h } from "./core-DOzMLPij.js";
9
9
  export {
10
10
  g as getMessage,
11
11
  h as htmlToMarkdown
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-C3IjbMzg.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"),c=await import("node:path"),o=e(process.argv.slice(2),{string:["locale"],default:{locale:"en-US"}}),s=o._[0],a=o.locale;s||process.exit(1);try{new URL(s)}catch(n){process.exit(1)}try{const e=await t.htmlToMarkdown(s,{locale:a}),o=`${s.replace(/https?:\/\//,"").replace(/[^a-zA-Z0-9]/g,"_")}_${Date.now()}.md`,n=c.join(process.cwd(),".outputs","raw");r.existsSync(n)||r.mkdirSync(n,{recursive:!0});const i=c.join(n,o);r.writeFileSync(i,e)}catch(n){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-CVMYjtlI.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,13 +6,16 @@ Object.assign(global, {
6
6
  window,
7
7
  self: window
8
8
  });
9
- import { g as getMessage, h as htmlToMarkdown } from "./core-D07ieSmY.js";
9
+ import { g as getMessage, h as htmlToMarkdown } from "./core-DOzMLPij.js";
10
10
  const runCli = async () => {
11
11
  const yargsParser = (await import("yargs-parser")).default;
12
12
  const fs = await import("node:fs");
13
13
  const path = await import("node:path");
14
14
  const argv = yargsParser(process.argv.slice(2), {
15
- string: ["locale"],
15
+ string: ["locale", "output"],
16
+ alias: {
17
+ output: ["o"]
18
+ },
16
19
  default: {
17
20
  locale: "en-US"
18
21
  // デフォルトのロケール
@@ -20,6 +23,7 @@ const runCli = async () => {
20
23
  });
21
24
  const targetUrl = argv._[0];
22
25
  const locale = argv.locale;
26
+ const outputFile = argv.output;
23
27
  if (!targetUrl) {
24
28
  console.error(getMessage(locale, "error_url_required"));
25
29
  console.info(getMessage(locale, "usage"));
@@ -37,19 +41,35 @@ const runCli = async () => {
37
41
  ${getMessage(locale, "cli_start")}`);
38
42
  console.info(getMessage(locale, "cli_url", { url: targetUrl }));
39
43
  console.info(getMessage(locale, "cli_locale", { locale }));
40
- const markdown = await htmlToMarkdown(targetUrl, { locale });
41
- const safeFileName = targetUrl.replace(/https?:\/\//, "").replace(/[^a-zA-Z0-9]/g, "_");
42
- const timestamp = Date.now();
43
- const fileName = `${safeFileName}_${timestamp}.md`;
44
- const outputDir = path.join(process.cwd(), ".outputs", "raw");
45
- if (!fs.existsSync(outputDir)) {
46
- fs.mkdirSync(outputDir, { recursive: true });
44
+ if (outputFile) {
45
+ console.info(getMessage(locale, "cli_output", { path: outputFile }));
46
+ }
47
+ const { markdown, html } = await htmlToMarkdown(targetUrl, { locale });
48
+ let filePath;
49
+ if (outputFile) {
50
+ filePath = path.resolve(process.cwd(), outputFile);
51
+ const outputDir = path.dirname(filePath);
52
+ if (!fs.existsSync(outputDir)) {
53
+ fs.mkdirSync(outputDir, { recursive: true });
54
+ }
55
+ } else {
56
+ const safeFileName = targetUrl.replace(/https?:\/\//, "").replace(/[^a-zA-Z0-9]/g, "_");
57
+ const timestamp = Date.now();
58
+ const fileName = `${safeFileName}_${timestamp}.md`;
59
+ const outputDir = path.join(process.cwd(), ".outputs", "raw");
60
+ if (!fs.existsSync(outputDir)) {
61
+ fs.mkdirSync(outputDir, { recursive: true });
62
+ }
63
+ filePath = path.join(outputDir, fileName);
47
64
  }
48
- const filePath = path.join(outputDir, fileName);
49
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);
50
69
  console.info(`
51
70
  ${getMessage(locale, "success_save")}`);
52
71
  console.info(getMessage(locale, "success_path", { path: filePath }));
72
+ console.info(getMessage(locale, "success_path", { path: htmlFilePath }));
53
73
  } catch (error) {
54
74
  console.error(`
55
75
  ${getMessage(locale, "error_app")}`, error);
@@ -5,6 +5,9 @@ import { HtmlToMarkdownOptions } from './types.js';
5
5
  *
6
6
  * @param url The URL of the web page to convert. / 変換対象のWebページのURL。
7
7
  * @param options Options for the conversion process. / 変換プロセスのオプション。
8
- * @returns A promise that resolves to the Markdown string. / Markdown文字列を解決するPromise。
8
+ * @returns A promise that resolves to an object containing the HTML and Markdown strings. / HTMLとMarkdown文字列を含むオブジェクトを解決するPromise。
9
9
  */
10
- export declare const htmlToMarkdown: (url: string, options?: HtmlToMarkdownOptions) => Promise<string>;
10
+ export declare const htmlToMarkdown: (url: string, options?: HtmlToMarkdownOptions) => Promise<{
11
+ html: string;
12
+ markdown: string;
13
+ }>;
@@ -3,5 +3,7 @@
3
3
  * Rehype プラグインのインデックス
4
4
  */
5
5
  export { rehypeAbsoluteLinks } from './rehype-absolute-links.js';
6
+ export { rehypeNamedAnchors } from './rehype-named-anchors.js';
7
+ export { rehypeParagraphWrapper } from './rehype-paragraph-wrapper.js';
6
8
  export { rehypeSanitizeHtml } from './rehype-sanitize-html.js';
7
9
  export { rehypeWikipediaFootnotes } from './rehype-wikipedia-footnotes.js';
@@ -0,0 +1,2 @@
1
+ import { Root } from 'hast';
2
+ export declare const rehypeNamedAnchors: () => (tree: Root) => void;
@@ -0,0 +1,6 @@
1
+ import { Root } from 'hast';
2
+ /**
3
+ * A rehype plugin to wrap an element and its adjacent anchor span with a paragraph.
4
+ * 要素とそれに隣接するアンカースパンを段落でラップする rehype プラグイン。
5
+ */
6
+ export declare const rehypeParagraphWrapper: () => (tree: Root) => void;
@@ -19,7 +19,7 @@ export interface HtmlToMarkdownOptions {
19
19
  * Message keys for i18n.
20
20
  * i18n 用のメッセージキー
21
21
  */
22
- export type MessageKey = "error_url_required" | "usage" | "example" | "cli_start" | "cli_url" | "cli_locale" | "success_save" | "success_path" | "error_app" | "playwright_launch" | "playwright_goto" | "playwright_get_content" | "playwright_close" | "playwright_error" | "convert_start" | "convert_success" | "url_parse_error";
22
+ export type MessageKey = "error_url_required" | "usage" | "example" | "cli_start" | "cli_url" | "cli_locale" | "cli_output" | "success_save" | "success_path" | "error_app" | "playwright_launch" | "playwright_goto" | "playwright_get_content" | "playwright_close" | "playwright_error" | "convert_start" | "convert_success" | "url_parse_error";
23
23
  /**
24
24
  * Supported locales.
25
25
  * サポートされているロケール
@@ -0,0 +1 @@
1
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiquants/html-to-markdown",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "HTML to Markdown converter",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -20,12 +20,15 @@
20
20
  "LICENSE"
21
21
  ],
22
22
  "scripts": {
23
+ "dev": "NODE_OPTIONS='--enable-source-maps' vite-node --inspect=9229 --watch main.ts --",
24
+ "debug": "NODE_OPTIONS='--enable-source-maps' vite-node --inspect-brk=9229 main.ts --",
23
25
  "start": "vite-node main.ts",
24
26
  "build": "vite build",
25
27
  "lint": "biome lint app/",
26
28
  "format": "biome format --write",
27
29
  "check": "biome check --fix src/",
28
30
  "test": "vitest run",
31
+ "test1": "pnpm -F @aiquants/html-to-markdown test --test-name-pattern 'rehypeNamedAnchors plugin'",
29
32
  "coverage": "vitest run --coverage"
30
33
  },
31
34
  "keywords": [