@tryfabric/martian

repository·master·Indexed 20 days ago

https://github.com/tryfabric/martian

A utility library for converting Markdown and GitHub Flavored Markdown (GFM) into Notion-compatible data structures, specifically Notion Blocks and RichText objects. It provides functions like markdownToBlocks() and markdownToRichText(), as well as builder and factory functions for programmatically constructing Markdown ASTs and Notion blocks.

Tokens
3.6K
Snippets
14
Records
15
Agent score
69%

What's inside @tryfabric/martian

  1. Understand the Markdown AST node hierarchy

    master

    The Markdown AST is organized into hierarchical content types that restrict which nodes can be nested within others. Understanding these relationships is critical for writing valid AST transformations:

    • Root: The top-level node containing FlowContent.
    • FlowContent: Block-level elements such as Blockquote, Code, Heading, HTML, List, Image, ImageReference, ThematicBreak, Table, Math, or Content (which includes Definition and Paragraph).
    • PhrasingContent: Inline elements that can appear within text, such as Link, LinkReference, or StaticPhrasingContent.
    • StaticPhrasingContent: A subset of phrasing content that does not allow further nesting, including Image, Break, Emphasis, HTML, ImageReference, InlineCode, Strong, Text, Delete, and InlineMath.
    • Table Structure: A Table contains TableContent (TableRow), which contains RowContent (TableCell), which contains PhrasingContent.
  2. Create a Markdown AST using builder functions

    master

    The src/markdown/ast.ts module provides a set of builder functions to programmatically construct a Markdown Abstract Syntax Tree (AST) compatible with Notion-like structures. These functions return typed objects representing different Markdown elements such as text, headings, lists, and tables.

    Common Builder Functions

    Text and Inline Elements

    • text(value: string): Creates a plain text node.
    • emphasis(...children: PhrasingContent[]): Creates italicized text.
    • strong(...children: PhrasingContent[]): Creates bold text.
    • inlineCode(value: string): Creates an inline code span.
    • link(url: string, ...children: StaticPhrasingContent[]): Creates a hyperlink.
    • strikethrough(...children: PhrasingContent[]): Creates a deletion/strikethrough node.

    Block Elements

    • root(...children: FlowContent[]): The top-level container for the AST.
    • paragraph(...children: PhrasingContent[]): Creates a paragraph.
    • heading(depth: 1 | 2 | 3 | 4 | 5 | 6, ...children: PhrasingContent[]): Creates a heading of a specific level.
    • blockquote(...children: FlowContent[]): Creates a blockquote.
    • thematicBreak(): Creates a horizontal rule.
    • code(value: string, lang?: string): Creates a fenced code block.

    Lists

    • unorderedList(...children: ListContent[]): Creates a bulleted list.
    • orderedList(...children: ListContent[]): Creates a numbered list.
    • listItem(...children: FlowContent[]): Creates a standard list item.
    • checkedListItem(checked: boolean, ...children: FlowContent[]): Creates a list item with a checkbox state.

    Tables

    • table(...children: TableContent[]): Creates a table container.
    • tableRow(...children: RowContent[]): Creates a row within a table.
    • tableCell(...children: PhrasingContent[]): Creates a cell within a row.
    import {
      root,
      heading,
      paragraph,
      text,
      strong,
      link
    } from './markdown/ast';
    
    const ast = root(
      heading(1, text('Hello World')),
      paragraph(
        text('This is a '),
        strong(text('bold')),
        text(' statement with a '),
        link('https://example.com', text('link'))
      )
    );
  3. Check supported code block languages

    master

    The library provides a list of languages supported for Notion code blocks. Use isSupportedCodeLang(lang: string) to validate if a specific language string is supported by the Notion API via this library.

    Supported languages include typescript, javascript, python, rust, go, sql, markdown, and many others.

    import { isSupportedCodeLang } from './notion/common';
    
    console.log(isSupportedCodeLang('typescript')); // true
    console.log(isSupportedCodeLang('unknown-lang')); // false
  4. Parse callout emojis with parseCalloutEmoji()

    master

    The parseCalloutEmoji function inspects a string to determine if it starts with an emoji. If an emoji is detected at the beginning of the first line, it returns an object containing the emoji itself (as an EmojiRequest) and a corresponding Notion ApiColor determined by the SUPPORTED_EMOJI_COLOR_MAP. If no emoji is found at the start of the text, it returns null.

    import { parseCalloutEmoji } from '@tryfabric/martian/notion';
    
    const result = parseCalloutEmoji('🚀 This is a callout');
    if (result) {
      console.log(result.emoji); // '🚀'
      console.log(result.color); // The mapped ApiColor
    }
  5. Create Notion blocks using factory functions

    master

    The src/notion/blocks.ts module provides factory functions to easily construct Notion Block objects. These functions abstract the complex JSON structure required by the Notion API. Most functions accept RichText[] as an argument to define the content of the block.

    Common Block Types

    • Text Blocks: paragraph, headingOne, headingTwo, headingThree, code, blockquote, equation.
    • List Blocks: bulletedListItem, numberedListItem, toDo.
    • Layout & Media: divider, image, table_of_contents, callout, table.

    Usage Note on RichText

    For most text-based blocks, you must pass an array of RichText objects. You can obtain these using the richText helper imported from ./common.

    import { paragraph, headingOne, code, richText } from './notion/blocks';
    
    const blocks = [
      headingOne([richText('My Title')]),
      paragraph([richText('This is a paragraph with ', richText('bold text', { bold: true }))]),
      code([richText('console.log("hello");')], 'javascript'),
    ];
  6. Convert Markdown to Notion Blocks with markdownToBlocks()

    master

    Use markdownToBlocks() to transform a full Markdown or GitHub Flavored Markdown (GFM) string into an array of Notion Block objects. This function supports GFM and mathematical notation via remark-math. It uses unified with remark-parse, remark-gfm, and remark-math under the hood to process the input.

    import { markdownToBlocks } from '@tryfabric/martian';
    
    const markdown = '# Hello\nThis is a block.';
    const blocks = markdownToBlocks(markdown);
    // returns notion.Block[]
  7. Create a table with rows and cells

    master

    To create a table in Notion, you must use a combination of table, tableRow, and RichText arrays for cells.

    1. Use tableRow(cells) where cells is an array of RichText[] (one array per cell in the row).
    2. Use table(children, tableWidth) where children is an array of the rows created above.
    import { table, tableRow, richText } from './notion/blocks';
    
    const row1 = tableRow([
      [richText('Cell 1')],
      [richText('Cell 2')]
    ]);
    
    const row2 = tableRow([
      [richText('Cell 3')],
      [richText('Cell 4')]
    ]);
    
    const myTable = table([row1, row2], 100);
  8. Convert inline Markdown to Notion RichText with markdownToRichText()

    master

    Use markdownToRichText() to transform inline Markdown or GFM content into an array of Notion RichText objects. This is intended for parsing text segments that reside within a block.

    Supported formatting:

    • Plain text
    • Italics
    • Bold
    • Strikethrough
    • Inline code
    • Hyperlinks
    import { markdownToRichText } from '@tryfabric/martian';
    
    const inlineMd = '**bold text** and *italics*';
    const richText = markdownToRichText(inlineMd);
    // returns notion.RichText[]
  9. Create RichText objects with richText()

    master

    Use the richText function to construct Notion-compatible RichText objects. You can specify the content, the type ('text' or 'equation'), and various annotations like bold, italic, color, etc. If a valid URL is provided in the options.url field, it will be automatically applied as a link to the text.

    Supported type values:

    • 'text' (default)
    • 'equation'

    Supported annotations:

    • bold?: boolean
    • italic?: boolean
    • strikethrough?: boolean
    • underline?: boolean
    • code?: boolean
    • color?: string
    import { richText } from './notion/common';
    
    // Create basic text
    const text = richText('Hello World');
    
    // Create bold, blue text with a link
    const styledLink = richText('Click me', {
      annotations: { bold: true, color: 'blue' },
      url: 'https://example.com'
    });
    
    // Create an equation
    const equation = richText('e=mc^2', { type: 'equation' });
  10. Parse code language with parseCodeLanguage()

    master

    The parseCodeLanguage function takes a string representing a programming language and attempts to map it to a supportedCodeLang type. It performs a case-insensitive lookup against an internal language map. If the language is not recognized or no string is provided, it returns undefined.

    import { parseCodeLanguage } from '@tryfabric/martian/notion';
    
    const lang = parseCodeLanguage('typescript');
    // returns the corresponding supportedCodeLang type
  11. Use GFM Alert types and mappings

    master

    The library supports GitHub Flavored Markdown (GFM) alert types, which can be mapped to specific Notion emojis and background colors.

    Supported GfmAlertType values:

    • NOTE (Emoji: 📘, Color: blue_background)
    • TIP (Emoji: 💡, Color: green_background)
    • IMPORTANT (Emoji: ☝️, Color: purple_background)
    • WARNING (Emoji: ⚠️, Color: yellow_background)
    • CAUTION (Emoji: ❗, Color: red_background)

    Use isGfmAlertType(type: string) to validate a type and GFM_ALERT_MAP to retrieve the corresponding emoji and color configuration.

    import { GFM_ALERT_MAP, isGfmAlertType } from './notion/common';
    
    const type = 'WARNING';
    if (isGfmAlertType(type)) {
      const config = GFM_ALERT_MAP[type];
      console.log(config.emoji); // '⚠️'
      console.log(config.color); // 'yellow_background'
    }
  12. Notion API property value limits

    master

    When constructing payloads for the Notion API, ensure you stay within these defined limits to avoid errors:

    Limit KeyValue
    PAYLOAD_BLOCKS1000
    RICH_TEXT_ARRAYS100
    RICH_TEXT.TEXT_CONTENT2000
    RICH_TEXT.LINK_URL1000
    RICH_TEXT.EQUATION_EXPRESSION1000
    import { LIMITS } from './notion/common';
    
    console.log(LIMITS.PAYLOAD_BLOCKS); // 1000