@isdk/mdast-plus 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ "use strict";var e,t=Object.create,r=Object.defineProperty,i=Object.getOwnPropertyDescriptor,n=Object.getOwnPropertyNames,a=Object.getPrototypeOf,s=Object.prototype.hasOwnProperty,o=(e,t,a,o)=>{if(t&&"object"==typeof t||"function"==typeof t)for(let u of n(t))s.call(e,u)||u===a||r(e,u,{get:()=>t[u],enumerable:!(o=i(t,u))||o.enumerable});return e},u=(e,i,n)=>(n=null!=e?t(a(e)):{},o(!i&&e&&e.__esModule?n:r(n,"default",{value:e,enumerable:!0}),e)),l={};((e,t)=>{for(var i in t)r(e,i,{get:t[i],enumerable:!0})})(l,{FluentProcessor:()=>B,htmlFormat:()=>z,markdownFormat:()=>w,mdast:()=>C}),module.exports=(e=l,o(r({},"__esModule",{value:!0}),e));var c=require("unified"),m=u(require("remark-parse")),h=u(require("remark-stringify")),f=u(require("rehype-parse")),d=u(require("rehype-remark")),p=u(require("remark-gfm")),v=u(require("remark-directive")),g=u(require("remark-math")),y=u(require("remark-frontmatter"));function w(){const e=this.data();var t,r;r={handlers:{mark:(e,t,r)=>"=="+r.containerPhrasing(e,{before:"==",after:"=="})+"==",sub:(e,t,r)=>"~"+r.containerPhrasing(e,{before:"~",after:"~"})+"~",sup:(e,t,r)=>"^"+r.containerPhrasing(e,{before:"^",after:"^"})+"^"}},e[t="toMarkdownExtensions"]?e[t].push(r):e[t]=[r],this.use(p.default,{singleTilde:!1}).use(v.default).use(g.default).use(y.default,["yaml","toml"])}var b=u(require("remark-rehype")),k=u(require("rehype-sanitize")),q=u(require("rehype-stringify"));function z(){this.use(b.default).use(k.default,{...k.defaultSchema,tagNames:[...k.defaultSchema.tagNames||[],"mark","sub","sup"],attributes:{...k.defaultSchema.attributes,"*":[...k.defaultSchema.attributes?.["*"]||[],"className","id","style"],td:[...k.defaultSchema.attributes?.td||[],"rowSpan","colSpan","rowspan","colspan"],th:[...k.defaultSchema.attributes?.th||[],"rowSpan","colSpan","rowspan","colspan"],img:[...k.defaultSchema.attributes?.img||[],"width","height"]}}).use(q.default)}var x=require("unist-util-visit"),j={error:"danger",warn:"warning",success:"tip",important:"important",caution:"caution",note:"note"},O={name:"normalize-directive",stage:"normalize",order:10,transform:async e=>{(0,x.visit)(e,["containerDirective","leafDirective","textDirective"],e=>{const t=e,r=t.name.toLowerCase();if(t.name=j[r]||r,t.children&&t.children.length>0){const e=t.children[0];if(e.data?.directiveLabel||"directiveLabel"===e.type){const r=e;let i="";(0,x.visit)(r,"text",e=>{i+=e.value}),i&&!t.attributes?.title&&(t.attributes=t.attributes||{},t.attributes.title=i.trim()),t.children.shift()}}t.attributes?.title&&(t.attributes.title=String(t.attributes.title).trim()),t.data=t.data||{},t.data.hName=t.data.hName||("containerDirective"===t.type?"div":"span"),t.data.hProperties={...t.data.hProperties||{},...t.attributes,className:[t.name,t.data.hProperties?.className].filter(Boolean).join(" ")}})}},S=require("unist-util-visit"),D={name:"normalize-table-span",stage:"normalize",order:20,transform:async e=>{(0,S.visit)(e,"tableCell",e=>{if(e.data){const{rowspan:t,colspan:r}=e.data;e.data.hProperties=e.data.hProperties||{},void 0!==t&&(e.data.hProperties.rowSpan=t,delete e.data.rowspan),void 0!==r&&(e.data.hProperties.colSpan=r,delete e.data.colspan)}})}},F=require("unist-util-visit");var M={name:"extract-code-meta",stage:"normalize",order:30,transform:async e=>{(0,F.visit)(e,"code",e=>{if(e.meta){const t=function(e){const t={},r=/([a-zA-Z0-9_-]+)(?:=?(?:"([^"]*)"|'([^']*)'|([^ \t\n\r\f]+)))?/g;let i;for(;null!==(i=r.exec(e));){const e=i[1],r=i[2]||i[3]||i[4]||"";t[e]=r}return t}(e.meta),r=e.data=e.data||{};t.title&&(r.title=t.title),t.filename&&(r.filename=t.filename),r.kv={...r.kv||{},...t}}})}},N=require("unist-util-visit"),_={name:"image-size",stage:"normalize",order:40,transform:async e=>{(0,N.visit)(e,"image",e=>{const t=e.data=e.data||{},r=t.hProperties=t.hProperties||{},i=/[#?&](?:width=([0-9]+))?(?:&?height=([0-9]+))?(?:=([0-9]+)x([0-9]+))?$/,n=e.url.match(i);if(n){const t=n[1]||n[3],a=n[2]||n[4];t&&!r.width&&(r.width=parseInt(t,10)),a&&!r.height&&(r.height=parseInt(a,10)),e.url=e.url.replace(i,"")}t.width&&!r.width&&(r.width=t.width),t.height&&!r.height&&(r.height=t.height)})}},I=require("unist-util-visit");function L(e,t){return{type:e,children:t,data:{hName:e}}}var T={name:"normalize-inline-styles",stage:"normalize",transform:e=>{!function(e){(0,I.visit)(e,"text",(e,t,r)=>{if(!r||void 0===t)return;let i=e.value,n=0;const a=[];let s=!1;const o=/(==[^=]+==|~[^~]+~|\^[^^]+\^)/g;let u;for(;null!==(u=o.exec(i));){s=!0;const e=u[0],t=u.index;t>n&&a.push({type:"text",value:i.slice(n,t)});let r="mark",l="";e.startsWith("==")?(r="mark",l=e.slice(2,-2)):e.startsWith("~")?(r="sub",l=e.slice(1,-1)):e.startsWith("^")&&(r="sup",l=e.slice(1,-1)),a.push(L(r,[{type:"text",value:l}])),n=o.lastIndex}return s?(n<i.length&&a.push({type:"text",value:i.slice(n)}),r.children.splice(t,1,...a),t+a.length):void 0})}(e)}},A=class e{constructor(e){this.inputFormat="markdown",this.plugins=[],this.globalData={},this.input=e,this.processor=(0,c.unified)(),this.use(O),this.use(D),this.use(M),this.use(_),this.use(T)}static registerFormat(t,r){e.formats[t.toLowerCase()]=r}from(e){return this.inputFormat=e.toLowerCase(),this}use(e){return this.plugins.push(e),this}data(e){return this.globalData={...this.globalData,...e},this}async to(t){t=t.toLowerCase();const r=e.formats[this.inputFormat];r?.parse&&r.parse(this.processor);let i="string"==typeof this.input?this.processor.parse(this.input):this.input;i.data={...i.data,...this.globalData};const n=[...this.plugins].sort((e,t)=>{const r={normalize:0,compile:1,finalize:2},i=e.stage||"normalize",n=t.stage||"normalize";return i!==n?r[i]-r[n]:(e.order||0)-(t.order||0)});for(const e of n)await e.transform(i,this.processor);const a=(0,c.unified)().data("settings",this.processor.data("settings")),s=e.formats[t];s?.stringify&&s.stringify(a),"markdown"!==t||s||a.use(h.default).use(w);const o=await a.run(i),u=a.stringify(o),l=[];return i.data&&i.data.assets&&l.push(...i.data.assets),{content:u,assets:l}}async toMarkdown(){return(await this.to("markdown")).content}async toHTML(){return(await this.to("html")).content}};A.formats={markdown:{parse:e=>e.use(m.default).use(w),stringify:e=>e.use(h.default).use(w)},html:{parse:e=>e.use(f.default).use(d.default),stringify:e=>e.use(z)},ast:{parse:e=>{}}};var B=A;function C(e){return new B(e)}
package/dist/index.mjs ADDED
@@ -0,0 +1 @@
1
+ import{unified as t}from"unified";import r from"remark-parse";import i from"remark-stringify";import e from"rehype-parse";import s from"rehype-remark";import a from"remark-gfm";import n from"remark-directive";import o from"remark-math";import m from"remark-frontmatter";function c(){const t=this.data();var r,i;i={handlers:{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:"^"})+"^"}},t[r="toMarkdownExtensions"]?t[r].push(i):t[r]=[i],this.use(a,{singleTilde:!1}).use(n).use(o).use(m,["yaml","toml"])}import l from"remark-rehype";import f,{defaultSchema as p}from"rehype-sanitize";import h from"rehype-stringify";function u(){this.use(l).use(f,{...p,tagNames:[...p.tagNames||[],"mark","sub","sup"],attributes:{...p.attributes,"*":[...p.attributes?.["*"]||[],"className","id","style"],td:[...p.attributes?.td||[],"rowSpan","colSpan","rowspan","colspan"],th:[...p.attributes?.th||[],"rowSpan","colSpan","rowspan","colspan"],img:[...p.attributes?.img||[],"width","height"]}}).use(h)}import{visit as d}from"unist-util-visit";var v={error:"danger",warn:"warning",success:"tip",important:"important",caution:"caution",note:"note"},g={name:"normalize-directive",stage:"normalize",order:10,transform:async t=>{d(t,["containerDirective","leafDirective","textDirective"],t=>{const r=t,i=r.name.toLowerCase();if(r.name=v[i]||i,r.children&&r.children.length>0){const t=r.children[0];if(t.data?.directiveLabel||"directiveLabel"===t.type){let i="";d(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 y}from"unist-util-visit";var w={name:"normalize-table-span",stage:"normalize",order:20,transform:async t=>{y(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 k}from"unist-util-visit";var z={name:"extract-code-meta",stage:"normalize",order:30,transform:async t=>{k(t,"code",t=>{if(t.meta){const r=function(t){const r={},i=/([a-zA-Z0-9_-]+)(?:=?(?:"([^"]*)"|'([^']*)'|([^ \t\n\r\f]+)))?/g;let e;for(;null!==(e=i.exec(t));){const t=e[1],i=e[2]||e[3]||e[4]||"";r[t]=i}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 b}from"unist-util-visit";var x={name:"image-size",stage:"normalize",order:40,transform:async t=>{b(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],a=s[2]||s[4];r&&!i.width&&(i.width=parseInt(r,10)),a&&!i.height&&(i.height=parseInt(a,10)),t.url=t.url.replace(e,"")}r.width&&!i.width&&(i.width=r.width),r.height&&!i.height&&(i.height=r.height)})}};import{visit as S}from"unist-util-visit";function D(t,r){return{type:t,children:r,data:{hName:t}}}var N={name:"normalize-inline-styles",stage:"normalize",transform:t=>{!function(t){S(t,"text",(t,r,i)=>{if(!i||void 0===r)return;let e=t.value,s=0;const a=[];let n=!1;const o=/(==[^=]+==|~[^~]+~|\^[^^]+\^)/g;let m;for(;null!==(m=o.exec(e));){n=!0;const t=m[0],r=m.index;r>s&&a.push({type:"text",value:e.slice(s,r)});let i="mark",c="";t.startsWith("==")?(i="mark",c=t.slice(2,-2)):t.startsWith("~")?(i="sub",c=t.slice(1,-1)):t.startsWith("^")&&(i="sup",c=t.slice(1,-1)),a.push(D(i,[{type:"text",value:c}])),s=o.lastIndex}return n?(s<e.length&&a.push({type:"text",value:e.slice(s)}),i.children.splice(r,1,...a),r+a.length):void 0})}(t)}},M=class r{constructor(r){this.inputFormat="markdown",this.plugins=[],this.globalData={},this.input=r,this.processor=t(),this.use(g),this.use(w),this.use(z),this.use(x),this.use(N)}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(e){e=e.toLowerCase();const s=r.formats[this.inputFormat];s?.parse&&s.parse(this.processor);let a="string"==typeof this.input?this.processor.parse(this.input):this.input;a.data={...a.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(a,this.processor);const o=t().data("settings",this.processor.data("settings")),m=r.formats[e];m?.stringify&&m.stringify(o),"markdown"!==e||m||o.use(i).use(c);const l=await o.run(a),f=o.stringify(l),p=[];return a.data&&a.data.assets&&p.push(...a.data.assets),{content:f,assets:p}}async toMarkdown(){return(await this.to("markdown")).content}async toHTML(){return(await this.to("html")).content}};M.formats={markdown:{parse:t=>t.use(r).use(c),stringify:t=>t.use(i).use(c)},html:{parse:t=>t.use(e).use(s),stringify:t=>t.use(u)},ast:{parse:t=>{}}};var I=M;function L(t){return new I(t)}export{I as FluentProcessor,u as htmlFormat,c as markdownFormat,L as mdast};
package/docs/README.md ADDED
@@ -0,0 +1,113 @@
1
+ **@isdk/mdast-plus**
2
+
3
+ ***
4
+
5
+ # @isdk/mdast-plus
6
+
7
+ > A "Semantic-First" Markdown processing toolkit based on unified, remark and rehype.
8
+
9
+ English | [简体中文](_media/README.cn.md) | [GitHub](https://github.com/isdk/mdast-plus.js)
10
+
11
+ [![NPM version](https://img.shields.io/npm/v/@isdk/mdast-plus.svg)](https://www.npmjs.com/package/@isdk/mdast-plus)
12
+
13
+ `@isdk/mdast-plus` is a powerful extension to the unified ecosystem, designed to provide consistent, semantic-first normalization and transformation of Markdown content. It simplifies complex processing pipelines with a fluent API and a staged plugin system.
14
+
15
+ ## Features
16
+
17
+ - **Fluent API**: Chainable interface `mdast(input).use(plugin).toHTML()`.
18
+ - **Staged Plugins**: Organize transformations into `normalize`, `compile`, and `finalize` stages with priority ordering.
19
+ - **Semantic Normalization**:
20
+ - **Directives**: Canonicalizes admonition names and extracts titles from labels.
21
+ - **Table Spans**: Support for `rowspan` and `colspan` in HTML output.
22
+ - **Code Meta**: Structured parsing of code block metadata strings.
23
+ - **Image Sizing**: URL "sugar" support (e.g., `image.png#=500x300`) for image dimensions.
24
+ - **Deeply Typed**: Built on TypeScript with full support for unist/mdast module augmentation.
25
+
26
+ ## Installation
27
+
28
+ ```bash
29
+ npm install @isdk/mdast-plus
30
+ # or
31
+ pnpm add @isdk/mdast-plus
32
+ ```
33
+
34
+ ## Basic Usage
35
+
36
+ ### HTML Conversion
37
+
38
+ ```typescript
39
+ import { mdast } from '@isdk/mdast-plus';
40
+
41
+ const html = await mdast(':::warning[Special Note]\nBe careful!\n:::')
42
+ .toHTML();
43
+ // Result: <div title="Special Note" class="warning"><p>Be careful!</p></div>
44
+ ```
45
+
46
+ ### Image Sizing
47
+
48
+ ```typescript
49
+ const html = await mdast('![Cat](cat.png#=500x300)').toHTML();
50
+ // Result: <img src="cat.png" alt="Cat" width="500" height="300">
51
+ ```
52
+
53
+ ### Advanced Pipeline
54
+
55
+ ```typescript
56
+ const { content, assets } = await mdast(myInput)
57
+ .data({ myGlobal: 'value' })
58
+ .use({
59
+ name: 'my-plugin',
60
+ stage: 'compile',
61
+ transform: async (tree) => {
62
+ // transform the AST
63
+ }
64
+ })
65
+ .to('html');
66
+ ```
67
+
68
+ ### Arbitrary Formats
69
+
70
+ You can register custom input or output formats:
71
+
72
+ ```typescript
73
+ import { FluentProcessor, mdast } from '@isdk/mdast-plus';
74
+
75
+ // Register a custom output format
76
+ FluentProcessor.registerFormat('reverse', {
77
+ stringify: (p) => {
78
+ p.Compiler = (tree) => {
79
+ // your custom stringification logic
80
+ return '...';
81
+ };
82
+ }
83
+ });
84
+
85
+ const result = await mdast('Hello').to('reverse');
86
+ ```
87
+
88
+ > **Note**: Format names are case-insensitive (always converted to lowercase internally).
89
+
90
+ ## Staged Processing
91
+
92
+ Plugins are executed based on their `stage` and `order`:
93
+
94
+ 1. **normalize** (order 0-100): Cleanup and canonicalize the tree.
95
+ 2. **compile** (order 0-100): High-level semantic transformations.
96
+ 3. **finalize** (order 0-100): Final preparation before output.
97
+
98
+ ## Core Plugins Included
99
+
100
+ | Plugin | Stage | Description |
101
+ | :--- | :--- | :--- |
102
+ | `normalize-directive` | normalize | Handles aliases (`warn` -> `warning`) and extracts titles. |
103
+ | `normalize-table-span` | normalize | Migrates table cell spans to `hProperties`. |
104
+ | `extract-code-meta` | normalize | Parses `title="foo"` from code block meta. |
105
+ | `image-size` | normalize | Parses `#=WxH` from image URLs. |
106
+
107
+ ## Contributing
108
+
109
+ Please see [CONTRIBUTING.md](_media/CONTRIBUTING.md) for guidelines on how to contribute to this project.
110
+
111
+ ## License
112
+
113
+ [MIT](_media/LICENSE-MIT)
@@ -0,0 +1,108 @@
1
+ # Contributing to @isdk/mdast-plus
2
+
3
+ Thank you for your interest in contributing to `@isdk/mdast-plus`! This document provides guidelines for contributing to the project.
4
+
5
+ ## Architecture Overview
6
+
7
+ `@isdk/mdast-plus` is built on the `unified` ecosystem. It extends `mdast` (Markdown Abstract Syntax Tree) with semantic-first principles.
8
+
9
+ ### Core Concepts
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.
17
+
18
+ ## Getting Started
19
+
20
+ ### Prerequisites
21
+
22
+ - Node.js (Latest LTS recommended)
23
+ - [pnpm](https://pnpm.io/) (used for dependency management)
24
+
25
+ ### Installation
26
+
27
+ ```bash
28
+ git clone https://github.com/isdk/mdast-plus.js.git
29
+ cd mdast-plus.js
30
+ pnpm install
31
+ ```
32
+
33
+ ## Development Workflow
34
+
35
+ ### Building
36
+
37
+ ```bash
38
+ pnpm run build
39
+ ```
40
+
41
+ ### Testing
42
+
43
+ We use `vitest` for testing. Add your tests in the `test/` directory.
44
+
45
+ ```bash
46
+ pnpm run test # Run tests once
47
+ ```
48
+
49
+ ### Linting
50
+
51
+ ```bash
52
+ pnpm run lint
53
+ ```
54
+
55
+ ## Adding a New Plugin
56
+
57
+ 1. Create your plugin in `src/plugins/`.
58
+ 2. Implement the `MdastPlugin` interface:
59
+
60
+ ```typescript
61
+ import { MdastPlugin } from '../types';
62
+
63
+ 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
+ }
70
+ };
71
+ ```
72
+
73
+ 3. Register your plugin in `src/pipeline.ts` in the `FluentProcessor` constructor if it should be a default plugin.
74
+
75
+ ## Adding a New Format
76
+
77
+ 1. Implement the `MdastFormatDefinition` interface.
78
+ 2. Register it using `FluentProcessor.registerFormat(name, definition)`.
79
+
80
+ ```typescript
81
+ import { FluentProcessor } from '../pipeline';
82
+
83
+ FluentProcessor.registerFormat('json', {
84
+ stringify: (processor) => {
85
+ processor.Compiler = (tree) => JSON.stringify(tree);
86
+ }
87
+ });
88
+ ```
89
+
90
+ > **Note**: Format names are case-insensitive.
91
+
92
+ ## Coding Standards
93
+
94
+ - Use **TypeScript** for all new code.
95
+ - Follow the existing code style and formatting (use Prettier/ESLint).
96
+ - Ensure all new features are covered by **tests**.
97
+ - Use **semantic-first** principles: focus on the meaning of the content, not just its visual representation.
98
+
99
+ ## Submission Guidelines
100
+
101
+ - Create a feature branch for your changes.
102
+ - Ensure all tests pass.
103
+ - Update documentation (`README.md`, `README.cn.md`) if necessary.
104
+ - Submit a Pull Request with a clear description of your changes.
105
+
106
+ ## License
107
+
108
+ By contributing to this project, you agree that your contributions will be licensed under the [MIT License](./LICENSE-MIT).
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2025 Riceball LEE
2
+
3
+ Permission is hereby granted, free of charge, to any person
4
+ obtaining a copy of this software and associated documentation
5
+ files (the "Software"), to deal in the Software without
6
+ restriction, including without limitation the rights to use,
7
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the
9
+ Software is furnished to do so, subject to the following
10
+ conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,109 @@
1
+ # @isdk/mdast-plus
2
+
3
+ > 基于 unified, remark 和 rehype 的“语义优先” Markdown 处理工具包。
4
+
5
+ [English](./README.md) | 简体中文 | [GitHub](https://github.com/isdk/mdast-plus.js)
6
+
7
+ [![NPM version](https://img.shields.io/npm/v/@isdk/mdast-plus.svg)](https://www.npmjs.com/package/@isdk/mdast-plus)
8
+
9
+ `@isdk/mdast-plus` 是 unified 生态系统的强大扩展,旨在为 Markdown 内容提供一致且语义优先的规范化与转换。它通过 Fluent API 和分阶段的插件系统简化了复杂的处理工作流。
10
+
11
+ ## 特性
12
+
13
+ - **Fluent API**: 链式调用接口 `mdast(input).use(plugin).toHTML()`。
14
+ - **分阶段插件**: 将转换组织为 `normalize`、`compile` 和 `finalize` 阶段,支持优先级排序。
15
+ - **语义化规范**:
16
+ - **指令 (Directives)**: 规范化提示框 (Admonition) 名称并从标签中提取标题。
17
+ - **表格跨行/跨列**: 支持 HTML 输出中的 `rowspan` 和 `colspan`。
18
+ - **代码元数据**: 对代码块元数据字符串进行结构化解析。
19
+ - **图片尺寸**: 支持 URL 糖语法 (例如 `image.png#=500x300`) 来设置图片尺寸。
20
+ - **深度类型支持**: 基于 TypeScript 构建,完整支持 unist/mdast 的模块扩充。
21
+
22
+ ## 安装
23
+
24
+ ```bash
25
+ npm install @isdk/mdast-plus
26
+ # 或
27
+ pnpm add @isdk/mdast-plus
28
+ ```
29
+
30
+ ## 基本用法
31
+
32
+ ### HTML 转换
33
+
34
+ ```typescript
35
+ import { mdast } from '@isdk/mdast-plus';
36
+
37
+ const html = await mdast(':::warning[重要提示]\n请小心!\n:::')
38
+ .toHTML();
39
+ // 结果: <div title="重要提示" class="warning"><p>请小心!</p></div>
40
+ ```
41
+
42
+ ### 图片尺寸
43
+
44
+ ```typescript
45
+ const html = await mdast('![Cat](cat.png#=500x300)').toHTML();
46
+ // 结果: <img src="cat.png" alt="Cat" width="500" height="300">
47
+ ```
48
+
49
+ ### 高级工作流
50
+
51
+ ```typescript
52
+ const { content, assets } = await mdast(myInput)
53
+ .data({ myGlobal: 'value' })
54
+ .use({
55
+ name: 'my-plugin',
56
+ stage: 'compile',
57
+ transform: async (tree) => {
58
+ // 转换 AST
59
+ }
60
+ })
61
+ .to('html');
62
+ ```
63
+
64
+ ### 任意格式支持
65
+
66
+ 您可以注册自定义的输入或输出格式:
67
+
68
+ ```typescript
69
+ import { FluentProcessor, mdast } from '@isdk/mdast-plus';
70
+
71
+ // 注册自定义输出格式
72
+ FluentProcessor.registerFormat('reverse', {
73
+ stringify: (p) => {
74
+ p.Compiler = (tree) => {
75
+ // 您的自定义序列化逻辑
76
+ return '...';
77
+ };
78
+ }
79
+ });
80
+
81
+ const result = await mdast('Hello').to('reverse');
82
+ ```
83
+
84
+ > **注意**: 格式名称不区分大小写(内部始终转换为小写)。
85
+
86
+ ## 分阶段处理
87
+
88
+ 插件根据它们的 `stage` (阶段) 和 `order` (顺序) 执行:
89
+
90
+ 1. **normalize** (order 0-100): 清理并规范化树。
91
+ 2. **compile** (order 0-100): 高级语义转换。
92
+ 3. **finalize** (order 0-100): 输出前的最后准备。
93
+
94
+ ## 内置核心插件
95
+
96
+ | 插件 | 阶段 | 描述 |
97
+ | :--- | :--- | :--- |
98
+ | `normalize-directive` | normalize | 处理别名 (`warn` -> `warning`) 并提取标题。 |
99
+ | `normalize-table-span` | normalize | 将表格单元格跨度迁移到 `hProperties`。 |
100
+ | `extract-code-meta` | normalize | 从代码块元数据中解析 `title="foo"`。 |
101
+ | `image-size` | normalize | 从图片 URL 中解析 `#=WxH`。 |
102
+
103
+ ## 贡献
104
+
105
+ 请查看 [CONTRIBUTING.cn.md](./CONTRIBUTING.cn.md) 以获取有关如何贡献本项目的指南。
106
+
107
+ ## 许可证
108
+
109
+ [MIT](./LICENSE-MIT)
@@ -0,0 +1,194 @@
1
+ [**@isdk/mdast-plus**](../README.md)
2
+
3
+ ***
4
+
5
+ [@isdk/mdast-plus](../globals.md) / FluentProcessor
6
+
7
+ # Class: FluentProcessor
8
+
9
+ Defined in: [packages/mdast-plus/src/pipeline.ts:24](https://github.com/isdk/mdast-plus.js/blob/c94d215035e579925cf60814f0a5c05c543ca784/src/pipeline.ts#L24)
10
+
11
+ Fluent processor for mdast transformations.
12
+ Allows chaining configuration and finally converting to a target format.
13
+
14
+ ## Constructors
15
+
16
+ ### Constructor
17
+
18
+ > **new FluentProcessor**(`input`): `FluentProcessor`
19
+
20
+ Defined in: [packages/mdast-plus/src/pipeline.ts:59](https://github.com/isdk/mdast-plus.js/blob/c94d215035e579925cf60814f0a5c05c543ca784/src/pipeline.ts#L59)
21
+
22
+ Creates a new FluentProcessor instance.
23
+
24
+ #### Parameters
25
+
26
+ ##### input
27
+
28
+ `any`
29
+
30
+ The input content (string or mdast tree)
31
+
32
+ #### Returns
33
+
34
+ `FluentProcessor`
35
+
36
+ ## Properties
37
+
38
+ ### formats
39
+
40
+ > `static` **formats**: `Record`\<`string`, [`MdastFormatDefinition`](../interfaces/MdastFormatDefinition.md)\>
41
+
42
+ Defined in: [packages/mdast-plus/src/pipeline.ts:26](https://github.com/isdk/mdast-plus.js/blob/c94d215035e579925cf60814f0a5c05c543ca784/src/pipeline.ts#L26)
43
+
44
+ Map of registered format definitions
45
+
46
+ ## Methods
47
+
48
+ ### data()
49
+
50
+ > **data**(`data`): `this`
51
+
52
+ Defined in: [packages/mdast-plus/src/pipeline.ts:93](https://github.com/isdk/mdast-plus.js/blob/c94d215035e579925cf60814f0a5c05c543ca784/src/pipeline.ts#L93)
53
+
54
+ Merges global data into the processor.
55
+
56
+ #### Parameters
57
+
58
+ ##### data
59
+
60
+ `Record`\<`string`, `any`\>
61
+
62
+ Key-value pairs to store in global data
63
+
64
+ #### Returns
65
+
66
+ `this`
67
+
68
+ ***
69
+
70
+ ### from()
71
+
72
+ > **from**(`format`): `this`
73
+
74
+ Defined in: [packages/mdast-plus/src/pipeline.ts:75](https://github.com/isdk/mdast-plus.js/blob/c94d215035e579925cf60814f0a5c05c543ca784/src/pipeline.ts#L75)
75
+
76
+ Specifies the input format.
77
+
78
+ #### Parameters
79
+
80
+ ##### format
81
+
82
+ `string`
83
+
84
+ The input format name (default: 'markdown')
85
+
86
+ #### Returns
87
+
88
+ `this`
89
+
90
+ ***
91
+
92
+ ### to()
93
+
94
+ > **to**(`format`): `Promise`\<[`ConvertResult`](../interfaces/ConvertResult.md)\<`string`\>\>
95
+
96
+ Defined in: [packages/mdast-plus/src/pipeline.ts:103](https://github.com/isdk/mdast-plus.js/blob/c94d215035e579925cf60814f0a5c05c543ca784/src/pipeline.ts#L103)
97
+
98
+ Converts the input content to the specified format.
99
+
100
+ #### Parameters
101
+
102
+ ##### format
103
+
104
+ `string`
105
+
106
+ The output format name
107
+
108
+ #### Returns
109
+
110
+ `Promise`\<[`ConvertResult`](../interfaces/ConvertResult.md)\<`string`\>\>
111
+
112
+ A promise resolving to the conversion result (content and assets)
113
+
114
+ ***
115
+
116
+ ### toHTML()
117
+
118
+ > **toHTML**(): `Promise`\<`string`\>
119
+
120
+ Defined in: [packages/mdast-plus/src/pipeline.ts:173](https://github.com/isdk/mdast-plus.js/blob/c94d215035e579925cf60814f0a5c05c543ca784/src/pipeline.ts#L173)
121
+
122
+ Helper to convert content to HTML.
123
+
124
+ #### Returns
125
+
126
+ `Promise`\<`string`\>
127
+
128
+ A promise resolving to the HTML string
129
+
130
+ ***
131
+
132
+ ### toMarkdown()
133
+
134
+ > **toMarkdown**(): `Promise`\<`string`\>
135
+
136
+ Defined in: [packages/mdast-plus/src/pipeline.ts:164](https://github.com/isdk/mdast-plus.js/blob/c94d215035e579925cf60814f0a5c05c543ca784/src/pipeline.ts#L164)
137
+
138
+ Helper to convert content to Markdown.
139
+
140
+ #### Returns
141
+
142
+ `Promise`\<`string`\>
143
+
144
+ A promise resolving to the Markdown string
145
+
146
+ ***
147
+
148
+ ### use()
149
+
150
+ > **use**(`plugin`): `this`
151
+
152
+ Defined in: [packages/mdast-plus/src/pipeline.ts:84](https://github.com/isdk/mdast-plus.js/blob/c94d215035e579925cf60814f0a5c05c543ca784/src/pipeline.ts#L84)
153
+
154
+ Adds a plugin to the processing pipeline.
155
+
156
+ #### Parameters
157
+
158
+ ##### plugin
159
+
160
+ [`MdastPlugin`](../interfaces/MdastPlugin.md)
161
+
162
+ The mdast plugin to use
163
+
164
+ #### Returns
165
+
166
+ `this`
167
+
168
+ ***
169
+
170
+ ### registerFormat()
171
+
172
+ > `static` **registerFormat**(`name`, `definition`): `void`
173
+
174
+ Defined in: [packages/mdast-plus/src/pipeline.ts:45](https://github.com/isdk/mdast-plus.js/blob/c94d215035e579925cf60814f0a5c05c543ca784/src/pipeline.ts#L45)
175
+
176
+ Registers a new format definition.
177
+
178
+ #### Parameters
179
+
180
+ ##### name
181
+
182
+ `string`
183
+
184
+ The name of the format (e.g., 'docx')
185
+
186
+ ##### definition
187
+
188
+ [`MdastFormatDefinition`](../interfaces/MdastFormatDefinition.md)
189
+
190
+ The format definition containing parse/stringify logic
191
+
192
+ #### Returns
193
+
194
+ `void`
@@ -0,0 +1,24 @@
1
+ [**@isdk/mdast-plus**](../README.md)
2
+
3
+ ***
4
+
5
+ [@isdk/mdast-plus](../globals.md) / htmlFormat
6
+
7
+ # Function: htmlFormat()
8
+
9
+ > **htmlFormat**(`this`): `void`
10
+
11
+ Defined in: [packages/mdast-plus/src/formats/html.ts:9](https://github.com/isdk/mdast-plus.js/blob/c94d215035e579925cf60814f0a5c05c543ca784/src/formats/html.ts#L9)
12
+
13
+ Unified plugin/configuration for HTML format.
14
+ Includes mdast-to-hast conversion, sanitization (with table span support), and stringification.
15
+
16
+ ## Parameters
17
+
18
+ ### this
19
+
20
+ `any`
21
+
22
+ ## Returns
23
+
24
+ `void`
@@ -0,0 +1,24 @@
1
+ [**@isdk/mdast-plus**](../README.md)
2
+
3
+ ***
4
+
5
+ [@isdk/mdast-plus](../globals.md) / markdownFormat
6
+
7
+ # Function: markdownFormat()
8
+
9
+ > **markdownFormat**(`this`): `void`
10
+
11
+ Defined in: [packages/mdast-plus/src/formats/markdown.ts:10](https://github.com/isdk/mdast-plus.js/blob/c94d215035e579925cf60814f0a5c05c543ca784/src/formats/markdown.ts#L10)
12
+
13
+ Unified plugin/configuration for Markdown format.
14
+ Includes GFM, directives, math, and frontmatter.
15
+
16
+ ## Parameters
17
+
18
+ ### this
19
+
20
+ `any`
21
+
22
+ ## Returns
23
+
24
+ `void`
@@ -0,0 +1,27 @@
1
+ [**@isdk/mdast-plus**](../README.md)
2
+
3
+ ***
4
+
5
+ [@isdk/mdast-plus](../globals.md) / mdast
6
+
7
+ # Function: mdast()
8
+
9
+ > **mdast**(`input`): [`FluentProcessor`](../classes/FluentProcessor.md)
10
+
11
+ Defined in: [packages/mdast-plus/src/pipeline.ts:184](https://github.com/isdk/mdast-plus.js/blob/c94d215035e579925cf60814f0a5c05c543ca784/src/pipeline.ts#L184)
12
+
13
+ Entry point for the fluent mdast-plus API.
14
+
15
+ ## Parameters
16
+
17
+ ### input
18
+
19
+ `any`
20
+
21
+ The input content (string or mdast tree)
22
+
23
+ ## Returns
24
+
25
+ [`FluentProcessor`](../classes/FluentProcessor.md)
26
+
27
+ A FluentProcessor instance
@@ -0,0 +1,33 @@
1
+ [**@isdk/mdast-plus**](README.md)
2
+
3
+ ***
4
+
5
+ # @isdk/mdast-plus
6
+
7
+ ## Classes
8
+
9
+ - [FluentProcessor](classes/FluentProcessor.md)
10
+
11
+ ## Interfaces
12
+
13
+ - [ConvertResult](interfaces/ConvertResult.md)
14
+ - [MdastAsset](interfaces/MdastAsset.md)
15
+ - [MdastDataOrigin](interfaces/MdastDataOrigin.md)
16
+ - [MdastFormatDefinition](interfaces/MdastFormatDefinition.md)
17
+ - [MdastMark](interfaces/MdastMark.md)
18
+ - [MdastPlugin](interfaces/MdastPlugin.md)
19
+ - [MdastReader](interfaces/MdastReader.md)
20
+ - [MdastSub](interfaces/MdastSub.md)
21
+ - [MdastSup](interfaces/MdastSup.md)
22
+ - [MdastTransformer](interfaces/MdastTransformer.md)
23
+ - [MdastWriter](interfaces/MdastWriter.md)
24
+
25
+ ## Type Aliases
26
+
27
+ - [Stage](type-aliases/Stage.md)
28
+
29
+ ## Functions
30
+
31
+ - [htmlFormat](functions/htmlFormat.md)
32
+ - [markdownFormat](functions/markdownFormat.md)
33
+ - [mdast](functions/mdast.md)