@isdk/mdast-plus 0.1.2 → 0.2.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.
Files changed (43) hide show
  1. package/README.cn.md +69 -22
  2. package/README.md +69 -22
  3. package/dist/index.d.mts +564 -199
  4. package/dist/index.d.ts +564 -199
  5. package/dist/index.js +1 -1
  6. package/dist/index.mjs +1 -1
  7. package/docs/README.md +69 -22
  8. package/docs/_media/CONTRIBUTING.md +53 -24
  9. package/docs/_media/README.cn.md +69 -22
  10. package/docs/classes/MdastBasePipeline.md +416 -0
  11. package/docs/classes/MdastPipeline.md +611 -0
  12. package/docs/enumerations/PipelineStage.md +62 -0
  13. package/docs/functions/astCompiler.md +25 -0
  14. package/docs/functions/jsonParser.md +24 -0
  15. package/docs/functions/mdast.md +4 -4
  16. package/docs/globals.md +17 -10
  17. package/docs/interfaces/MdastDataOrigin.md +8 -8
  18. package/docs/interfaces/MdastFormat.md +71 -0
  19. package/docs/interfaces/MdastMark.md +4 -4
  20. package/docs/interfaces/MdastPlugin.md +57 -25
  21. package/docs/interfaces/MdastSub.md +4 -4
  22. package/docs/interfaces/MdastSup.md +4 -4
  23. package/docs/interfaces/ReadabilityOptions.md +33 -0
  24. package/docs/type-aliases/PipelineStageName.md +13 -0
  25. package/docs/variables/DefaultPipelineStage.md +13 -0
  26. package/docs/variables/astFormat.md +15 -0
  27. package/docs/variables/htmlFormat.md +6 -4
  28. package/docs/variables/htmlReadability.md +13 -0
  29. package/docs/variables/htmlReadabilityPlugin.md +27 -0
  30. package/docs/variables/htmlReadabilityPlugins.md +13 -0
  31. package/docs/variables/markdownFormat.md +6 -4
  32. package/docs/variables/restoreReadabilityMetaPlugin.md +49 -0
  33. package/package.json +18 -9
  34. package/docs/classes/FluentProcessor.md +0 -210
  35. package/docs/functions/htmlStringify.md +0 -23
  36. package/docs/functions/markdownCommon.md +0 -23
  37. package/docs/interfaces/ConvertResult.md +0 -39
  38. package/docs/interfaces/MdastAsset.md +0 -41
  39. package/docs/interfaces/MdastFormatDefinition.md +0 -51
  40. package/docs/interfaces/MdastReader.md +0 -41
  41. package/docs/interfaces/MdastTransformer.md +0 -33
  42. package/docs/interfaces/MdastWriter.md +0 -47
  43. package/docs/type-aliases/Stage.md +0 -13
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- "use strict";var t,e=Object.create,r=Object.defineProperty,i=Object.getOwnPropertyDescriptor,n=Object.getOwnPropertyNames,s=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(t,e,s,o)=>{if(e&&"object"==typeof e||"function"==typeof e)for(let u of n(e))a.call(t,u)||u===s||r(t,u,{get:()=>e[u],enumerable:!(o=i(e,u))||o.enumerable});return t},u=(t,i,n)=>(n=null!=t?e(s(t)):{},o(!i&&t&&t.__esModule?n:r(n,"default",{value:t,enumerable:!0}),t)),c={};((t,e)=>{for(var i in e)r(t,i,{get:e[i],enumerable:!0})})(c,{FluentProcessor:()=>P,htmlFormat:()=>F,htmlStringify:()=>D,markdownCommon:()=>q,markdownFormat:()=>z,mdast:()=>$}),module.exports=(t=c,o(r({},"__esModule",{value:!0}),t));var l=require("unified"),m=u(require("remark-parse")),h=u(require("remark-stringify")),f=u(require("remark-gfm")),p=u(require("remark-directive")),d=u(require("remark-math")),y=u(require("remark-frontmatter")),v=require("unist-util-visit");function g(t,e){return{type:t,children:e,data:{hName:t}}}var w={mark:(t,e)=>{const r={type:"mark",children:t.all(e)};return t.patch(e,r),r},sub:(t,e)=>{const r={type:"sub",children:t.all(e)};return t.patch(e,r),r},sup:(t,e)=>{const r={type:"sup",children:t.all(e)};return t.patch(e,r),r}},b={mark:(t,e,r)=>"=="+r.containerPhrasing(t,{before:"==",after:"=="})+"==",sub:(t,e,r)=>"~"+r.containerPhrasing(t,{before:"~",after:"~"})+"~",sup:(t,e,r)=>"^"+r.containerPhrasing(t,{before:"^",after:"^"})+"^"},k={name:"normalize-inline-styles",stage:"normalize",transform:t=>{!function(t){(0,v.visit)(t,"text",(t,e,r)=>{if(!r||void 0===e)return;const i=t.value;let n=0;const s=[];let a=!1;const o=/(==[^=]+==|~[^~]+~|\^[^^]+\^)/g;let u;for(;null!==(u=o.exec(i));){a=!0;const t=u[0],e=u.index;e>n&&s.push({type:"text",value:i.slice(n,e)});let r="mark",c="";t.startsWith("==")?(r="mark",c=t.slice(2,-2)):t.startsWith("~")?(r="sub",c=t.slice(1,-1)):t.startsWith("^")&&(r="sup",c=t.slice(1,-1)),s.push(g(r,[{type:"text",value:c}])),n=o.lastIndex}return a?(n<i.length&&s.push({type:"text",value:i.slice(n)}),r.children.splice(e,1,...s),e+s.length):void 0})}(t)}};function q(){const t=this.data();var e,r;r={handlers:b},t[e="toMarkdownExtensions"]?t[e].push(r):t[e]=[r],this.use(f.default,{singleTilde:!1}).use(p.default).use(d.default).use(y.default,["yaml","toml"])}var z={parse:t=>t.use(m.default).use(q),stringify:t=>t.use(h.default).use(q)},x=u(require("rehype-parse")),S=u(require("rehype-remark")),j=u(require("remark-rehype")),O=u(require("rehype-sanitize")),N=u(require("rehype-stringify"));function D(){this.use(j.default).use(O.default,{...O.defaultSchema,tagNames:[...O.defaultSchema.tagNames||[],"mark","sub","sup"],attributes:{...O.defaultSchema.attributes,"*":[...O.defaultSchema.attributes?.["*"]||[],"className","id","style"],td:[...O.defaultSchema.attributes?.td||[],"rowSpan","colSpan","rowspan","colspan"],th:[...O.defaultSchema.attributes?.th||[],"rowSpan","colSpan","rowspan","colspan"],img:[...O.defaultSchema.attributes?.img||[],"width","height"]}}).use(N.default)}var F={parse:t=>t.use(x.default).use(S.default,{handlers:w}),stringify:t=>t.use(D)},M=require("unist-util-visit"),T={error:"danger",warn:"warning",success:"tip",important:"important",caution:"caution",note:"note"},C={name:"normalize-directive",stage:"normalize",order:10,transform:async t=>{(0,M.visit)(t,["containerDirective","leafDirective","textDirective"],t=>{const e=t,r=e.name.toLowerCase();if(e.name=T[r]||r,e.children&&e.children.length>0){const t=e.children[0];if(t.data?.directiveLabel||"directiveLabel"===t.type){const r=t;let i="";(0,M.visit)(r,"text",t=>{i+=t.value}),i&&!e.attributes?.title&&(e.attributes=e.attributes||{},e.attributes.title=i.trim()),e.children.shift()}}e.attributes?.title&&(e.attributes.title=String(e.attributes.title).trim()),e.data=e.data||{},e.data.hName=e.data.hName||("containerDirective"===e.type?"div":"span"),e.data.hProperties={...e.data.hProperties||{},...e.attributes,className:[e.name,e.data.hProperties?.className].filter(Boolean).join(" ")}})}},I=require("unist-util-visit"),L={name:"normalize-table-span",stage:"normalize",order:20,transform:async t=>{(0,I.visit)(t,"tableCell",t=>{if(t.data){const{rowspan:e,colspan:r}=t.data;t.data.hProperties=t.data.hProperties||{},void 0!==e&&(t.data.hProperties.rowSpan=e,delete t.data.rowspan),void 0!==r&&(t.data.hProperties.colSpan=r,delete t.data.colspan)}})}},_=require("unist-util-visit"),A=require("shell-quote");var B={name:"extract-code-meta",stage:"normalize",order:30,transform:async t=>{(0,_.visit)(t,"code",t=>{if(t.meta){const e=function(t){const e={},r=(0,A.parse)(t);for(const t of r)if("string"==typeof t){const r=t.split("=",2);2===r.length?e[r[0]]=r[1]:e[t]="true"}return e}(t.meta),r=t.data=t.data||{};e.title&&(r.title=e.title),e.filename&&(r.filename=e.filename),r.kv={...r.kv||{},...e}}})}},E=require("unist-util-visit"),H={name:"image-size",stage:"normalize",order:40,transform:async t=>{(0,E.visit)(t,"image",t=>{const e=t.data=t.data||{},r=e.hProperties=e.hProperties||{},i=/[#?&](?:width=([0-9]+))?(?:&?height=([0-9]+))?(?:=([0-9]+)x([0-9]+))?$/,n=t.url.match(i);if(n){const e=n[1]||n[3],s=n[2]||n[4];e&&!r.width&&(r.width=parseInt(e,10)),s&&!r.height&&(r.height=parseInt(s,10)),t.url=t.url.replace(i,"")}e.width&&!r.width&&(r.width=e.width),e.height&&!r.height&&(r.height=e.height)})}},J=class t{constructor(t){this.inputFormat="markdown",this.plugins=[],this.globalData={},this.input=t,this.processor=(0,l.unified)(),this.use(C),this.use(L),this.use(B),this.use(H),this.use(k)}static registerFormat(e,r){t.formats[e.toLowerCase()]=r}from(t){return this.inputFormat=t.toLowerCase(),this}use(t){return this.plugins.push(t),this}data(t){return this.globalData={...this.globalData,...t},this}async to(e){e=e.toLowerCase();const r=t.formats[this.inputFormat];r?.parse&&r.parse(this.processor);let i="string"==typeof this.input?this.processor.parse(this.input):this.input;i=await this.processor.run(i),i.data={...i.data,...this.globalData};const n=[...this.plugins].sort((t,e)=>{const r={normalize:0,compile:1,finalize:2},i=t.stage||"normalize",n=e.stage||"normalize";return i!==n?r[i]-r[n]:(t.order||0)-(e.order||0)});for(const t of n)await t.transform(i,this.processor);const s=(0,l.unified)().data("settings",this.processor.data("settings")),a=t.formats[e];a?.stringify&&a.stringify(s),"markdown"!==e||a||z.stringify(s);const o=await s.run(i),u=s.stringify(o),c=[];return i.data&&i.data.assets&&c.push(...i.data.assets),{content:u,assets:c}}async toMarkdown(){return(await this.to("markdown")).content}async toHTML(){return(await this.to("html")).content}async toAST(){return(await this.to("ast")).content}};J.formats={markdown:z,html:F,ast:{parse:t=>{t.Parser=t=>JSON.parse(t)},stringify:t=>{t.Compiler=t=>t}}};var P=J;function $(t){return new P(t)}
1
+ "use strict";var t,e=Object.create,r=Object.defineProperty,i=Object.getOwnPropertyDescriptor,n=Object.getOwnPropertyNames,s=Object.getPrototypeOf,o=Object.prototype.hasOwnProperty,a=(t,e,s,a)=>{if(e&&"object"==typeof e||"function"==typeof e)for(let u of n(e))o.call(t,u)||u===s||r(t,u,{get:()=>e[u],enumerable:!(a=i(e,u))||a.enumerable});return t},u=(t,i,n)=>(n=null!=t?e(s(t)):{},a(!i&&t&&t.__esModule?n:r(n,"default",{value:t,enumerable:!0}),t)),l={};((t,e)=>{for(var i in e)r(t,i,{get:e[i],enumerable:!0})})(l,{DefaultPipelineStage:()=>p,MdastBasePipeline:()=>Q,MdastPipeline:()=>U,PipelineStage:()=>c,astCompiler:()=>V,astFormat:()=>G,htmlFormat:()=>C,htmlReadability:()=>Y,htmlReadabilityPlugin:()=>Z,htmlReadabilityPlugins:()=>et,jsonParser:()=>_,markdownFormat:()=>R,mdast:()=>W,restoreReadabilityMetaPlugin:()=>tt}),module.exports=(t=l,a(r({},"__esModule",{value:!0}),t));var c=(t=>(t[t.parse=0]="parse",t[t.normalize=100]="normalize",t[t.compile=200]="compile",t[t.finalize=300]="finalize",t[t.stringify=400]="stringify",t))(c||{}),p=200,f=require("unified"),h=require("vfile"),m=u(require("remark-parse")),g=u(require("remark-stringify")),d=u(require("remark-gfm")),y=u(require("remark-directive")),b=u(require("remark-math")),w=u(require("remark-frontmatter")),v=require("unist-util-visit");function k(t,e){return{type:t,children:e,data:{hName:t}}}var q={mark:(t,e,r)=>"=="+r.containerPhrasing(t,{before:"==",after:"=="})+"==",sub:(t,e,r)=>"~"+r.containerPhrasing(t,{before:"~",after:"~"})+"~",sup:(t,e,r)=>"^"+r.containerPhrasing(t,{before:"^",after:"^"})+"^"},j={plugin:()=>t=>{!function(t){(0,v.visit)(t,"text",(t,e,r)=>{if(!r||void 0===e)return;const i=t.value;let n=0;const s=[];let o=!1;const a=/(==[^=]+==|~[^~]+~|\^[^^]+\^)/g;let u;for(;null!==(u=a.exec(i));){o=!0;const t=u[0],e=u.index;e>n&&s.push({type:"text",value:i.slice(n,e)});let r="mark",l="";t.startsWith("==")?(r="mark",l=t.slice(2,-2)):t.startsWith("~")?(r="sub",l=t.slice(1,-1)):t.startsWith("^")&&(r="sup",l=t.slice(1,-1)),s.push(k(r,[{type:"text",value:l}])),n=a.lastIndex}return o?(n<i.length&&s.push({type:"text",value:i.slice(n)}),r.children.splice(e,1,...s),e+s.length):void 0})}(t)},stage:100},M=require("unist-util-visit"),P={error:"danger",warn:"warning",success:"tip",important:"important",caution:"caution",note:"note"},x={plugin:()=>async t=>{(0,M.visit)(t,["containerDirective","leafDirective","textDirective"],t=>{const e=t,r=e.name.toLowerCase();if(e.name=P[r]||r,e.children&&e.children.length>0){const t=e.children[0];if(t.data?.directiveLabel||"directiveLabel"===t.type){const r=t;let i="";(0,M.visit)(r,"text",t=>{i+=t.value}),i&&!e.attributes?.title&&(e.attributes=e.attributes||{},e.attributes.title=i.trim()),e.children.shift()}}e.attributes?.title&&(e.attributes.title=String(e.attributes.title).trim()),e.data=e.data||{},e.data.hName=e.data.hName||("containerDirective"===e.type?"div":"span"),e.data.hProperties={...e.data.hProperties||{},...e.attributes,className:[e.name,e.data.hProperties?.className].filter(Boolean).join(" ")}})},stage:100,order:10},F=require("unist-util-visit"),S={plugin:()=>async t=>{(0,F.visit)(t,"tableCell",t=>{if(t.data){const{rowspan:e,colspan:r}=t.data;t.data.hProperties=t.data.hProperties||{},void 0!==e&&(t.data.hProperties.rowSpan=e,delete t.data.rowspan),void 0!==r&&(t.data.hProperties.colSpan=r,delete t.data.colspan)}})},stage:100,order:20},O=require("unist-util-visit"),A=require("shell-quote");var D={plugin:()=>async t=>{(0,O.visit)(t,"code",t=>{if(t.meta){const e=function(t){const e={},r=(0,A.parse)(t);for(const t of r)if("string"==typeof t){const r=t.split("=",2);2===r.length?e[r[0]]=r[1]:e[t]="true"}return e}(t.meta),r=t.data=t.data||{};e.title&&(r.title=e.title),e.filename&&(r.filename=e.filename),r.kv={...r.kv||{},...e}}})},stage:100,order:30},T=require("unist-util-visit"),N={plugin:()=>async t=>{(0,T.visit)(t,"image",t=>{const e=t.data=t.data||{},r=e.hProperties=e.hProperties||{},i=/[#?&](?:width=([0-9]+))?(?:&?height=([0-9]+))?(?:=([0-9]+)x([0-9]+))?$/,n=t.url.match(i);if(n){const e=n[1]||n[3],s=n[2]||n[4];e&&!r.width&&(r.width=parseInt(e,10)),s&&!r.height&&(r.height=parseInt(s,10)),t.url=t.url.replace(i,"")}e.width&&!r.width&&(r.width=e.width),e.height&&!r.height&&(r.height=e.height)})},stage:100,order:40},z=[{plugin:m.default,stage:0},{plugin:d.default,options:[{singleTilde:!1}],stage:0},{plugin:y.default,stage:0},{plugin:b.default,stage:0},{plugin:w.default,options:[["yaml","toml"]],stage:0},x,S,D,N,j],E=[{plugin:g.default,options:[{handlers:q}],stage:400},{plugin:d.default,options:[{singleTilde:!1}],stage:400},{plugin:y.default,stage:400},{plugin:b.default,stage:400},{plugin:w.default,options:[["yaml","toml"]],stage:400}];E.forEach(t=>{t.plugin===g.default?t.order=100:t.order=10});var R={id:"markdown",title:"Markdown (GFM + Directives)",extensions:["md","markdown","mdown","mkdn"],mediaTypes:["text/markdown"],input:z,output:E},H=u(require("rehype-parse")),$=u(require("rehype-remark")),B=u(require("remark-rehype")),I=u(require("rehype-sanitize")),L=u(require("rehype-stringify")),C={id:"html",title:"HTML",extensions:["html","htm"],mediaTypes:["text/html"],input:[{name:"rehype-parse",plugin:H.default,stage:0},{name:"rehype-remark",plugin:$.default,options:[{handlers:{mark:(t,e)=>{const r={type:"mark",children:t.all(e)};return t.patch(e,r),r},sub:(t,e)=>{const r={type:"sub",children:t.all(e)};return t.patch(e,r),r},sup:(t,e)=>{const r={type:"sup",children:t.all(e)};return t.patch(e,r),r}}}],stage:0}],output:[{plugin:B.default,stage:300,order:10},{plugin:I.default,options:[{...I.defaultSchema,tagNames:[...I.defaultSchema.tagNames||[],"mark","sub","sup"],attributes:{...I.defaultSchema.attributes,"*":[...I.defaultSchema.attributes?.["*"]||[],"className","id","style"],td:[...I.defaultSchema.attributes?.td||[],"rowSpan","colSpan","rowspan","colspan"],th:[...I.defaultSchema.attributes?.th||[],"rowSpan","colSpan","rowspan","colspan"],img:[...I.defaultSchema.attributes?.img||[],"width","height"]}}],stage:300,order:20},{plugin:L.default,stage:400}]};function V(){this.Compiler=t=>t}function _(){this.Parser=t=>JSON.parse(t)}var G={id:"ast",title:"MDAST",input:[{plugin:_,stage:0},x,S,D,N,j],output:[{plugin:V,options:[],stage:400}]};function J(t){return"object"==typeof t&&null!==t&&"string"==typeof t.type}var K=class t{constructor(t){this.queue=[],this._data={},this.input=t}static register(t){this.registry.set(t.id,t)}static getFormat(t){return this.registry.get(t)}data(t,e){return"string"==typeof t?this._data[t]=e:Object.assign(this._data,t),this}getFormat(t){return this.constructor.getFormat(t)}resolveFormat(t){if("string"==typeof t){const e=this.getFormat(t);if(!e)throw new Error(`[MdastPlus] Format '${t}' is not registered.`);return e}return t}toRuntimeEntry(t,e,r){let i=e;void 0!==t.stage&&(i="string"==typeof t.stage?c[t.stage]??e:t.stage);let n=t.options||[];const s=t.name||t.plugin.name;if(r&&s&&s in r){const e=r[s];"object"!=typeof e||null===e||Array.isArray(e)?n=Array.isArray(e)?e:[e]:("main"in e&&(t.main=!!e.main),"before"in e&&(t.before=e.before),"after"in e&&(t.after=e.after),n=[e])}return{name:s,plugin:t.plugin,options:n,stage:i,order:t.order||0,main:t.main,before:t.before,after:t.after}}ensureInputPlugins(e,r,i=400){const n=e.some(t=>0===(t.stage??p)),s=J(this.input);if(!n){let n=[];if(s){const e=t.getFormat("ast");e&&e.input&&(n=e.input)}else{const e=t.getFormat("markdown");e&&e.input&&(n=e.input)}for(const t of n){const n=this.toRuntimeEntry(t,0,r);(n.stage??p)<=i&&e.push(n)}}}from(t,e){const r=this.resolveFormat(t);if(!r.input||0===r.input.length)throw new Error(`[MdastPlus] Format '${r.id}' does not support input.`);for(const t of r.input)this.queue.push(this.toRuntimeEntry(t,0,e));return this}async to(t,e){const r=this.resolveFormat(t);if(!r.output)throw new Error(`[MdastPlus] Format '${r.id}' does not support output.`);const i=[...this.queue];this.ensureInputPlugins(i,e);for(const t of r.output)i.push(this.toRuntimeEntry(t,300,e));const n=this.assembleProcessor(i);if(J(this.input)){const t=await n.run(this.input),e=n.stringify(t),r=new h.VFile;return"string"==typeof e||Buffer.isBuffer(e)?r.value=e:r.result=e,r}return n.process(this.input)}use(t,...e){return this.useAt("compile",t,...e)}useAt(t,e,...r){if(Array.isArray(t)){for(const i of t)this.useAt(i,e,...r);return this}if(Array.isArray(e)){for(const i of e)this.useAt(t,i,...r);return this}if("object"==typeof t&&null!==t&&"plugin"in t){const i=t,n=void 0!==i.stage?"string"==typeof i.stage?i.stage:c[i.stage]:"compile",s=void 0!==e?[e,...r]:i.options;this.queue.push(this.toRuntimeEntry({...i,options:s},c[n]))}else{const i=t;if("object"==typeof e&&null!==e&&"plugin"in e){const t=e,n=r.length>0?r:t.options;this.queue.push(this.toRuntimeEntry({...t,options:n},c[i]))}else e&&this.queue.push({plugin:e,options:r,stage:c[i],order:0})}return this}priority(t){const e=this.queue[this.queue.length-1];return e&&(e.order=t),this}assembleProcessor(t){const e={};for(const r of t){const t=r.stage??p;e[t]||(e[t]=[]),e[t].push(r)}const r=[],i=Object.keys(e).map(Number).sort((t,e)=>t-e);for(const t of i){const i=e[t].sort((t,e)=>(t.order||0)-(e.order||0)),n=i.findIndex(t=>t.main);if(-1!==n){const t=i[n];i.splice(n,1),i[0]=t}let s=!0,o=0;for(;s&&o<i.length;){s=!1,o++;for(let t=0;t<i.length;t++){const e=i[t];if(e.after){const r=i.findIndex(t=>t.name===e.after);if(-1!==r&&r>t){i.splice(t,1),i.splice(r,0,e),s=!0;break}}if(e.before){const r=i.findIndex(t=>t.name===e.before);if(-1!==r&&r<t){i.splice(t,1),i.splice(r,0,e),s=!0;break}}}}r.push(...i)}const n=(0,f.unified)();Object.keys(this._data).length>0&&n.data(this._data);for(const t of r)n.use(t.plugin,...t.options||[]);return n}};K.registry=new Map;var Q=K,U=class extends Q{async toMarkdown(){const t=await this.to("markdown");return String(t)}toMarkdownVFile(){return this.to("markdown")}async toHtml(){const t=await this.to("html");return String(t)}toHtmlVFile(){return this.to("html")}async toAst(t){if(t?.stage){const e=c[t.stage],r=this.queue.filter(t=>(t.stage??p)<=e);r.push({plugin:V,options:[],stage:400,order:0}),this.ensureInputPlugins(r,t.overrides,e);const i=this.assembleProcessor(r);if(J(this.input)){return await i.run(this.input)}return(await i.process(this.input)).result}return(await this.to("ast",t?.overrides)).result}toHTML(){return this.toHtml()}toAST(t){return this.toAst(t)}};function W(t){return new U(t)}U.register(R),U.register(C),U.register(G);var X=require("hast-util-from-html"),Y=function(t){let e={};"boolean"==typeof t?e={enable:t}:t&&(e=t);const{enable:r,readability:i,jsdom:n}=e;!1!==r&&(this.parser=function(t,e){let r,s;try{r=require("jsdom").JSDOM;s=require("@mozilla/readability").Readability}catch(t){throw new Error("[html-readability] Dependency missing. Please install 'jsdom' and '@mozilla/readability'.")}const o=new s(new r(t,n).window.document,i).parse();if(!o||!o.content)return(0,X.fromHtml)(t,{fragment:!0});const a=(0,X.fromHtml)(o.content,{fragment:!0}),u={title:o.title,byline:o.byline,excerpt:o.excerpt,siteName:o.siteName,lang:o.lang};return e&&(e.data=e.data||{},e.data.readability=u),a&&(a.data=a.data||{},a.data.readability=u),a})},Z={name:"readability",plugin:Y,stage:0},tt={name:"restore-readability-meta",plugin:()=>(t,e)=>{e.data?.readability&&(t.data=t.data||{},t.data.readability=e.data.readability)},stage:0,after:"rehype-remark"},et=[Z,tt];
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- import{unified as t}from"unified";import r from"remark-parse";import i from"remark-stringify";import e from"remark-gfm";import s from"remark-directive";import n from"remark-math";import a from"remark-frontmatter";import{visit as o}from"unist-util-visit";function m(t,r){return{type:t,children:r,data:{hName:t}}}var c={mark:(t,r)=>{const i={type:"mark",children:t.all(r)};return t.patch(r,i),i},sub:(t,r)=>{const i={type:"sub",children:t.all(r)};return t.patch(r,i),i},sup:(t,r)=>{const i={type:"sup",children:t.all(r)};return t.patch(r,i),i}},l={mark:(t,r,i)=>"=="+i.containerPhrasing(t,{before:"==",after:"=="})+"==",sub:(t,r,i)=>"~"+i.containerPhrasing(t,{before:"~",after:"~"})+"~",sup:(t,r,i)=>"^"+i.containerPhrasing(t,{before:"^",after:"^"})+"^"},p={name:"normalize-inline-styles",stage:"normalize",transform:t=>{!function(t){o(t,"text",(t,r,i)=>{if(!i||void 0===r)return;const e=t.value;let s=0;const n=[];let a=!1;const o=/(==[^=]+==|~[^~]+~|\^[^^]+\^)/g;let c;for(;null!==(c=o.exec(e));){a=!0;const t=c[0],r=c.index;r>s&&n.push({type:"text",value:e.slice(s,r)});let i="mark",l="";t.startsWith("==")?(i="mark",l=t.slice(2,-2)):t.startsWith("~")?(i="sub",l=t.slice(1,-1)):t.startsWith("^")&&(i="sup",l=t.slice(1,-1)),n.push(m(i,[{type:"text",value:l}])),s=o.lastIndex}return a?(s<e.length&&n.push({type:"text",value:e.slice(s)}),i.children.splice(r,1,...n),r+n.length):void 0})}(t)}};function f(){const t=this.data();var r,i;i={handlers:l},t[r="toMarkdownExtensions"]?t[r].push(i):t[r]=[i],this.use(e,{singleTilde:!1}).use(s).use(n).use(a,["yaml","toml"])}var h={parse:t=>t.use(r).use(f),stringify:t=>t.use(i).use(f)};import u from"rehype-parse";import d from"rehype-remark";import v from"remark-rehype";import y,{defaultSchema as g}from"rehype-sanitize";import w from"rehype-stringify";function k(){this.use(v).use(y,{...g,tagNames:[...g.tagNames||[],"mark","sub","sup"],attributes:{...g.attributes,"*":[...g.attributes?.["*"]||[],"className","id","style"],td:[...g.attributes?.td||[],"rowSpan","colSpan","rowspan","colspan"],th:[...g.attributes?.th||[],"rowSpan","colSpan","rowspan","colspan"],img:[...g.attributes?.img||[],"width","height"]}}).use(w)}var z={parse:t=>t.use(u).use(d,{handlers:c}),stringify:t=>t.use(k)};import{visit as b}from"unist-util-visit";var x={error:"danger",warn:"warning",success:"tip",important:"important",caution:"caution",note:"note"},S={name:"normalize-directive",stage:"normalize",order:10,transform:async t=>{b(t,["containerDirective","leafDirective","textDirective"],t=>{const r=t,i=r.name.toLowerCase();if(r.name=x[i]||i,r.children&&r.children.length>0){const t=r.children[0];if(t.data?.directiveLabel||"directiveLabel"===t.type){let i="";b(t,"text",t=>{i+=t.value}),i&&!r.attributes?.title&&(r.attributes=r.attributes||{},r.attributes.title=i.trim()),r.children.shift()}}r.attributes?.title&&(r.attributes.title=String(r.attributes.title).trim()),r.data=r.data||{},r.data.hName=r.data.hName||("containerDirective"===r.type?"div":"span"),r.data.hProperties={...r.data.hProperties||{},...r.attributes,className:[r.name,r.data.hProperties?.className].filter(Boolean).join(" ")}})}};import{visit as N}from"unist-util-visit";var D={name:"normalize-table-span",stage:"normalize",order:20,transform:async t=>{N(t,"tableCell",t=>{if(t.data){const{rowspan:r,colspan:i}=t.data;t.data.hProperties=t.data.hProperties||{},void 0!==r&&(t.data.hProperties.rowSpan=r,delete t.data.rowspan),void 0!==i&&(t.data.hProperties.colSpan=i,delete t.data.colspan)}})}};import{visit as M}from"unist-util-visit";import{parse as T}from"shell-quote";var I={name:"extract-code-meta",stage:"normalize",order:30,transform:async t=>{M(t,"code",t=>{if(t.meta){const r=function(t){const r={},i=T(t);for(const t of i)if("string"==typeof t){const i=t.split("=",2);2===i.length?r[i[0]]=i[1]:r[t]="true"}return r}(t.meta),i=t.data=t.data||{};r.title&&(i.title=r.title),r.filename&&(i.filename=r.filename),i.kv={...i.kv||{},...r}}})}};import{visit as L}from"unist-util-visit";var q={name:"image-size",stage:"normalize",order:40,transform:async t=>{L(t,"image",t=>{const r=t.data=t.data||{},i=r.hProperties=r.hProperties||{},e=/[#?&](?:width=([0-9]+))?(?:&?height=([0-9]+))?(?:=([0-9]+)x([0-9]+))?$/,s=t.url.match(e);if(s){const r=s[1]||s[3],n=s[2]||s[4];r&&!i.width&&(i.width=parseInt(r,10)),n&&!i.height&&(i.height=parseInt(n,10)),t.url=t.url.replace(e,"")}r.width&&!i.width&&(i.width=r.width),r.height&&!i.height&&(i.height=r.height)})}},A=class r{constructor(r){this.inputFormat="markdown",this.plugins=[],this.globalData={},this.input=r,this.processor=t(),this.use(S),this.use(D),this.use(I),this.use(q),this.use(p)}static registerFormat(t,i){r.formats[t.toLowerCase()]=i}from(t){return this.inputFormat=t.toLowerCase(),this}use(t){return this.plugins.push(t),this}data(t){return this.globalData={...this.globalData,...t},this}async to(i){i=i.toLowerCase();const e=r.formats[this.inputFormat];e?.parse&&e.parse(this.processor);let s="string"==typeof this.input?this.processor.parse(this.input):this.input;s=await this.processor.run(s),s.data={...s.data,...this.globalData};const n=[...this.plugins].sort((t,r)=>{const i={normalize:0,compile:1,finalize:2},e=t.stage||"normalize",s=r.stage||"normalize";return e!==s?i[e]-i[s]:(t.order||0)-(r.order||0)});for(const t of n)await t.transform(s,this.processor);const a=t().data("settings",this.processor.data("settings")),o=r.formats[i];o?.stringify&&o.stringify(a),"markdown"!==i||o||h.stringify(a);const m=await a.run(s),c=a.stringify(m),l=[];return s.data&&s.data.assets&&l.push(...s.data.assets),{content:c,assets:l}}async toMarkdown(){return(await this.to("markdown")).content}async toHTML(){return(await this.to("html")).content}async toAST(){return(await this.to("ast")).content}};A.formats={markdown:h,html:z,ast:{parse:t=>{t.Parser=t=>JSON.parse(t)},stringify:t=>{t.Compiler=t=>t}}};var B=A;function C(t){return new B(t)}export{B as FluentProcessor,z as htmlFormat,k as htmlStringify,f as markdownCommon,h as markdownFormat,C as mdast};
1
+ var t=(t=>"undefined"!=typeof require?require:"undefined"!=typeof Proxy?new Proxy(t,{get:(t,e)=>("undefined"!=typeof require?require:t)[e]}):t)(function(t){if("undefined"!=typeof require)return require.apply(this,arguments);throw Error('Dynamic require of "'+t+'" is not supported')}),e=(t=>(t[t.parse=0]="parse",t[t.normalize=100]="normalize",t[t.compile=200]="compile",t[t.finalize=300]="finalize",t[t.stringify=400]="stringify",t))(e||{}),r=200;import{unified as i}from"unified";import{VFile as n}from"vfile";import o from"remark-parse";import s from"remark-stringify";import a from"remark-gfm";import u from"remark-directive";import p from"remark-math";import l from"remark-frontmatter";import{visit as f}from"unist-util-visit";function m(t,e){return{type:t,children:e,data:{hName:t}}}var c={plugin:()=>t=>{!function(t){f(t,"text",(t,e,r)=>{if(!r||void 0===e)return;const i=t.value;let n=0;const o=[];let s=!1;const a=/(==[^=]+==|~[^~]+~|\^[^^]+\^)/g;let u;for(;null!==(u=a.exec(i));){s=!0;const t=u[0],e=u.index;e>n&&o.push({type:"text",value:i.slice(n,e)});let r="mark",p="";t.startsWith("==")?(r="mark",p=t.slice(2,-2)):t.startsWith("~")?(r="sub",p=t.slice(1,-1)):t.startsWith("^")&&(r="sup",p=t.slice(1,-1)),o.push(m(r,[{type:"text",value:p}])),n=a.lastIndex}return s?(n<i.length&&o.push({type:"text",value:i.slice(n)}),r.children.splice(e,1,...o),e+o.length):void 0})}(t)},stage:100};import{visit as h}from"unist-util-visit";var g={error:"danger",warn:"warning",success:"tip",important:"important",caution:"caution",note:"note"},d={plugin:()=>async t=>{h(t,["containerDirective","leafDirective","textDirective"],t=>{const e=t,r=e.name.toLowerCase();if(e.name=g[r]||r,e.children&&e.children.length>0){const t=e.children[0];if(t.data?.directiveLabel||"directiveLabel"===t.type){let r="";h(t,"text",t=>{r+=t.value}),r&&!e.attributes?.title&&(e.attributes=e.attributes||{},e.attributes.title=r.trim()),e.children.shift()}}e.attributes?.title&&(e.attributes.title=String(e.attributes.title).trim()),e.data=e.data||{},e.data.hName=e.data.hName||("containerDirective"===e.type?"div":"span"),e.data.hProperties={...e.data.hProperties||{},...e.attributes,className:[e.name,e.data.hProperties?.className].filter(Boolean).join(" ")}})},stage:100,order:10};import{visit as y}from"unist-util-visit";var v={plugin:()=>async t=>{y(t,"tableCell",t=>{if(t.data){const{rowspan:e,colspan:r}=t.data;t.data.hProperties=t.data.hProperties||{},void 0!==e&&(t.data.hProperties.rowSpan=e,delete t.data.rowspan),void 0!==r&&(t.data.hProperties.colSpan=r,delete t.data.colspan)}})},stage:100,order:20};import{visit as w}from"unist-util-visit";import{parse as b}from"shell-quote";var k={plugin:()=>async t=>{w(t,"code",t=>{if(t.meta){const e=function(t){const e={},r=b(t);for(const t of r)if("string"==typeof t){const r=t.split("=",2);2===r.length?e[r[0]]=r[1]:e[t]="true"}return e}(t.meta),r=t.data=t.data||{};e.title&&(r.title=e.title),e.filename&&(r.filename=e.filename),r.kv={...r.kv||{},...e}}})},stage:100,order:30};import{visit as x}from"unist-util-visit";var M={plugin:()=>async t=>{x(t,"image",t=>{const e=t.data=t.data||{},r=e.hProperties=e.hProperties||{},i=/[#?&](?:width=([0-9]+))?(?:&?height=([0-9]+))?(?:=([0-9]+)x([0-9]+))?$/,n=t.url.match(i);if(n){const e=n[1]||n[3],o=n[2]||n[4];e&&!r.width&&(r.width=parseInt(e,10)),o&&!r.height&&(r.height=parseInt(o,10)),t.url=t.url.replace(i,"")}e.width&&!r.width&&(r.width=e.width),e.height&&!r.height&&(r.height=e.height)})},stage:100,order:40},j=[{plugin:o,stage:0},{plugin:a,options:[{singleTilde:!1}],stage:0},{plugin:u,stage:0},{plugin:p,stage:0},{plugin:l,options:[["yaml","toml"]],stage:0},d,v,k,M,c],S=[{plugin:s,options:[{handlers:{mark:(t,e,r)=>"=="+r.containerPhrasing(t,{before:"==",after:"=="})+"==",sub:(t,e,r)=>"~"+r.containerPhrasing(t,{before:"~",after:"~"})+"~",sup:(t,e,r)=>"^"+r.containerPhrasing(t,{before:"^",after:"^"})+"^"}}],stage:400},{plugin:a,options:[{singleTilde:!1}],stage:400},{plugin:u,stage:400},{plugin:p,stage:400},{plugin:l,options:[["yaml","toml"]],stage:400}];S.forEach(t=>{t.plugin===s?t.order=100:t.order=10});var F={id:"markdown",title:"Markdown (GFM + Directives)",extensions:["md","markdown","mdown","mkdn"],mediaTypes:["text/markdown"],input:j,output:S};import q from"rehype-parse";import A from"rehype-remark";import D from"remark-rehype";import P,{defaultSchema as T}from"rehype-sanitize";import N from"rehype-stringify";var E={id:"html",title:"HTML",extensions:["html","htm"],mediaTypes:["text/html"],input:[{name:"rehype-parse",plugin:q,stage:0},{name:"rehype-remark",plugin:A,options:[{handlers:{mark:(t,e)=>{const r={type:"mark",children:t.all(e)};return t.patch(e,r),r},sub:(t,e)=>{const r={type:"sub",children:t.all(e)};return t.patch(e,r),r},sup:(t,e)=>{const r={type:"sup",children:t.all(e)};return t.patch(e,r),r}}}],stage:0}],output:[{plugin:D,stage:300,order:10},{plugin:P,options:[{...T,tagNames:[...T.tagNames||[],"mark","sub","sup"],attributes:{...T.attributes,"*":[...T.attributes?.["*"]||[],"className","id","style"],td:[...T.attributes?.td||[],"rowSpan","colSpan","rowspan","colspan"],th:[...T.attributes?.th||[],"rowSpan","colSpan","rowspan","colspan"],img:[...T.attributes?.img||[],"width","height"]}}],stage:300,order:20},{plugin:N,stage:400}]};function z(){this.Compiler=t=>t}function H(){this.Parser=t=>JSON.parse(t)}var O={id:"ast",title:"MDAST",input:[{plugin:H,stage:0},d,v,k,M,c],output:[{plugin:z,options:[],stage:400}]};function $(t){return"object"==typeof t&&null!==t&&"string"==typeof t.type}var I=class t{constructor(t){this.queue=[],this._data={},this.input=t}static register(t){this.registry.set(t.id,t)}static getFormat(t){return this.registry.get(t)}data(t,e){return"string"==typeof t?this._data[t]=e:Object.assign(this._data,t),this}getFormat(t){return this.constructor.getFormat(t)}resolveFormat(t){if("string"==typeof t){const e=this.getFormat(t);if(!e)throw new Error(`[MdastPlus] Format '${t}' is not registered.`);return e}return t}toRuntimeEntry(t,r,i){let n=r;void 0!==t.stage&&(n="string"==typeof t.stage?e[t.stage]??r:t.stage);let o=t.options||[];const s=t.name||t.plugin.name;if(i&&s&&s in i){const e=i[s];"object"!=typeof e||null===e||Array.isArray(e)?o=Array.isArray(e)?e:[e]:("main"in e&&(t.main=!!e.main),"before"in e&&(t.before=e.before),"after"in e&&(t.after=e.after),o=[e])}return{name:s,plugin:t.plugin,options:o,stage:n,order:t.order||0,main:t.main,before:t.before,after:t.after}}ensureInputPlugins(e,i,n=400){const o=e.some(t=>0===(t.stage??r)),s=$(this.input);if(!o){let o=[];if(s){const e=t.getFormat("ast");e&&e.input&&(o=e.input)}else{const e=t.getFormat("markdown");e&&e.input&&(o=e.input)}for(const t of o){const o=this.toRuntimeEntry(t,0,i);(o.stage??r)<=n&&e.push(o)}}}from(t,e){const r=this.resolveFormat(t);if(!r.input||0===r.input.length)throw new Error(`[MdastPlus] Format '${r.id}' does not support input.`);for(const t of r.input)this.queue.push(this.toRuntimeEntry(t,0,e));return this}async to(t,e){const r=this.resolveFormat(t);if(!r.output)throw new Error(`[MdastPlus] Format '${r.id}' does not support output.`);const i=[...this.queue];this.ensureInputPlugins(i,e);for(const t of r.output)i.push(this.toRuntimeEntry(t,300,e));const o=this.assembleProcessor(i);if($(this.input)){const t=await o.run(this.input),e=o.stringify(t),r=new n;return"string"==typeof e||Buffer.isBuffer(e)?r.value=e:r.result=e,r}return o.process(this.input)}use(t,...e){return this.useAt("compile",t,...e)}useAt(t,r,...i){if(Array.isArray(t)){for(const e of t)this.useAt(e,r,...i);return this}if(Array.isArray(r)){for(const e of r)this.useAt(t,e,...i);return this}if("object"==typeof t&&null!==t&&"plugin"in t){const n=t,o=void 0!==n.stage?"string"==typeof n.stage?n.stage:e[n.stage]:"compile",s=void 0!==r?[r,...i]:n.options;this.queue.push(this.toRuntimeEntry({...n,options:s},e[o]))}else{const n=t;if("object"==typeof r&&null!==r&&"plugin"in r){const t=r,o=i.length>0?i:t.options;this.queue.push(this.toRuntimeEntry({...t,options:o},e[n]))}else r&&this.queue.push({plugin:r,options:i,stage:e[n],order:0})}return this}priority(t){const e=this.queue[this.queue.length-1];return e&&(e.order=t),this}assembleProcessor(t){const e={};for(const i of t){const t=i.stage??r;e[t]||(e[t]=[]),e[t].push(i)}const n=[],o=Object.keys(e).map(Number).sort((t,e)=>t-e);for(const t of o){const r=e[t].sort((t,e)=>(t.order||0)-(e.order||0)),i=r.findIndex(t=>t.main);if(-1!==i){const t=r[i];r.splice(i,1),r[0]=t}let o=!0,s=0;for(;o&&s<r.length;){o=!1,s++;for(let t=0;t<r.length;t++){const e=r[t];if(e.after){const i=r.findIndex(t=>t.name===e.after);if(-1!==i&&i>t){r.splice(t,1),r.splice(i,0,e),o=!0;break}}if(e.before){const i=r.findIndex(t=>t.name===e.before);if(-1!==i&&i<t){r.splice(t,1),r.splice(i,0,e),o=!0;break}}}}n.push(...r)}const s=i();Object.keys(this._data).length>0&&s.data(this._data);for(const t of n)s.use(t.plugin,...t.options||[]);return s}};I.registry=new Map;var L=I,B=class extends L{async toMarkdown(){const t=await this.to("markdown");return String(t)}toMarkdownVFile(){return this.to("markdown")}async toHtml(){const t=await this.to("html");return String(t)}toHtmlVFile(){return this.to("html")}async toAst(t){if(t?.stage){const i=e[t.stage],n=this.queue.filter(t=>(t.stage??r)<=i);n.push({plugin:z,options:[],stage:400,order:0}),this.ensureInputPlugins(n,t.overrides,i);const o=this.assembleProcessor(n);if($(this.input)){return await o.run(this.input)}return(await o.process(this.input)).result}return(await this.to("ast",t?.overrides)).result}toHTML(){return this.toHtml()}toAST(t){return this.toAst(t)}};function V(t){return new B(t)}B.register(F),B.register(E),B.register(O);import{fromHtml as C}from"hast-util-from-html";var G=function(e){let r={};"boolean"==typeof e?r={enable:e}:e&&(r=e);const{enable:i,readability:n,jsdom:o}=r;!1!==i&&(this.parser=function(e,r){let i,s;try{i=t("jsdom").JSDOM;s=t("@mozilla/readability").Readability}catch(t){throw new Error("[html-readability] Dependency missing. Please install 'jsdom' and '@mozilla/readability'.")}const a=new s(new i(e,o).window.document,n).parse();if(!a||!a.content)return C(e,{fragment:!0});const u=C(a.content,{fragment:!0}),p={title:a.title,byline:a.byline,excerpt:a.excerpt,siteName:a.siteName,lang:a.lang};return r&&(r.data=r.data||{},r.data.readability=p),u&&(u.data=u.data||{},u.data.readability=p),u})},J={name:"readability",plugin:G,stage:0},R={name:"restore-readability-meta",plugin:()=>(t,e)=>{e.data?.readability&&(t.data=t.data||{},t.data.readability=e.data.readability)},stage:0,after:"rehype-remark"},K=[J,R];export{r as DefaultPipelineStage,L as MdastBasePipeline,B as MdastPipeline,e as PipelineStage,z as astCompiler,O as astFormat,E as htmlFormat,G as htmlReadability,J as htmlReadabilityPlugin,K as htmlReadabilityPlugins,H as jsonParser,F as markdownFormat,V as mdast,R as restoreReadabilityMetaPlugin};
package/docs/README.md CHANGED
@@ -15,7 +15,7 @@ English | [简体中文](_media/README.cn.md) | [GitHub](https://github.com/isdk
15
15
  ## Features
16
16
 
17
17
  - **Fluent API**: Chainable interface `mdast(input).use(plugin).toHTML()`.
18
- - **Staged Plugins**: Organize transformations into `normalize`, `compile`, and `finalize` stages with priority ordering.
18
+ - **Staged Plugins**: Organize transformations into `parse`, `normalize`, `compile`, `finalize`, and `stringify` stages.
19
19
  - **Semantic Normalization**:
20
20
  - **Directives**: Canonicalizes admonition names and extracts titles from labels.
21
21
  - **Table Spans**: Support for `rowspan` and `colspan` in HTML output.
@@ -44,6 +44,17 @@ const html = await mdast(':::warning[Special Note]\nBe careful!\n:::')
44
44
  // Result: <div title="Special Note" class="warning"><p>Be careful!</p></div>
45
45
  ```
46
46
 
47
+ ### Configure Input Options
48
+
49
+ You can pass options to input plugins (like `remark-gfm` or `remark-parse`) using the second argument of `.from()`:
50
+
51
+ ```typescript
52
+ // Enable single tilde strikethrough (~text~)
53
+ const md = await mdast('Hello ~world~')
54
+ .from('markdown', { remarkGfm: { singleTilde: true } })
55
+ .toMarkdown();
56
+ ```
57
+
47
58
  ### Image Sizing
48
59
 
49
60
  ```typescript
@@ -54,23 +65,46 @@ const html = await mdast('![Cat](cat.png#=500x300)').toHTML();
54
65
  ### AST Output
55
66
 
56
67
  ```typescript
68
+ // Get the fully processed AST (after normalization)
57
69
  const ast = await mdast('==Highlighted==').toAST();
58
- // Returns the mdast Root object
70
+
71
+ // Get the raw AST (after parsing, before normalization)
72
+ const rawAst = await mdast('==Highlighted==').toAST({ stage: 'parse' });
59
73
  ```
60
74
 
61
75
  ### Advanced Pipeline
62
76
 
63
77
  ```typescript
64
- const { content, assets } = await mdast(myInput)
78
+ import { htmlReadabilityPlugins } from '@isdk/mdast-plus';
79
+
80
+ const vfile = await mdast(myInput)
65
81
  .data({ myGlobal: 'value' })
66
- .use({
67
- name: 'my-plugin',
68
- stage: 'compile',
69
- transform: async (tree) => {
70
- // transform the AST
71
- }
72
- })
82
+ // Add multiple plugins as an array at the 'compile' stage
83
+ .use([pluginA, pluginB])
84
+ // Or add a set of plugins at a specific stage
85
+ .useAt('parse', htmlReadabilityPlugins)
86
+ .priority(10) // Run later than default plugins
73
87
  .to('html');
88
+
89
+ console.log(vfile.value); // The serialized HTML string
90
+ ```
91
+
92
+ ### Plugin Behavior
93
+
94
+ `mdast-plus` uses [unified](https://github.com/unifiedjs/unified) internally. If you add the same plugin function multiple times, the last configuration **overrides** the previous ones.
95
+
96
+ ```typescript
97
+ // The plugin will run ONCE with option: 2
98
+ pipeline.use(myPlugin, { option: 1 });
99
+ pipeline.use(myPlugin, { option: 2 });
100
+ ```
101
+
102
+ To run the same plugin logic multiple times (e.g., for different purposes), provide a distinct function reference:
103
+
104
+ ```typescript
105
+ // The plugin will run TWICE
106
+ pipeline.use(myPlugin, { option: 1 });
107
+ pipeline.use(myPlugin.bind({}), { option: 2 });
74
108
  ```
75
109
 
76
110
  ### Arbitrary Formats
@@ -78,16 +112,20 @@ const { content, assets } = await mdast(myInput)
78
112
  You can register custom input or output formats:
79
113
 
80
114
  ```typescript
81
- import { FluentProcessor, mdast } from '@isdk/mdast-plus';
115
+ import { MdastPipeline, mdast, PipelineStage } from '@isdk/mdast-plus';
82
116
 
83
117
  // Register a custom output format
84
- FluentProcessor.registerFormat('reverse', {
85
- stringify: (p) => {
86
- p.Compiler = (tree) => {
87
- // your custom stringification logic
88
- return '...';
89
- };
90
- }
118
+ MdastPipeline.register({
119
+ id: 'reverse',
120
+ output: [{
121
+ plugin: function() {
122
+ this.Compiler = (tree) => {
123
+ // your custom stringification logic
124
+ return '...';
125
+ };
126
+ },
127
+ stage: PipelineStage.stringify
128
+ }]
91
129
  });
92
130
 
93
131
  const result = await mdast('Hello').to('reverse');
@@ -97,11 +135,19 @@ const result = await mdast('Hello').to('reverse');
97
135
 
98
136
  ## Staged Processing
99
137
 
100
- Plugins are executed based on their `stage` and `order`:
138
+ Plugins are executed based on their `stage`, `order`, and semantic constraints (`before`/`after`):
139
+
140
+ 1. **parse** (0): Input parsing (e.g., `remark-parse`).
141
+ 2. **normalize** (100): Cleanup and canonicalize the tree.
142
+ 3. **compile** (200): High-level semantic transformations.
143
+ 4. **finalize** (300): Final preparation before output (e.g. `rehype-sanitize`).
144
+ 5. **stringify** (400): Output generation.
145
+
146
+ ### Main Plugin Replacement
147
+
148
+ Each stage can have one "main" plugin. If a plugin is marked with `main: true`, it will **replace** the first plugin in that same stage. This is useful for swapping out default parsers or compilers while keeping the rest of the pipeline intact.
101
149
 
102
- 1. **normalize** (order 0-100): Cleanup and canonicalize the tree.
103
- 2. **compile** (order 0-100): High-level semantic transformations.
104
- 3. **finalize** (order 0-100): Final preparation before output.
150
+ > **Note**: Only one main plugin is allowed per stage. If multiple plugins are marked as main, only the last one defined will take effect as the replacement.
105
151
 
106
152
  ## Core Plugins Included
107
153
 
@@ -112,6 +158,7 @@ Plugins are executed based on their `stage` and `order`:
112
158
  | `extract-code-meta` | normalize | Parses `title="foo"` from code block meta. |
113
159
  | `image-size` | normalize | Parses `#=WxH` from image URLs. |
114
160
  | `normalize-inline-styles` | normalize | Standardizes `==mark==`, `~sub~`, and `^sup^`. |
161
+ | `html-readability` | parse | Uses Mozilla's Readability to extract main content from HTML. Use `htmlReadabilityPlugins` array for easier setup. |
115
162
 
116
163
  ## Contributing
117
164
 
@@ -8,12 +8,14 @@ Thank you for your interest in contributing to `@isdk/mdast-plus`! This document
8
8
 
9
9
  ### Core Concepts
10
10
 
11
- 1. **Fluent API**: The main entry point is the `mdast()` function in `src/pipeline.ts`.
12
- 2. **Staged Plugins**: Plugins are categorized into three stages:
13
- * `normalize`: Cleanup and canonicalize the tree.
14
- * `compile`: High-level semantic transformations.
15
- * `finalize`: Final preparation before output.
16
- 3. **Universal Data Protocols**: Nodes use `node.data` for metadata, and `node.data.hProperties` for HTML attributes.
11
+ 1. **Fluent API**: The main entry point is the `mdast()` function in `src/pipeline.ts`, backed by `MdastPipeline`.
12
+ 2. **Staged Plugins**: Plugins are categorized into 5 stages (`PipelineStage`):
13
+ * `parse`: Parsing input to AST.
14
+ * `normalize`: Cleanup and canonicalize the tree.
15
+ * `compile`: High-level semantic transformations.
16
+ * `finalize`: Final preparation before output.
17
+ * `stringify`: Serializing AST to output.
18
+ 3. **Universal Data Protocols**: Nodes use `node.data` for metadata, and `node.data.hProperties` for HTML attributes.
17
19
 
18
20
  ## Getting Started
19
21
 
@@ -54,40 +56,67 @@ pnpm run lint
54
56
 
55
57
  ## Adding a New Plugin
56
58
 
57
- 1. Create your plugin in `src/plugins/`.
58
- 2. Implement the `MdastPlugin` interface:
59
+ 1. Create your plugin in `src/plugins/`.
60
+ 2. Implement the `MdastPlugin` interface. The `plugin` property should be a standard unified plugin.
59
61
 
60
62
  ```typescript
61
- import { MdastPlugin } from '../types';
63
+ import { MdastPlugin, PipelineStage } from '../types';
64
+ import { visit } from 'unist-util-visit';
65
+
66
+ const myUnifiedPlugin = (options) => {
67
+ return (tree) => {
68
+ visit(tree, 'text', (node) => {
69
+ // transform logic
70
+ });
71
+ };
72
+ };
62
73
 
63
74
  export const myPlugin: MdastPlugin = {
64
- name: 'my-plugin',
65
- stage: 'normalize', // 'normalize' | 'compile' | 'finalize'
66
- order: 50, // 0-100
67
- transform: async (tree, processor) => {
68
- // Visit nodes and transform the tree
69
- }
75
+ name: 'my-custom-plugin',
76
+ plugin: myUnifiedPlugin,
77
+ stage: PipelineStage.normalize, // or 'compile', 'finalize', etc.
78
+ order: 50, // 0-100
79
+ main: false, // Set to true to replace the first plugin of the stage, defaults to false
80
+ before: 'some-other-plugin', // Optional: run before another plugin
81
+ after: 'another-plugin', // Optional: run after another plugin
70
82
  };
71
83
  ```
72
84
 
73
- 3. Register your plugin in `src/pipeline.ts` in the `FluentProcessor` constructor if it should be a default plugin.
85
+ ### Main Plugins
86
+
87
+ Each stage supports a **single** main plugin. When a plugin is marked with `main: true`, it will replace the first plugin that was originally registered for that stage. This is primarily used by formats to allow users to override the default parser or stringifier by injecting a different one at the same stage.
88
+
89
+ 1. If it's a default plugin, add it to the `input` or `output` list of the relevant format in `src/formats/`.
74
90
 
75
91
  ## Adding a New Format
76
92
 
77
- 1. Implement the `MdastFormatDefinition` interface.
78
- 2. Register it using `FluentProcessor.registerFormat(name, definition)`.
93
+ 1. Define a `MdastFormat` object.
94
+ 2. Register it using `MdastPipeline.register(format)`.
79
95
 
80
96
  ```typescript
81
- import { FluentProcessor } from '../pipeline';
82
-
83
- FluentProcessor.registerFormat('json', {
84
- stringify: (processor) => {
85
- processor.Compiler = (tree) => JSON.stringify(tree);
86
- }
97
+ import { MdastPipeline, PipelineStage } from '../src/pipeline';
98
+
99
+ MdastPipeline.register({
100
+ id: 'json',
101
+ title: 'JSON Format',
102
+ output: [{
103
+ plugin: function() {
104
+ this.Compiler = (tree) => JSON.stringify(tree);
105
+ },
106
+ stage: PipelineStage.stringify
107
+ }]
87
108
  });
88
109
  ```
89
110
 
90
111
  > **Note**: Format names are case-insensitive.
112
+ >
113
+ > **Important**: `unified` requires a `Compiler` to be attached for the process to complete successfully. If your format is intended to return an object (like the AST itself) rather than a string, you must provide a "pass-through" compiler:
114
+ >
115
+ > ```typescript
116
+ > function astCompiler() {
117
+ > this.Compiler = (tree) => tree;
118
+ > }
119
+ > ```
91
120
 
92
121
  ## Coding Standards
93
122
 
@@ -11,7 +11,7 @@
11
11
  ## 特性
12
12
 
13
13
  - **Fluent API**: 链式调用接口 `mdast(input).use(plugin).toHTML()`。
14
- - **分阶段插件**: 将转换组织为 `normalize`、`compile` 和 `finalize` 阶段,支持优先级排序。
14
+ - **分阶段插件**: 将转换组织为 `parse`, `normalize`, `compile`, `finalize` 和 `stringify` 阶段。
15
15
  - **语义化规范**:
16
16
  - **指令 (Directives)**: 规范化提示框 (Admonition) 名称并从标签中提取标题。
17
17
  - **表格跨行/跨列**: 支持 HTML 输出中的 `rowspan` 和 `colspan`。
@@ -40,6 +40,17 @@ const html = await mdast(':::warning[重要提示]\n请小心!\n:::')
40
40
  // 结果: <div title="重要提示" class="warning"><p>请小心!</p></div>
41
41
  ```
42
42
 
43
+ ### 配置输入选项
44
+
45
+ 您可以通过 `.from()` 的第二个参数向输入插件(如 `remark-gfm` 或 `remark-parse`)传递选项:
46
+
47
+ ```typescript
48
+ // 启用单个波浪线删除线 (~text~)
49
+ const md = await mdast('Hello ~world~')
50
+ .from('markdown', { remarkGfm: { singleTilde: true } })
51
+ .toMarkdown();
52
+ ```
53
+
43
54
  ### 图片尺寸
44
55
 
45
56
  ```typescript
@@ -50,23 +61,46 @@ const html = await mdast('![Cat](cat.png#=500x300)').toHTML();
50
61
  ### AST 输出
51
62
 
52
63
  ```typescript
64
+ // 获取处理后的完整 AST (在 normalization 之后)
53
65
  const ast = await mdast('==高亮内容==').toAST();
54
- // 返回 mdast Root 对象
66
+
67
+ // 获取原始 AST (在 parse 之后, normalization 之前)
68
+ const rawAst = await mdast('==高亮内容==').toAST({ stage: 'parse' });
55
69
  ```
56
70
 
57
71
  ### 高级工作流
58
72
 
59
73
  ```typescript
60
- const { content, assets } = await mdast(myInput)
74
+ import { htmlReadabilityPlugins } from '@isdk/mdast-plus';
75
+
76
+ const vfile = await mdast(myInput)
61
77
  .data({ myGlobal: 'value' })
62
- .use({
63
- name: 'my-plugin',
64
- stage: 'compile',
65
- transform: async (tree) => {
66
- // 转换 AST
67
- }
68
- })
78
+ // 以数组形式在 'compile' 阶段添加多个插件
79
+ .use([pluginA, pluginB])
80
+ // 或在特定阶段添加一组插件
81
+ .useAt('parse', htmlReadabilityPlugins)
82
+ .priority(10) // 比默认插件更晚执行
69
83
  .to('html');
84
+
85
+ console.log(vfile.value); // 序列化后的 HTML 字符串
86
+ ```
87
+
88
+ ### 插件行为
89
+
90
+ `mdast-plus` 内部使用 [unified](https://github.com/unifiedjs/unified)。如果您多次添加同一个插件函数,最后的配置将**覆盖**之前的配置。
91
+
92
+ ```typescript
93
+ // 插件将只执行一次,且选项为: 2
94
+ pipeline.use(myPlugin, { option: 1 });
95
+ pipeline.use(myPlugin, { option: 2 });
96
+ ```
97
+
98
+ 若要多次运行相同的插件逻辑(例如用于不同目的),请提供不同的函数引用:
99
+
100
+ ```typescript
101
+ // 插件将执行两次
102
+ pipeline.use(myPlugin, { option: 1 });
103
+ pipeline.use(myPlugin.bind({}), { option: 2 });
70
104
  ```
71
105
 
72
106
  ### 任意格式支持
@@ -74,16 +108,20 @@ const { content, assets } = await mdast(myInput)
74
108
  您可以注册自定义的输入或输出格式:
75
109
 
76
110
  ```typescript
77
- import { FluentProcessor, mdast } from '@isdk/mdast-plus';
111
+ import { MdastPipeline, mdast, PipelineStage } from '@isdk/mdast-plus';
78
112
 
79
113
  // 注册自定义输出格式
80
- FluentProcessor.registerFormat('reverse', {
81
- stringify: (p) => {
82
- p.Compiler = (tree) => {
83
- // 您的自定义序列化逻辑
84
- return '...';
85
- };
86
- }
114
+ MdastPipeline.register({
115
+ id: 'reverse',
116
+ output: [{
117
+ plugin: function() {
118
+ this.Compiler = (tree) => {
119
+ // 您的自定义序列化逻辑
120
+ return '...';
121
+ };
122
+ },
123
+ stage: PipelineStage.stringify
124
+ }]
87
125
  });
88
126
 
89
127
  const result = await mdast('Hello').to('reverse');
@@ -93,11 +131,19 @@ const result = await mdast('Hello').to('reverse');
93
131
 
94
132
  ## 分阶段处理
95
133
 
96
- 插件根据它们的 `stage` (阶段) 和 `order` (顺序) 执行:
134
+ 插件根据它们的 `stage` (阶段)、`order` (顺序) 以及语义约束 (`before`/`after`) 执行:
135
+
136
+ 1. **parse** (0): 输入解析 (例如 `remark-parse`)。
137
+ 2. **normalize** (100): 清理并规范化树。
138
+ 3. **compile** (200): 高级语义转换。
139
+ 4. **finalize** (300): 输出前的最后准备 (例如 `rehype-sanitize`)。
140
+ 5. **stringify** (400): 输出生成。
141
+
142
+ ### 主插件替换 (Main Plugin Replacement)
143
+
144
+ 每个阶段可以有一个“主”插件。如果一个插件被标记为 `main: true`,它将 **替换** 该阶段中的第一个插件。这对于在保持管道其余部分不变的情况下更换默认解析器或编译器非常有用。
97
145
 
98
- 1. **normalize** (order 0-100): 清理并规范化树。
99
- 2. **compile** (order 0-100): 高级语义转换。
100
- 3. **finalize** (order 0-100): 输出前的最后准备。
146
+ > **注意**: 每个阶段只允许存在一个主插件。如果多个插件被标记为 main,则只有最后定义的那个会作为替换生效。
101
147
 
102
148
  ## 内置核心插件
103
149
 
@@ -108,6 +154,7 @@ const result = await mdast('Hello').to('reverse');
108
154
  | `extract-code-meta` | normalize | 从代码块元数据中解析 `title="foo"`。 |
109
155
  | `image-size` | normalize | 从图片 URL 中解析 `#=WxH`。 |
110
156
  | `normalize-inline-styles` | normalize | 标准化 `==mark==`、`~sub~` 和 `^sup^`。 |
157
+ | `html-readability` | parse | 使用 Mozilla 的 Readability 从 HTML 中提取主体内容。使用 `htmlReadabilityPlugins` 数组可以简化配置。 |
111
158
 
112
159
  ## 贡献
113
160