Skip to content

Make slashdown conform to unist spec#5

Open
nickisnoble wants to merge 12 commits into
mainfrom
claude/slashdown-unist-spec-01VUoJpKZN7bcjXAsB4Zqcat
Open

Make slashdown conform to unist spec#5
nickisnoble wants to merge 12 commits into
mainfrom
claude/slashdown-unist-spec-01VUoJpKZN7bcjXAsB4Zqcat

Conversation

@nickisnoble

Copy link
Copy Markdown
Owner

No description provided.

SlashDown now conforms to the Universal Syntax Tree (unist) specification,
making it compatible with the broader unified ecosystem.

Changes:
- Add unist-compliant type definitions (Node, Position, Point)
- Implement position tracking in lexer (line/column with 1-indexed values)
- Add position information to all AST nodes in parser
- Update all test files to include position tracking
- 30/31 tests passing (1 pre-existing parser issue)

All nodes now include optional `position` field with start/end points
conforming to unist spec. This enables integration with remark, rehype,
and other unist-compatible tools.

Resolves the unist conformance future goal from README.
Create EXAMPLES.md and ARCHITECTURE.md to design the integration
of SlashDown with the unified/remark/mdast ecosystem.

Key design decisions:
- Hybrid AST (slast): SlashDown nodes contain mdast children
- Replace Markdown/Text nodes with proper mdast nodes
- Configurable markdown handler for different parsers
- Support for slashdown ↔ HTML, markdown, and other formats
- Plugin-compatible architecture using unified processors

This sets the foundation for v1.0.0 with breaking AST changes
and full unified ecosystem compatibility.
BREAKING CHANGE: Complete AST structure overhaul for unified ecosystem integration

This is a major architectural change that makes SlashDown fully compatible
with the unified/remark/mdast ecosystem by implementing a hybrid AST where
SlashDown tags contain mdast nodes as children.

## Key Changes

### 1. Hybrid AST Structure
- AST is now a Root node with children array (not a direct array)
- SlashDown tags are now `slashdownTag` nodes (not `Tag`)
- Markdown content is parsed into proper mdast nodes (heading, paragraph, list, etc.)
- Inline text becomes mdast `text` nodes (not custom `Text` nodes)
- Code fences become mdast `code` nodes with lang/meta/value

### 2. New Components
- **MarkdownHandler**: Parses markdown strings into mdast nodes
  - Uses mdast-util-from-markdown with GFM support
  - Handles inline text, markdown blocks, and code fences
  - Preserves position information

### 3. Updated Type System
- Import and extend unist Node types
- `SlashDownTag`: Hybrid node containing mdast children
- `Root`: Top-level node wrapping entire document
- `MarkdownHandlerOptions`: Configuration for markdown parsing

### 4. Parser Updates
- Integrates MarkdownHandler for parsing markdown content
- Returns Root node instead of array
- Spreads mdast nodes into children arrays
- Maintains position tracking throughout

### 5. HTML Renderer Updates
- Handles both slashdownTag and mdast nodes
- Uses mdast-util-to-hast + hast-util-to-html for mdast rendering
- Fixed attribute unpacking for boolean attributes

### 6. Test Updates
- Added comprehensive hybrid-ast.test.ts with 9 new tests
- Updated all existing tests for new AST structure
- All 40 tests passing ✅

## Migration Guide

### Before (v0.x):
```javascript
{
  type: "Tag",
  tagName: "article",
  children: [
    { type: "Markdown", content: "# Hello\n\nWorld" }
  ]
}
```

### After (v1.x):
```javascript
{
  type: "root",
  children: [{
    type: "slashdownTag",
    tagName: "article",
    children: [
      { type: "heading", depth: 1, children: [{ type: "text", value: "Hello" }] },
      { type: "paragraph", children: [{ type: "text", value: "World" }] }
    ]
  }]
}
```

## Benefits
- ✅ Full unist/mdast compatibility
- ✅ Use unist-util-visit for tree traversal
- ✅ Apply remark plugins to markdown content
- ✅ GFM support (tables, strikethrough, etc.)
- ✅ Foundation for JSX/MDX support
- ✅ Interoperable with rehype for HTML transforms

## Dependencies Added
- mdast-util-from-markdown
- mdast-util-to-markdown
- mdast-util-to-hast
- mdast-util-gfm
- hast-util-to-html

Version: 1.0.0-alpha
Document the architectural decision to use type: 'slashdownTag' rather than
type: 'element' (hast) or tag name as type.

Key rationale:
- Clear distinction between SlashDown and mdast nodes in hybrid tree
- Maintains ergonomic classes/ids arrays vs hast properties
- Follows hast's pattern (generic type + tagName field)
- Easy to convert to hast when needed for unified pipeline

This decision keeps the current implementation optimal while providing
a path forward for full unified/rehype integration.
…own'

Use consistent naming throughout the codebase:
- Node type: 'slashdownTag' → 'element' (matches hast convention)
- Project name: 'SlashDown' → 'Slashdown' (title case, not camel case)
- Package/CLI: Keep as 'slashdown' (lowercase)

This makes the AST more intuitive - we're representing HTML elements,
so using type: 'element' + tagName follows the same pattern as hast.

Changed files:
- src/types.d.ts: SlashDownTag → Element
- src/parser.ts: Use type: 'element'
- src/renderers/html.ts: renderSlashDownTag → renderElement
- All tests: Update expectations to use 'element'
- All docs: Consistent 'Slashdown' naming

All 40 tests passing ✅
- Fix slashdown.ts: use Root node, reuse instances, throw errors
- Add HTML sanitization (escapeHtml) to prevent XSS
- Pre-compile regex patterns to avoid recompilation
- Improve error handling (remove console.error, add token type)
- Add test:coverage script
- Add input validation: type checking, max line length, depth limits
- Add recursive depth protection (MAX_NESTING_DEPTH = 100)
- Update dependencies to latest versions
- Export SD types, Lexer, Parser, MarkdownHandler for users
- Add comprehensive JSDoc comments to public APIs
- Expose MarkdownHandler configuration via SlashdownOptions
- Extract constants: MAX_DEPTH, MAX_LINE_LENGTH, MAX_NESTING_DEPTH
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants