0.1.3 • Published 8 days ago

notion2hast v0.1.3

Weekly downloads
-
License
MIT
Repository
github
Last release
8 days ago

notion2hast

Notion の blockshast へ変換。

実装中。

Installtion

$ npm install --save notion2hast

Security

notion2hast は信頼できるソースのみを扱う前提で動作します。

第三者からのソースを扱う状況では hast-util-sanitize の併用を推奨します。

Usage

basic

import { Client as NotionClient } from '@notionhq/client'
import { ClientOptions } from '@notionhq/client/build/src/Client'
import { toHtml as hastToHtml } from 'hast-util-to-html'
import { blockToHast } from 'notion2hast'
import { Client } from 'notion2hast'

class FromNotion extends Client {
  private client: NotionClient
  constructor(options?: ClientOptions) {
    super()
    this.client = new NotionClient(options)
  }
  listBlockChildren(
    ...args: Parameters<NotionClient['blocks']['children']['list']>
  ): ReturnType<NotionClient['blocks']['children']['list']> {
    return this.client.blocks.children.list(...args)
  }
}

const apiKey = process.env.API_KEY
const blockId = process.env.BLOCK_ID

const client = new FromNotion({
  auth: apiKey
})
const tree = await blockToHast(client, {
  block_id: blockId,
  blocktoHastOpts: { defaultClassName: true },
  richTexttoHastOpts: { defaultClassName: true }
})
console.log(`${hastToHtml(tree)}`)
<p>最初のパラグラフ。</p>
<h2>文字装飾</h2>
<p><strong>太字</strong></p>
<p><em>italic</em></p>
<p><s>打消し線</s></p>
<p><code>code</code></p>
<h2>リスト</h2>
<ul>
  <li>項目1</li>
  <li>
    項目2
    <ul>
      <li>
        項目2-1
        <ul>
          <li>項目<span style="color: #0b6e99">2-1-1</span></li>
        </ul>
      </li>
      <li>項目2-2</li>
    </ul>
  </li>
</ul>

Custom Client

Notions の Client(主に fetch 関連)が利用できない環境では Custom Client を利用可能。

以下、Google Apps Script(clasp) で利用する場合の例。

tohast.ts

import { Client as NotionClient } from '@notionhq/client'
import { ClientOptions } from '@notionhq/client/build/src/Client'
import { toHtml as hastToHtml } from 'hast-util-to-html'
import { blockToHast } from '@hankei6km/notion2hast'
import { Client } from '@hankei6km/notion2hast'
import { listBlockChildren } from './notion'

export namespace NotionToHast {
  class FromNotion extends Client {
    //private client: NotionClient;
    private auth: ClientOptions['auth']
    constructor(options?: ClientOptions) {
      super()
      this.auth = options?.auth
    }
    async listBlockChildren(
      ...args: Parameters<NotionClient['blocks']['children']['list']>
    ): Promise<ReturnType<NotionClient['blocks']['children']['list']>> {
      return listBlockChildren(this.auth || '', args[0].block_id) as any
    }
  }

  export async function toHast(apiKey: string, block_id: string) {
    const client = new FromNotion({ auth: apiKey })
    const tree = await blockToHast(client, {
      block_id,
      blocktoHastOpts: { defaultClassName: true },
      richTexttoHastOpts: { defaultClassName: true }
    })
    console.log(`${hastToHtml(tree)}`)
  }
}

notion.ts

import {
  CreatePageParameters,
  UpdatePageParameters,
  QueryDatabaseParameters,
  QueryDatabaseResponse
} from '@notionhq/client/build/src/api-endpoints'

const apiIUrlCreatePage = 'https://api.notion.com/v1/pages'
const apiUrlDabtabaseQuery = (database_id: string) =>
  `https://api.notion.com/v1/databases/${database_id}/query`
const apiUrlUpdatePage = (page_id: string) =>
  `https://api.notion.com/v1/pages/${page_id}`
const apiUrListBlockChildren = (block_id: string) =>
  `https://api.notion.com/v1/blocks/${block_id}/children`
const apiVersion = '2022-02-22'

export function isErrRes(
  res: GoogleAppsScript.URL_Fetch.HTTPResponse
): boolean {
  const code = Math.trunc(res.getResponseCode() / 100)
  if (code === 4 || code === 5) {
    return true
  }
  return false
}

export function listBlockChildren(apiKey: string, block_id: string) {
  const url = apiUrListBlockChildren(block_id)
  try {
    const res = UrlFetchApp.fetch(url, {
      headers: {
        Authorization: `Bearer ${apiKey}`,
        'Content-Type': 'application/json',
        'Notion-Version': apiVersion
      },
      muteHttpExceptions: true
    })
    const code = res.getResponseCode()
    if (isErrRes(res)) {
      throw res.getContentText()
    }
    const resQuery = JSON.parse(res.getContentText()) as QueryDatabaseResponse
    return resQuery
  } catch (e) {
    console.log(`${e}`)
    throw e
  }
}

default class name

各要素にデフォルトの class 名を追加。デフォルトのクラス名は properties map の key が使用される。

const client = new FromNotion({
  auth: apiKey,
  blocktoHastOpts: { defaultClassName: true },
  richTexttoHastOpts: { defaultClassName: true }
})
<p class="paragraph">最初のパラグラフ。</p>
<h2 class="heading-2">文字装飾</h2>
<p class="paragraph"><strong class="text-bold">太字</strong></p>
<p class="paragraph"><em class="text-italic">italic</em></p>
<p class="paragraph"><s class="text-strikethrough">打消し線</s></p>
<p class="paragraph"><code class="text-code">code</code></p>

properties map

指定要素のプロパティを変更。className を指定した場合、指定された要素にデフォルト class 名は追加されない。

const client = new FromNotion({
  auth: apiKey,
  blocktoHastOpts: {
    defaultClassName: true,
    blockToHastBuilderOpts: {
      propertiesMap: {
        'heading-2': { style: 'border: solid;' },
        callout: { className: 'flex' },
        'callout-paragraph': { className: 'grow' }
      }
    }
  },
  richTexttoHastOpts: { defaultClassName: true }
})
<h2 style="border: solid" class="heading-2">コールアウト</h2>
<div class="flex" style="background-color: #ebeced">
  <div class="callout-icon-emoji">💡</div>
  <div class="grow">
    <p>コールアウトの<strong class="text-bold">テキスト</strong>。</p>
  </div>
</div>

対応状況

Block

  • paragraph
  • heading_1
  • heading_2
  • heading_3
  • bulleted_list_item
  • numbered_list_item
  • quote
  • to_do - チェックは properties map で表現
  • toggle
  • template
  • synced_block
  • child_page
  • child_database
  • equation
  • code
  • callout
  • divider
  • breadcrumb
  • table_of_contents
  • column_list
  • column
  • link_to_page
  • table
  • table_row
  • embed
  • bookmark - a タグとして変換
  • image
  • video
  • pdf
  • file
  • audio
  • link_preview
  • unsupported

rich text

  • text
    • annotations
      • bold
      • italic
      • strikethrough
      • underline
      • code
      • color
    • href
  • mention
    • user
    • date
    • link_preview
    • template_mention
      • template_mention_date
      • template_mention_user
    • page
    • database
    • annotations
      • bold
      • italic
      • strikethrough
      • underline
      • code
      • color
    • href
  • equation
    • annotations
      • bold
      • italic
      • strikethrough
      • underline
      • code
      • color
    • href

properties mapping

block

  • paragraph
  • heading-1
  • heading-2
  • heading-3
  • code
  • code-pre
  • code-code
  • code-caption
  • callout
  • callout-icon-emoji
  • callout-icon-image
  • callout-paragraph
  • divider
  • column-list
  • column
  • bulleted-list
  • bulleted-list-item
  • numbered-list
  • numbered-list-item
  • quote
  • todo
  • todo-checked
  • todo-not-checked
  • todo-text
  • toggle
  • toggle-summary
  • table
  • table-row
  • table-row-cell
  • table-row-header
  • table-row-header-top-left
  • table-row-header-top
  • table-row-header-left
  • bookmark
  • bookmark-link
  • bookmark-caption
  • image
  • image-img
  • image-caption

rich text

  • text-link
  • text-bold
  • text-code
  • text-italic
  • text-strikethrough
  • text-underline

color

  • gray
  • brown
  • orange
  • yellow
  • green
  • blue
  • purple
  • pink
  • red
  • gray_background
  • brown_background
  • orange_background
  • yellow_background
  • green_background
  • blue_background
  • purple_background
  • pink_background
  • red_background

ライセンス

MIT License

Copyright (c) 2022 hankei6km