├── .gitignore ├── packages ├── html2sb │ ├── .gitignore │ ├── src │ │ ├── index.ts │ │ ├── command │ │ │ ├── index.ts │ │ │ └── command.ts │ │ └── main.ts │ ├── biome.json │ ├── tsconfig.json │ ├── test │ │ ├── evernote-html.test.ts │ │ └── fixtures │ │ │ ├── scrapbox │ │ │ └── evernote │ │ │ │ └── test.txt │ │ │ └── html │ │ │ └── evernote │ │ │ └── test.html │ ├── README.md │ ├── LICENSE │ ├── package.json │ └── CHANGELOG.md ├── md2sb │ ├── .gitignore │ ├── test │ │ ├── fixtures │ │ │ ├── md │ │ │ │ ├── strike.md │ │ │ │ ├── strong-italic.md │ │ │ │ ├── italic.md │ │ │ │ ├── strong.md │ │ │ │ ├── code.md │ │ │ │ ├── image.md │ │ │ │ ├── link-includes-image.md │ │ │ │ ├── blockquote.md │ │ │ │ ├── link.md │ │ │ │ ├── hr.md │ │ │ │ ├── heading.md │ │ │ │ ├── paragraph.md │ │ │ │ ├── list-strong-style-text.md │ │ │ │ ├── list.md │ │ │ │ ├── codeblock.md │ │ │ │ ├── table.md │ │ │ │ └── sample-ja.md │ │ │ └── scrapbox │ │ │ │ ├── strike.txt │ │ │ │ ├── strong-italic.txt │ │ │ │ ├── image.txt │ │ │ │ ├── italic.txt │ │ │ │ ├── strong.txt │ │ │ │ ├── code.txt │ │ │ │ ├── link-includes-image.txt │ │ │ │ ├── link.txt │ │ │ │ ├── blockquote.txt │ │ │ │ ├── hr.txt │ │ │ │ ├── heading.txt │ │ │ │ ├── paragraph.txt │ │ │ │ ├── table.txt │ │ │ │ ├── list-strong-style-text.txt │ │ │ │ ├── list.txt │ │ │ │ └── codeblock.txt │ │ ├── complexText.test.ts │ │ ├── simpleReteral.test.ts │ │ ├── textDecoration.test.ts │ │ └── helpers │ │ │ └── loadAndAssert.ts │ ├── biome.json │ ├── src │ │ ├── index.ts │ │ ├── command │ │ │ ├── index.ts │ │ │ └── command.ts │ │ ├── libs │ │ │ ├── addListItemCount.ts │ │ │ ├── generateCodeBlock.ts │ │ │ └── compiler.ts │ │ └── main.ts │ ├── tsconfig.json │ ├── README.md │ ├── package.json │ └── CHANGELOG.md ├── html2sb-compiler │ ├── .npmignore │ ├── test │ │ ├── fixtures │ │ │ ├── entities.txt │ │ │ ├── entities.html │ │ │ ├── evernote-multipage-1.txt │ │ │ ├── evernote-multipage-2.txt │ │ │ ├── evernote-multipage-3.txt │ │ │ ├── invalid-list-in-list.txt │ │ │ ├── hr.html │ │ │ ├── invalid-list-in-list.html │ │ │ ├── simple-paragraph.txt │ │ │ ├── simple-paragraph.html │ │ │ ├── invalid-text-in-table.txt │ │ │ ├── table-in-div.txt │ │ │ ├── complex-paragraph.txt │ │ │ ├── evernote.txt │ │ │ ├── links-empty.txt │ │ │ ├── hr.txt │ │ │ ├── list-of-links.txt │ │ │ ├── images.txt │ │ │ ├── invalid-text-in-table.html │ │ │ ├── images.html │ │ │ ├── complex-paragraph.html │ │ │ ├── table.txt │ │ │ ├── styled-code.txt │ │ │ ├── list-of-links.html │ │ │ ├── formatting.txt │ │ │ ├── list-skipped-inheritance.txt │ │ │ ├── text-styles.txt │ │ │ ├── links-empty.html │ │ │ ├── links.txt │ │ │ ├── entities.json │ │ │ ├── list.txt │ │ │ ├── blocks.txt │ │ │ ├── evernote.json │ │ │ ├── list-in-list.txt │ │ │ ├── text-styles.html │ │ │ ├── evernote-multipage-1.json │ │ │ ├── evernote-multipage-2.json │ │ │ ├── evernote-multipage-3.json │ │ │ ├── links.html │ │ │ ├── table-in-div.html │ │ │ ├── header.txt │ │ │ ├── hr.json │ │ │ ├── images.json │ │ │ ├── list-wrong-inheritance.txt │ │ │ ├── list-of-links.json │ │ │ ├── code.txt │ │ │ ├── invalid-list-in-list.json │ │ │ ├── blocks.html │ │ │ ├── list.html │ │ │ ├── evernote.html │ │ │ ├── header.html │ │ │ ├── styled-code.html │ │ │ ├── list-in-list.html │ │ │ ├── simple-paragraph.json │ │ │ ├── invalid-text-in-table.json │ │ │ ├── list-skipped-inheritance.html │ │ │ ├── table.html │ │ │ ├── formatting.html │ │ │ ├── links-empty.json │ │ │ ├── styled-code.json │ │ │ ├── links.json │ │ │ ├── list-wrong-inheritance.html │ │ │ ├── complex-paragraph.json │ │ │ ├── code.json │ │ │ ├── table-in-div.json │ │ │ ├── formatting.json │ │ │ ├── code.html │ │ │ ├── text-styles.json │ │ │ ├── evernote-multipage.html │ │ │ ├── blocks.json │ │ │ ├── list.json │ │ │ ├── complex.txt │ │ │ ├── header.json │ │ │ ├── list-in-list.json │ │ │ ├── list-skipped-inheritance.json │ │ │ ├── table.json │ │ │ ├── list-wrong-inheritance.json │ │ │ └── complex.html │ │ └── fixtures.test.ts │ ├── .gitignore │ ├── .eslintrc.js │ ├── tsconfig.build.json │ ├── src │ │ ├── index.ts │ │ ├── guessTitle.ts │ │ ├── toScrapbox.ts │ │ └── parse.ts │ ├── tsconfig.json │ ├── biome.json │ ├── package.json │ └── CHANGELOG.md ├── scrapbox-converter │ ├── .gitignore │ ├── biome.json │ ├── src │ │ ├── index.ts │ │ ├── libs │ │ │ ├── loader │ │ │ │ ├── md.ts │ │ │ │ ├── enex.ts │ │ │ │ └── html.ts │ │ │ └── findAndLoadFiles.ts │ │ └── command.ts │ ├── tsconfig.json │ ├── README.md │ ├── package.json │ ├── LICENSE │ └── CHANGELOG.md └── enex2sb │ ├── .gitignore │ ├── biome.json │ ├── src │ ├── index.ts │ ├── command │ │ ├── index.ts │ │ └── command.ts │ ├── node.ts │ ├── browser.ts │ ├── libs │ │ └── uploadImage.ts │ ├── browser │ │ └── uploadImage.ts │ └── main.ts │ ├── tsconfig.json │ ├── test │ ├── fixtures │ │ ├── example.sb.txt │ │ └── multiple.enex │ ├── multiple.test.ts │ └── example.test.ts │ ├── README.md │ ├── LICENSE │ ├── package.json │ └── CHANGELOG.md ├── pnpm-workspace.yaml ├── .changeset └── config.json ├── tsconfig.base.json ├── README.md ├── package.json ├── .github └── workflows │ ├── nodejs.yml │ └── npmpublish.yml ├── biome.json └── CHANGELOG.md /.gitignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | node_modules/ 3 | -------------------------------------------------------------------------------- /packages/html2sb/.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | -------------------------------------------------------------------------------- /packages/md2sb/.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/.npmignore: -------------------------------------------------------------------------------- 1 | test 2 | -------------------------------------------------------------------------------- /packages/scrapbox-converter/.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | -------------------------------------------------------------------------------- /packages/enex2sb/.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | yarn.lock 3 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - 'packages/**' 3 | -------------------------------------------------------------------------------- /packages/md2sb/test/fixtures/md/strike.md: -------------------------------------------------------------------------------- 1 | ~~strike text~~ 2 | -------------------------------------------------------------------------------- /packages/md2sb/test/fixtures/scrapbox/strike.txt: -------------------------------------------------------------------------------- 1 | [- strike text] 2 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/entities.txt: -------------------------------------------------------------------------------- 1 | Untitled 2 | 寿司 3 | -------------------------------------------------------------------------------- /packages/html2sb/src/index.ts: -------------------------------------------------------------------------------- 1 | module.exports = require("./main"); 2 | -------------------------------------------------------------------------------- /packages/md2sb/test/fixtures/md/strong-italic.md: -------------------------------------------------------------------------------- 1 | ***strong italic*** 2 | -------------------------------------------------------------------------------- /packages/enex2sb/biome.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../biome.json"] 3 | } 4 | -------------------------------------------------------------------------------- /packages/html2sb/biome.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../biome.json"] 3 | } 4 | -------------------------------------------------------------------------------- /packages/md2sb/biome.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../biome.json"] 3 | } 4 | -------------------------------------------------------------------------------- /packages/md2sb/test/fixtures/md/italic.md: -------------------------------------------------------------------------------- 1 | _italic_ 2 | 3 | *alt italic* 4 | -------------------------------------------------------------------------------- /packages/md2sb/test/fixtures/scrapbox/strong-italic.txt: -------------------------------------------------------------------------------- 1 | [/* strong italic] 2 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/entities.html: -------------------------------------------------------------------------------- 1 |

寿司

2 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/evernote-multipage-1.txt: -------------------------------------------------------------------------------- 1 | ノート1 2 | 本文1 3 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/evernote-multipage-2.txt: -------------------------------------------------------------------------------- 1 | ノート2 2 | 本文2 3 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/evernote-multipage-3.txt: -------------------------------------------------------------------------------- 1 | ノート3 2 | 本文3 3 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/invalid-list-in-list.txt: -------------------------------------------------------------------------------- 1 | List (?) 2 | 3 | -------------------------------------------------------------------------------- /packages/md2sb/src/index.ts: -------------------------------------------------------------------------------- 1 | import main from "./main"; 2 | export default main; 3 | -------------------------------------------------------------------------------- /packages/md2sb/test/fixtures/md/strong.md: -------------------------------------------------------------------------------- 1 | __string__ 2 | 3 | **alt string** 4 | -------------------------------------------------------------------------------- /packages/enex2sb/src/index.ts: -------------------------------------------------------------------------------- 1 | import main from "./node"; 2 | export default main; 3 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/.gitignore: -------------------------------------------------------------------------------- 1 | yarn.lock 2 | dist/ 3 | tsconfig.tsbuildinfo 4 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/hr.html: -------------------------------------------------------------------------------- 1 |
2 | hello 3 |
4 | Holla 5 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/invalid-list-in-list.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /packages/md2sb/test/fixtures/md/code.md: -------------------------------------------------------------------------------- 1 | `$ this is code` 2 | 3 | ``$ this is code`` 4 | -------------------------------------------------------------------------------- /packages/md2sb/test/fixtures/md/image.md: -------------------------------------------------------------------------------- 1 | ![alt text](http://example.com/example.png) 2 | -------------------------------------------------------------------------------- /packages/md2sb/test/fixtures/scrapbox/image.txt: -------------------------------------------------------------------------------- 1 | [http://example.com/example.png] 2 | -------------------------------------------------------------------------------- /packages/md2sb/test/fixtures/scrapbox/italic.txt: -------------------------------------------------------------------------------- 1 | [/ italic] 2 | 3 | [/ alt italic] 4 | -------------------------------------------------------------------------------- /packages/md2sb/test/fixtures/scrapbox/strong.txt: -------------------------------------------------------------------------------- 1 | [* string] 2 | 3 | [* alt string] 4 | -------------------------------------------------------------------------------- /packages/scrapbox-converter/biome.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../biome.json"] 3 | } 4 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/simple-paragraph.txt: -------------------------------------------------------------------------------- 1 | Untitled 2 | Hello 3 | World 4 | -------------------------------------------------------------------------------- /packages/md2sb/test/fixtures/scrapbox/code.txt: -------------------------------------------------------------------------------- 1 | `$ this is code` 2 | 3 | `$ this is code` 4 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/simple-paragraph.html: -------------------------------------------------------------------------------- 1 |

Hello

World

2 | -------------------------------------------------------------------------------- /packages/md2sb/src/command/index.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | module.exports = require("./command"); 3 | -------------------------------------------------------------------------------- /packages/enex2sb/src/command/index.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | module.exports = require("./command"); 3 | -------------------------------------------------------------------------------- /packages/html2sb/src/command/index.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | module.exports = require("./command"); 3 | -------------------------------------------------------------------------------- /packages/scrapbox-converter/src/index.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | module.exports = require("./command"); 4 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/invalid-text-in-table.txt: -------------------------------------------------------------------------------- 1 | Table (Untitled) 2 | table: 3 | 4 | cell 5 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/table-in-div.txt: -------------------------------------------------------------------------------- 1 | Table (Untitled) 2 | table: 3 | whos [/ house] [_ runs] 4 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/complex-paragraph.txt: -------------------------------------------------------------------------------- 1 | Untitled 2 | Hello 3 | World 4 | 5 | paragraph 1 6 | writing 7 | -------------------------------------------------------------------------------- /packages/md2sb/test/fixtures/md/link-includes-image.md: -------------------------------------------------------------------------------- 1 | [![test](http://example.com/example.png)](http://example.com/example) 2 | -------------------------------------------------------------------------------- /packages/md2sb/test/fixtures/scrapbox/link-includes-image.txt: -------------------------------------------------------------------------------- 1 | [http://example.com/example.png http://example.com/example] 2 | -------------------------------------------------------------------------------- /packages/md2sb/test/fixtures/scrapbox/link.txt: -------------------------------------------------------------------------------- 1 | [test http://example.com/example] 2 | 3 | [test http://example.com/example] 4 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/evernote.txt: -------------------------------------------------------------------------------- 1 | This is a fancy titled 2 | [* Some text] 3 | 4 | #fancy #house #in #inaka 5 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/links-empty.txt: -------------------------------------------------------------------------------- 1 | Untitled 2 | [Example] 3 | [Example_2] 4 | [Example_3] 5 | [Example_4] 6 | -------------------------------------------------------------------------------- /packages/md2sb/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base", 3 | "include": [ 4 | "src/**/*.ts" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/hr.txt: -------------------------------------------------------------------------------- 1 | Untitled 2 | [/icons/hr.icon] 3 | 4 | hello 5 | 6 | [/icons/hr.icon] 7 | 8 | Holla 9 | -------------------------------------------------------------------------------- /packages/md2sb/test/fixtures/md/blockquote.md: -------------------------------------------------------------------------------- 1 | > This is quoted text 2 | > nextline 3 | > > This is nested quoted test 4 | > hogehoge 5 | -------------------------------------------------------------------------------- /packages/md2sb/test/fixtures/md/link.md: -------------------------------------------------------------------------------- 1 | [test](http://example.com/example) 2 | 3 | [test](http://example.com/example "sample title") 4 | -------------------------------------------------------------------------------- /packages/md2sb/test/fixtures/scrapbox/blockquote.txt: -------------------------------------------------------------------------------- 1 | > This is quoted text 2 | > nextline 3 | > > This is nested quoted testhogehoge 4 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/.eslintrc.js: -------------------------------------------------------------------------------- 1 | const baseConfig = require("../../.eslintrc.js"); 2 | 3 | module.exports = { 4 | ...baseConfig, 5 | }; 6 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/list-of-links.txt: -------------------------------------------------------------------------------- 1 | List (Untitled) 2 | [http://example.com Example] 3 | [http://example.com/example2.png] 4 | -------------------------------------------------------------------------------- /packages/md2sb/test/fixtures/md/hr.md: -------------------------------------------------------------------------------- 1 | * * * 2 | 3 | *** 4 | 5 | ***** 6 | 7 | - - - 8 | 9 | --------------------------------------- 10 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/images.txt: -------------------------------------------------------------------------------- 1 | Untitled 2 | [http://example.com/example.png] 3 | [http://example.com/printImage.cgi?name=bird#.png] 4 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "composite": true 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /packages/enex2sb/src/node.ts: -------------------------------------------------------------------------------- 1 | import uploadImage from "./libs/uploadImage"; 2 | import main from "./main"; 3 | 4 | export default main.bind(null, uploadImage); 5 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/invalid-text-in-table.html: -------------------------------------------------------------------------------- 1 | 2 | invalid 3 | 4 |
cell
5 | -------------------------------------------------------------------------------- /packages/scrapbox-converter/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base", 3 | "include": [ 4 | "src/*.ts", 5 | "src/*/**.ts" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /packages/enex2sb/src/browser.ts: -------------------------------------------------------------------------------- 1 | import uploadImage from "./browser/uploadImage"; 2 | import main from "./main"; 3 | 4 | export default main.bind(null, uploadImage); 5 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/src/index.ts: -------------------------------------------------------------------------------- 1 | export { toScrapbox } from "./toScrapbox"; 2 | export { parse } from "./parse"; 3 | export { guessTitle } from "./guessTitle"; 4 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/images.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/complex-paragraph.html: -------------------------------------------------------------------------------- 1 |
2 |

Hello

3 |

World

4 |
5 |
6 |

paragraph 1
writing

7 |
8 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/table.txt: -------------------------------------------------------------------------------- 1 | Table (Untitled) 2 | table: 3 | whos [/ house] [_ runs] 4 | [* superhouse] I [http://test.com said] 5 | 6 | whos house? 7 | -------------------------------------------------------------------------------- /packages/md2sb/test/fixtures/scrapbox/hr.txt: -------------------------------------------------------------------------------- 1 | [/icons/hr.icon] 2 | 3 | [/icons/hr.icon] 4 | 5 | [/icons/hr.icon] 6 | 7 | [/icons/hr.icon] 8 | 9 | [/icons/hr.icon] 10 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base", 3 | "compilerOptions": { 4 | "rootDir": "./src" 5 | }, 6 | "include": ["src/**/*.ts"] 7 | } 8 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/styled-code.txt: -------------------------------------------------------------------------------- 1 | Untitled 2 | aaa 3 | code:_ 4 | hello 5 | world 6 | 7 | bbb 8 | 9 | code:_ 10 | hello2 11 | world2 12 | 13 | ccc 14 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/list-of-links.html: -------------------------------------------------------------------------------- 1 | 5 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/formatting.txt: -------------------------------------------------------------------------------- 1 | Untitled 2 | super script works as well as subscript and 3 | 4 | 5 | how about a little font change or [** size change?] colors can be changed as well 6 | -------------------------------------------------------------------------------- /packages/md2sb/test/fixtures/md/heading.md: -------------------------------------------------------------------------------- 1 | # h1 2 | test 3 | 4 | ## h2 5 | 2test 6 | 7 | ### h3 8 | 3test 9 | 10 | #### h4 11 | 4test 12 | 13 | #### h5 14 | 5test 15 | 16 | #### h6 17 | 6test 18 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/biome.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../biome.json"], 3 | "linter": { 4 | "rules": { 5 | "performance": { 6 | "noDelete": "off" 7 | } 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/list-skipped-inheritance.txt: -------------------------------------------------------------------------------- 1 | Untitled 2 | list item 3 | level1 4 | level2 5 | level4 6 | 7 | 1. ordered list 8 | 2. second 9 | 1. level2 10 | non ordered 11 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/text-styles.txt: -------------------------------------------------------------------------------- 1 | Untitled 2 | [*/ text] [*/ text] [/ [* a text] not really but [* b text]] 3 | [* [- Strikethrough text] and] 4 | [/_ Italic underline text too!] some unknown tag 5 | -------------------------------------------------------------------------------- /packages/md2sb/test/fixtures/md/paragraph.md: -------------------------------------------------------------------------------- 1 | This is test paragraph. 2 | 3 | これはテスト用の文章です。This is test paragraph. 4 | 5 | This is test paragraph.This is test paragraph.This is test paragraph.This is test paragraph. 6 | -------------------------------------------------------------------------------- /packages/md2sb/test/fixtures/scrapbox/heading.txt: -------------------------------------------------------------------------------- 1 | [**** h1] 2 | test 3 | 4 | [*** h2] 5 | 2test 6 | 7 | [** h3] 8 | 3test 9 | 10 | [* h4] 11 | 4test 12 | 13 | [* h5] 14 | 5test 15 | 16 | [* h6] 17 | 6test 18 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/links-empty.html: -------------------------------------------------------------------------------- 1 | Example
2 | Example 2
3 | Example 3
4 | Example 4 5 | -------------------------------------------------------------------------------- /packages/md2sb/test/fixtures/scrapbox/paragraph.txt: -------------------------------------------------------------------------------- 1 | This is test paragraph. 2 | 3 | これはテスト用の文章です。This is test paragraph. 4 | 5 | This is test paragraph.This is test paragraph.This is test paragraph.This is test paragraph. 6 | -------------------------------------------------------------------------------- /packages/enex2sb/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base", 3 | "include": [ 4 | "src/**/*.ts" 5 | ], 6 | "references": [{ 7 | "path": "../html2sb-compiler/tsconfig.build.json" 8 | }] 9 | } 10 | -------------------------------------------------------------------------------- /packages/html2sb/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base", 3 | "include": [ 4 | "src/**/*.ts" 5 | ], 6 | "references": [{ 7 | "path": "../html2sb-compiler/tsconfig.build.json" 8 | }] 9 | } 10 | -------------------------------------------------------------------------------- /packages/md2sb/src/libs/addListItemCount.ts: -------------------------------------------------------------------------------- 1 | export default (nodeList) => 2 | nodeList.map((node, index) => { 3 | if (node.type !== "listItem") return node; 4 | node.listItemCount = index + 1; 5 | return node; 6 | }); 7 | -------------------------------------------------------------------------------- /packages/md2sb/test/fixtures/md/list-strong-style-text.md: -------------------------------------------------------------------------------- 1 | - **強い強調(strong)です。** __これも強い強調です。__ ``strongタグです。`` 2 | - *強調(em)です。* _これも強調です。_ 斜体の``タグになります。 3 | - ***強調斜体です。*** ___強調斜体です。___ ``+``タグになります。 4 | -------------------------------------------------------------------------------- /packages/md2sb/test/fixtures/scrapbox/table.txt: -------------------------------------------------------------------------------- 1 | table:table 2 | Left align Right align Center align 3 | This This This 4 | column column column 5 | will will will 6 | be be be 7 | left right center 8 | aligned aligned aligned 9 | -------------------------------------------------------------------------------- /packages/scrapbox-converter/src/libs/loader/md.ts: -------------------------------------------------------------------------------- 1 | import * as fs from "node:fs"; 2 | import md2sb from "md2sb"; 3 | 4 | export default async (path: string): Promise => { 5 | return await md2sb(fs.readFileSync(path)); 6 | }; 7 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/links.txt: -------------------------------------------------------------------------------- 1 | Untitled 2 | [http://example.com http://example.com/example.png] 3 | [http://example.com/example2.png] [http://example.com Some text other than an image [* and bold] as well] 4 | scroll to here 5 | -------------------------------------------------------------------------------- /packages/md2sb/test/fixtures/scrapbox/list-strong-style-text.txt: -------------------------------------------------------------------------------- 1 | [* 強い強調(strong)です。] [* これも強い強調です。] ``strongタグです。`` 2 | [/ 強調(em)です。] [/ これも強調です。] 斜体の``タグになります。 3 | [/* 強調斜体です。] [/* 強調斜体です。] ``+``タグになります。 4 | -------------------------------------------------------------------------------- /packages/md2sb/test/fixtures/scrapbox/list.txt: -------------------------------------------------------------------------------- 1 | item 2 | 3 | unchecked item 4 | checked item 5 | item 6 | 1. item 7 | 2. item 8 | 9 | item 10 | item 11 | 12 | 13 | 1. item 14 | 2. item 15 | 1. item 16 | 2. item 17 | 3. item 18 | item 19 | -------------------------------------------------------------------------------- /packages/md2sb/test/complexText.test.ts: -------------------------------------------------------------------------------- 1 | import { test } from "vitest"; 2 | import loadAndAssert from "./helpers/loadAndAssert"; 3 | 4 | ["link-includes-image", "list-strong-style-text"].forEach((type) => { 5 | test("convert " + type, () => loadAndAssert(type)); 6 | }); 7 | -------------------------------------------------------------------------------- /packages/md2sb/test/fixtures/md/list.md: -------------------------------------------------------------------------------- 1 | * item 2 | + [ ] unchecked item 3 | * [x] checked item 4 | + item 5 | 1. item 6 | 1. item 7 | - item 8 | * item 9 | 10 | 1. item 11 | 1. item 12 | 1. item 13 | 1. item 14 | 1. item 15 | - item 16 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/entities.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": null, 3 | "children": [ 4 | { 5 | "type": "div", 6 | "children": [ 7 | { 8 | "type": "text", 9 | "text": "寿司" 10 | } 11 | ] 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/list.txt: -------------------------------------------------------------------------------- 1 | Untitled 2 | 1. France 3 | 2. Italy 4 | 3. Norway 5 | 6 | Tiles 7 | Bottles 8 | Sofas 9 | 10 | ⬜ Item 1 11 | ⬜ Item 2 12 | ✅ Item 3 (selected) 13 | ⬜ Item 4 14 | 15 | [https://test.com/image1.png] 16 | [https://test.com/image2.png] 17 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/blocks.txt: -------------------------------------------------------------------------------- 1 | Untitled 2 | And some more text... 3 | 4 | Lets align it right 5 | 6 | and align it center 7 | 8 | 9 | Indent a little 10 | 11 | > A little bit of this, a little bit of that, a little bit of everything lets me 12 | 13 | >> Indent a little further 14 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/evernote.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "page", 3 | "children": [ 4 | { 5 | "type": "text", 6 | "bold": true, 7 | "text": "Some text" 8 | } 9 | ], 10 | "title": "This is a fancy titled", 11 | "tags": ["fancy", "house", "in", "inaka"] 12 | } 13 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/list-in-list.txt: -------------------------------------------------------------------------------- 1 | List ([* France] B..., Italy Parmeg..., Norway Nothi...) 2 | 1. [* France] 3 | Baguette 4 | Camenbert 5 | Ganache 6 | 2. Italy 7 | Parmegiano 8 | Prociutto 9 | Ricotta 10 | 3. Norway 11 | Nothing 12 | comes 13 | to 14 | mind 15 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/text-styles.html: -------------------------------------------------------------------------------- 1 | text 2 | 3 | text 4 | 5 | a text not really but b text
6 | 7 | Strikethrough text and
8 | Italic underline text too! 9 | 10 | some unknown tag 11 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/evernote-multipage-1.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "page", 3 | "children": [ 4 | { 5 | "type": "div", 6 | "children": [ 7 | { 8 | "type": "text", 9 | "text": "本文1" 10 | } 11 | ] 12 | } 13 | ], 14 | "title": "ノート1" 15 | } 16 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/evernote-multipage-2.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "page", 3 | "children": [ 4 | { 5 | "type": "div", 6 | "children": [ 7 | { 8 | "type": "text", 9 | "text": "本文2" 10 | } 11 | ] 12 | } 13 | ], 14 | "title": "ノート2" 15 | } 16 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/evernote-multipage-3.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "page", 3 | "children": [ 4 | { 5 | "type": "div", 6 | "children": [ 7 | { 8 | "type": "text", 9 | "text": "本文3" 10 | } 11 | ] 12 | } 13 | ], 14 | "title": "ノート3" 15 | } 16 | -------------------------------------------------------------------------------- /packages/md2sb/test/fixtures/scrapbox/codeblock.txt: -------------------------------------------------------------------------------- 1 | code:_ 2 | def hoge 3 | p :foooo 4 | end 5 | 6 | code: (ruby) 7 | def hoge 8 | p :foooo 9 | end 10 | 11 | code:sample.js(js) 12 | () => { 13 | console.log('fooo') 14 | } 15 | 16 | code:sample(javascript) 17 | () => { 18 | console.log('fooo') 19 | } 20 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/links.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 | Some text other than an image and bold as well 6 |
7 | scroll to here 8 | -------------------------------------------------------------------------------- /packages/md2sb/test/fixtures/md/codeblock.md: -------------------------------------------------------------------------------- 1 | ``` 2 | def hoge 3 | p :foooo 4 | end 5 | ``` 6 | 7 | ```ruby 8 | def hoge 9 | p :foooo 10 | end 11 | ``` 12 | 13 | ```js:sample.js 14 | () => { 15 | console.log('fooo') 16 | } 17 | ``` 18 | 19 | ```javascript:sample 20 | () => { 21 | console.log('fooo') 22 | } 23 | ``` 24 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/table-in-div.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
whoshouseruns
11 |
12 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/header.txt: -------------------------------------------------------------------------------- 1 | Header 0 2 | Regular Text 3 | 4 | Header 5 5 | 6 | [** Header 4] 7 | 8 | [*** Header 3] 9 | 10 | [**** Header 2] 11 | 12 | [***** Header 1] 13 | 14 | [****** Header 0] 15 | 16 | [****** Header 0] 17 | [***** Header 1] 18 | [**** Header 2] 19 | [*** Header 3] 20 | [** Header 4] 21 | -------------------------------------------------------------------------------- /.changeset/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://unpkg.com/@changesets/config@3.0.5/schema.json", 3 | "changelog": "@changesets/cli/changelog", 4 | "commit": false, 5 | "fixed": [], 6 | "linked": [], 7 | "access": "restricted", 8 | "baseBranch": "master", 9 | "updateInternalDependencies": "patch", 10 | "ignore": [] 11 | } 12 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/hr.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": null, 3 | "children": [ 4 | { 5 | "type": "hr" 6 | }, 7 | { 8 | "type": "text", 9 | "text": "hello" 10 | }, 11 | { 12 | "type": "hr" 13 | }, 14 | { 15 | "type": "text", 16 | "text": "Holla" 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /packages/md2sb/test/simpleReteral.test.ts: -------------------------------------------------------------------------------- 1 | import { test } from "vitest"; 2 | import loadAndAssert from "./helpers/loadAndAssert"; 3 | 4 | [ 5 | "blockquote", 6 | "codeblock", 7 | "heading", 8 | "hr", 9 | "list", 10 | "paragraph", 11 | "table", 12 | ].forEach((type) => { 13 | test("convert " + type, () => loadAndAssert(type)); 14 | }); 15 | -------------------------------------------------------------------------------- /packages/md2sb/test/textDecoration.test.ts: -------------------------------------------------------------------------------- 1 | import { test } from "vitest"; 2 | import loadAndAssert from "./helpers/loadAndAssert"; 3 | 4 | [ 5 | "strong", 6 | "italic", 7 | "strong-italic", 8 | "strike", 9 | "link", 10 | "image", 11 | "code", 12 | ].forEach((type) => { 13 | test("convert " + type, () => loadAndAssert(type)); 14 | }); 15 | -------------------------------------------------------------------------------- /packages/enex2sb/src/libs/uploadImage.ts: -------------------------------------------------------------------------------- 1 | import Gyazo from "gyazo-api"; 2 | import intoStream from "into-stream"; 3 | 4 | const client = new Gyazo(process.env.GYAZO_ACCESS_TOKEN); 5 | 6 | export default async (file) => { 7 | const stream = intoStream(file); 8 | stream.path = "evernote-imported-file"; 9 | return await client.upload(stream); 10 | }; 11 | -------------------------------------------------------------------------------- /packages/scrapbox-converter/src/libs/loader/enex.ts: -------------------------------------------------------------------------------- 1 | import * as fs from "node:fs"; 2 | import enex2sb from "enex2sb"; 3 | 4 | export default async (path) => { 5 | try { 6 | return await enex2sb(fs.readFileSync(path)); 7 | } catch (e) { 8 | if (!process.env.GYAZO_ACCESS_TOKEN) { 9 | throw new Error("You should set GYAZO_ACCESS_TOKEN"); 10 | } 11 | } 12 | }; 13 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/images.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": null, 3 | "children": [ 4 | { 5 | "type": "img", 6 | "src": "http://example.com/example.png" 7 | }, 8 | { 9 | "type": "br", 10 | "force": true 11 | }, 12 | { 13 | "type": "img", 14 | "src": "http://example.com/printImage.cgi?name=bird" 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/list-wrong-inheritance.txt: -------------------------------------------------------------------------------- 1 | Untitled 2 | 1. [* France] 3 | Baguette 4 | Camenbert 5 | Ganache 6 | 2. Italy 7 | Parmegiano 8 | Prociutto 9 | Ricotta 10 | 3. Norway 11 | Nothing 12 | comes 13 | to 14 | mind 15 | 16 | list item 17 | level1 18 | level2 19 | level4 20 | 21 | 1. ordered list 22 | 2. second 23 | 1. level2 24 | non ordered 25 | -------------------------------------------------------------------------------- /packages/md2sb/test/fixtures/md/table.md: -------------------------------------------------------------------------------- 1 | | Left align | Right align | Center align | 2 | |:-----------|------------:|:------------:| 3 | | This | This | This | 4 | | column | column | column | 5 | | will | will | will | 6 | | be | be | be | 7 | | left | right | center | 8 | | aligned | aligned | aligned | 9 | -------------------------------------------------------------------------------- /tsconfig.base.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2019", 4 | "module": "commonjs", 5 | "moduleResolution": "node", 6 | "strict": false, 7 | "noImplicitAny": false, 8 | "removeComments": true, 9 | "noUnusedLocals": true, 10 | "noUnusedParameters": true, 11 | "esModuleInterop": true, 12 | "forceConsistentCasingInFileNames": true, 13 | "sourceMap": true, 14 | "declaration": true 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/scrapbox-converter/src/libs/loader/html.ts: -------------------------------------------------------------------------------- 1 | import * as fs from "node:fs"; 2 | import { guessTitle, parse, toScrapbox } from "html2sb-compiler"; 3 | 4 | export default async (path) => { 5 | const htmlString = fs.readFileSync(path).toString(); 6 | const parsed = parse(htmlString); 7 | const sb = toScrapbox(parsed[0]); 8 | sb.title = guessTitle(parsed, sb, (_, foundTitle) => { 9 | return foundTitle || null; 10 | }); 11 | return sb; 12 | }; 13 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/list-of-links.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": null, 3 | "children": [ 4 | { 5 | "type": "list", 6 | "variant": "ul", 7 | "children": [ 8 | { 9 | "type": "text", 10 | "href": "http://example.com", 11 | "text": "Example" 12 | }, 13 | { 14 | "type": "img", 15 | "src": "http://example.com/example2.png" 16 | } 17 | ] 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /packages/html2sb/src/main.ts: -------------------------------------------------------------------------------- 1 | import { parse, toScrapbox } from "html2sb-compiler"; 2 | 3 | export default async (input) => { 4 | let htmlString = input; 5 | if (typeof input === "object") { 6 | if (input instanceof Buffer) { 7 | htmlString = input.toString(); 8 | } else if (typeof input !== "string") { 9 | throw new Error("It allows string or buffer"); 10 | } 11 | } 12 | const result = toScrapbox(parse(htmlString)[0]); 13 | return result.lines.join("\n") + "\n"; 14 | }; 15 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/code.txt: -------------------------------------------------------------------------------- 1 | Untitled 2 | code:_ 3 | function myfunction (a, b) { 4 | let x = a * 1.08 5 | let y = b * 2 6 | return x * y 7 | } 8 | 9 | Hello 10 | 11 | Holla 12 | 13 | code:_ 14 | function myfunction (a, b) { 15 | let x = a * 1.08 16 | let y = b * 2 17 | return x * y 18 | } 19 | 20 | muxa 21 | 22 | code:_ 23 | function foo () {} 24 | 25 | code:_ 26 | function bar () {} 27 | 28 | code:_ 29 | warn $user->name; 30 | 31 | code:_ 32 | user.name 33 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/invalid-list-in-list.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": null, 3 | "children": [ 4 | { 5 | "type": "list", 6 | "variant": "ul", 7 | "children": [ 8 | { 9 | "type": "text", 10 | "children": [ 11 | { 12 | "type": "br" 13 | }, 14 | { 15 | "type": "list", 16 | "variant": "ul" 17 | } 18 | ] 19 | } 20 | ] 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/blocks.html: -------------------------------------------------------------------------------- 1 |
And some more text...
2 |
Lets align it right
3 |
and align it center
4 |

5 |
Indent a little
6 |
7 |
A little bit of this, a little bit of that, a little bit of everything lets me
8 |
9 |
10 |
Indent a little further
11 |
12 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/list.html: -------------------------------------------------------------------------------- 1 |
  1. France
  2. Italy
  3. Norway
2 | 3 |
  • Tiles
  • Bottles
  • Sofas
4 | 5 |
Item 1
6 |
Item 2
7 |
Item 3 (selected)
8 |
Item 4
9 | 10 |
    11 |
  • 12 |
  • 13 |
14 | 15 |
    16 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/evernote.html: -------------------------------------------------------------------------------- 1 | 2 | This is a fancy titled 3 | Some text]]> 4 | 20170425T045757Z 5 | 20170515T153011Z 6 | in 7 | inaka 8 | house 9 | fancy 10 | 11 | mh579 12 | 1493097211000 13 | 20170420T230000Z 14 | 15 | 16 | -------------------------------------------------------------------------------- /packages/md2sb/test/helpers/loadAndAssert.ts: -------------------------------------------------------------------------------- 1 | import fs from "node:fs"; 2 | import path from "node:path"; 3 | import { expect } from "vitest"; 4 | import md2sb from "../../src/main"; 5 | 6 | export default async (type) => { 7 | const markdown = fs.readFileSync( 8 | path.resolve("test/fixtures/md/" + type + ".md"), 9 | ); 10 | const input = await md2sb(markdown); 11 | const expected = fs 12 | .readFileSync(path.resolve("test/fixtures/scrapbox/" + type + ".txt")) 13 | .toString(); 14 | expect(input).toEqual(expected); 15 | }; 16 | -------------------------------------------------------------------------------- /packages/md2sb/src/libs/generateCodeBlock.ts: -------------------------------------------------------------------------------- 1 | export default (node) => { 2 | let result = "code:"; 3 | if (node.lang) { 4 | const t = node.lang.split(":"); 5 | if (t.length === 2) { 6 | result += t[1] + `(${t[0]})\n`; 7 | } else if (node.lang.indexOf(".") > -1) { 8 | // ファイル名のみが書かれているとき 9 | result += node.lang; 10 | } else { 11 | result += ` (${node.lang})\n`; 12 | } 13 | } else { 14 | result += "_\n"; 15 | } 16 | result += " " + node.value.split("\n").join("\n "); 17 | return result; 18 | }; 19 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/header.html: -------------------------------------------------------------------------------- 1 |
    Regular Text
    2 |
    Header 5
    3 |
    Header 4
    4 |
    Header 3
    5 |
    Header 2
    6 |
    Header 1
    7 |
    Header 0
    8 |

    Header 0

    9 |

    Header 1

    10 |

    Header 2

    11 |

    Header 3

    12 |
    Header 4
    13 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/styled-code.html: -------------------------------------------------------------------------------- 1 |
    2 |
    aaa
    3 |
    4 |
    5 | hello 6 |
    7 |
    8 | world 9 |
    10 |
    11 |
    bbb
    12 |
    13 | 14 |
    15 |
    hello2
    16 |
    world2
    17 |
    18 | 19 |
    ccc
    20 | -------------------------------------------------------------------------------- /packages/html2sb/test/evernote-html.test.ts: -------------------------------------------------------------------------------- 1 | import * as fs from "node:fs"; 2 | import * as path from "node:path"; 3 | import { expect, test } from "vitest"; 4 | import html2sb from "../src/main"; 5 | 6 | test("convert html exported by evernote", async (t) => { 7 | const expected = fs 8 | .readFileSync(path.resolve("test/fixtures/scrapbox/evernote/test.txt")) 9 | .toString(); 10 | const input = await html2sb( 11 | fs.readFileSync(path.resolve("test/fixtures/html/evernote/test.html")), 12 | ); 13 | expect(input).toEqual(expected); 14 | }); 15 | -------------------------------------------------------------------------------- /packages/enex2sb/src/browser/uploadImage.ts: -------------------------------------------------------------------------------- 1 | import upload from "gyazo-browser-upload"; 2 | 3 | export default async (buffer, options) => { 4 | // We could implement the proper data type but that would be wasted effort 5 | // because gyazo looks at the buffer content only, anyways 6 | const res = await upload( 7 | "data:image/*;base64," + buffer.toString("base64"), 8 | options, 9 | ); 10 | return { 11 | data: { 12 | // eslint-disable-next-line @typescript-eslint/camelcase 13 | permalink_url: res.url, 14 | }, 15 | }; 16 | }; 17 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/list-in-list.html: -------------------------------------------------------------------------------- 1 |
      2 |
    1. 3 | France 4 |
      5 |
        6 |
      • Baguette
      • 7 |
      • Camenbert
      • 8 |
      • Ganache
      • 9 |
      10 |
    2. 11 |
    3. 12 | Italy 13 |
        14 |
      • Parmegiano
      • 15 |
      • Prociutto
      • 16 |
      • Ricotta
      • 17 |
      18 |
    4. 19 |
    5. 20 | Norway 21 |
        22 |
      • Nothing
      • 23 |
      • comes
      • 24 |
      • to
      • 25 |
      • mind
      • 26 |
      27 |
    6. 28 |
    29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # scrapbox-converter mono respository 2 | 3 | ## This repsitory contains 4 | 5 | - main command 6 | - [scrapbox-converter](https://github.com/pastak/scrapbox-converter/tree/master/packages/scrapbox-converter) 7 | - converters 8 | - [md2sb](https://github.com/pastak/scrapbox-converter/tree/master/packages/md2sb) 9 | - Markdown 10 | - [enex2sb](https://github.com/pastak/scrapbox-converter/tree/master/packages/enex2sb) 11 | - Evernote's XML Export Format 12 | - [html2sb](https://github.com/pastak/scrapbox-converter/tree/master/packages/html2sb) 13 | - HTML (experimental) 14 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/simple-paragraph.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": null, 3 | "children": [ 4 | { 5 | "type": "div", 6 | "children": [ 7 | { 8 | "type": "div", 9 | "children": [ 10 | { 11 | "type": "text", 12 | "text": "Hello" 13 | } 14 | ] 15 | }, 16 | { 17 | "type": "div", 18 | "children": [ 19 | { 20 | "type": "text", 21 | "text": "World" 22 | } 23 | ] 24 | } 25 | ] 26 | } 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/invalid-text-in-table.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": null, 3 | "children": [ 4 | { 5 | "type": "table", 6 | "children": [ 7 | { 8 | "type": "text", 9 | "text": "invalid" 10 | }, 11 | { 12 | "type": "tr", 13 | "children": [ 14 | { 15 | "type": "td", 16 | "children": [ 17 | { 18 | "type": "text", 19 | "text": "cell" 20 | } 21 | ] 22 | } 23 | ] 24 | } 25 | ] 26 | } 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /packages/html2sb/test/fixtures/scrapbox/evernote/test.txt: -------------------------------------------------------------------------------- 1 | test 2 | 3 | This is text. 4 | 5 | 6 | [* bold] 7 | 8 | 9 | [/ Italic] 10 | 11 | [_ Underline] 12 | 13 | 14 | red 15 | 16 | 17 | 18 | hightlighted 19 | 20 | 21 | [***** font size large] 22 | 23 | 24 | [http://example.com link] 25 | 26 | 27 | 28 | 29 | list item 30 | level1 31 | level2 32 | level4 33 | 34 | 1. ordered list 35 | 2. second 36 | 1. level2 37 | non ordered 38 | 39 | 40 | todo 41 | 42 | done 43 | 44 | 45 | [test.resources/47C4AAC0-EFA4-4E4F-98F5-0ACD38B650F8.png] 46 | 47 | 48 | [/icons/hr.icon] 49 | 50 | table: 51 | 1x1 1x2 52 | 2x1 2x2 53 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/list-skipped-inheritance.html: -------------------------------------------------------------------------------- 1 |
      2 |
    • list item
    • 3 |
    • level1 4 |
        5 |
      • level2 6 |
          7 |
        • 8 |
            9 |
          • level4
          • 10 |
          11 |
        • 12 |
        13 |
      • 14 |
      15 |
    • 16 |
    17 |
      18 |
    1. ordered list
    2. 19 |
    3. second 20 |
        21 |
      1. level2
      2. 22 |
      23 |
    4. 24 |
    5. 25 |
        26 |
      • non ordered
      • 27 |
      28 |
    6. 29 |
    30 | -------------------------------------------------------------------------------- /packages/enex2sb/test/fixtures/example.sb.txt: -------------------------------------------------------------------------------- 1 | This is text. 2 | 3 | 4 | [* bold] 5 | 6 | 7 | [/ Italic] 8 | 9 | 10 | [_ Underline] 11 | 12 | 13 | red 14 | 15 | 16 | 17 | [_ hightlighted] 18 | 19 | 20 | [***** font size large] 21 | 22 | 23 | [http://example.com link] 24 | 25 | 26 | 27 | 28 | list item 29 | level1 30 | level2 31 | level4 32 | 33 | 1. ordered list 34 | 2. second 35 | 1. level2 36 | non ordered 37 | 38 | 39 | ⬜ todo 40 | ✅ done 41 | 42 | 43 | [https://gyazo.com/abcdef0123456789abcdef0123456789] 44 | 45 | 46 | [/icons/hr.icon] 47 | 48 | table: 49 | 1x1 1x2 50 | 2x1 2x2 51 | 52 | 53 | 54 | #tag1 #tag2 55 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/table.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
    whoshouseruns
    superhouse
    I



    whos
    house?
    25 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/formatting.html: -------------------------------------------------------------------------------- 1 |
    2 | super script 3 | works as well as 4 | subscript 5 | and 6 |
    7 |

    8 |
    9 | how about a little 10 | font change or 11 | size change? 12 | colors can 13 | be changed as well 14 |
    15 | -------------------------------------------------------------------------------- /packages/scrapbox-converter/README.md: -------------------------------------------------------------------------------- 1 | # scrapbox-converter 2 | 3 | import scrapbox from markdown|html|enex files 4 | 5 | ## Support Format 6 | 7 | - Markdown 8 | - Enex 9 | - Evernote's XML Export Format 10 | - If you want to convert this format, you should set `GYAZO_ACCESS_TOKEN` to OAuth access token get from https://gyazo.com/oauth/applications . 11 | - It uses to upload embeded images to gyazo.com 12 | - HTML (experimental) 13 | 14 | ## Usage 15 | 16 | 1. `% pnpm install -g scrapbox-converter` 17 | 2. `% scrapbox-converter ...YOUR_FILES_OR_DIRECTORYS_PATH > import.json` 18 | 3. Access `https://scrapbox.io/projects/YOUR_PROJECT/settings/page-data` 19 | 4. Select and upload `import.json` on Import Pages 20 | 5. Have fun on Scrapbox 🎉 21 | -------------------------------------------------------------------------------- /packages/html2sb/README.md: -------------------------------------------------------------------------------- 1 | # html2sb 2 | 3 | Encode HTML to Scrapbox.io style text 4 | 5 | ## Usage 6 | 7 | ### CLI 8 | 9 | - `% pnpm install -g html2sb` 10 | - `% html2sb hoge.html > hoge.txt` 11 | - You can pass filename as option 12 | - `% cat hoge.html | html2sb > hoge.txt` 13 | - You can pass markdown text via stdin 14 | 15 | ### API 16 | 17 | TBW 18 | 19 | ## Development 20 | 21 | - Require 22 | - NodeJS 23 | 24 | 1. Fork it 25 | 2. `% git clone git@github.com:YOUR-NAME/md2sb.git` 26 | 3. `cd md2sb` 27 | 4. `git checkout -b YOUR_WORKING_BRANCH` 28 | 5. Write Some Code! 29 | - Build: `% pnpm run build` 30 | - Watch: `% pnpm run watch` 31 | - Test: `% pnpm test` 32 | - Test Watching: `% pnpm test:watch` 33 | 6. Commit your work 34 | 7. Open PR to this repository 🎉 35 | -------------------------------------------------------------------------------- /packages/enex2sb/test/multiple.test.ts: -------------------------------------------------------------------------------- 1 | import * as fs from "node:fs"; 2 | import * as path from "node:path"; 3 | import { expect, test } from "vitest"; 4 | import enex2sb from "../src/main"; 5 | const uploadImage = () => 6 | new Promise((ok) => { 7 | // eslint-disable-next-line @typescript-eslint/camelcase 8 | ok({ 9 | data: { 10 | permalink_url: "https://gyazo.com/abcdef0123456789abcdef0123456789", 11 | }, 12 | }); 13 | }); 14 | 15 | test("convet multiple note in one enex", async (t) => { 16 | const input = await enex2sb( 17 | uploadImage, 18 | fs.readFileSync(path.resolve("test/fixtures/multiple.enex")), 19 | ); 20 | expect(input.length).toBe(3); 21 | input.forEach((note, index) => { 22 | expect(note.title).toBe("ノート" + (index + 1)); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/links-empty.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": null, 3 | "children": [ 4 | { 5 | "type": "text", 6 | "href": "___SELF_WIKI_LINK___", 7 | "text": "Example" 8 | }, 9 | { 10 | "type": "br", 11 | "force": true 12 | }, 13 | { 14 | "type": "text", 15 | "href": "___SELF_WIKI_LINK___", 16 | "text": "Example 2" 17 | }, 18 | { 19 | "type": "br", 20 | "force": true 21 | }, 22 | { 23 | "type": "text", 24 | "href": "___SELF_WIKI_LINK___", 25 | "text": "Example 3" 26 | }, 27 | { 28 | "type": "br", 29 | "force": true 30 | }, 31 | { 32 | "type": "text", 33 | "href": "___SELF_WIKI_LINK___", 34 | "text": "Example 4" 35 | } 36 | ] 37 | } 38 | -------------------------------------------------------------------------------- /packages/md2sb/src/main.ts: -------------------------------------------------------------------------------- 1 | import remark from "remark"; 2 | import gfm from "remark-gfm"; 3 | import { compiler } from "./libs/compiler"; 4 | 5 | export default (input: string | Buffer): Promise => { 6 | let mdText = input; 7 | if (typeof input === "object") { 8 | if (input instanceof Buffer) { 9 | mdText = input.toString(); 10 | } else if (typeof input !== "string") { 11 | throw new Error("It allows string or buffer"); 12 | } 13 | } 14 | return new Promise((resolve, ng) => { 15 | // @ts-nocheck 16 | remark() 17 | .use(gfm) 18 | .use(compiler) 19 | .process(mdText, (err, file) => { 20 | if (err) return ng(err); 21 | const result = (file + "") as string; 22 | return resolve(result); 23 | }); 24 | }); 25 | }; 26 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "scrapbox-converter-monorepo", 3 | "scripts": { 4 | "preinstall": "npx only-allow pnpm", 5 | "clean": "rm -rf packages/*/dist", 6 | "lint": "pnpm run -r lint", 7 | "lint:fix": "pnpm run -r lint:fix", 8 | "lint:ci": "pnpm run -r lint:ci" 9 | }, 10 | "homepage": "https://github.com/pastak/scrapbox-converter#readme", 11 | "repository": { 12 | "type": "git", 13 | "url": "ssh://git@github.com/pastak/scrapbox-converter.git" 14 | }, 15 | "author": "pastak ", 16 | "license": "MIT", 17 | "devDependencies": { 18 | "@changesets/cli": "^2.27.12" 19 | }, 20 | "packageManager": "pnpm@10.2.1+sha512.398035c7bd696d0ba0b10a688ed558285329d27ea994804a52bad9167d8e3a72bcb993f9699585d3ca25779ac64949ef422757a6c31102c12ab932e5cbe5cc92" 21 | } 22 | -------------------------------------------------------------------------------- /.github/workflows/nodejs.yml: -------------------------------------------------------------------------------- 1 | name: Node CI 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - master 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | 14 | strategy: 15 | matrix: 16 | node-version: [20.x, 21.x, 22.x, 23.x] 17 | 18 | steps: 19 | - uses: actions/checkout@v1 20 | - uses: pnpm/action-setup@v4 21 | with: 22 | run_install: false 23 | - name: Use Node.js ${{ matrix.node-version }} 24 | uses: actions/setup-node@v3 25 | with: 26 | node-version: ${{ matrix.node-version }} 27 | cache: 'pnpm' 28 | - name: pnpm install, build, and test 29 | run: | 30 | pnpm install 31 | pnpm run -r build 32 | pnpm run -r lint:ci 33 | pnpm -r test 34 | env: 35 | CI: true 36 | -------------------------------------------------------------------------------- /packages/scrapbox-converter/src/command.ts: -------------------------------------------------------------------------------- 1 | import { program } from "commander"; 2 | const settings = require("../package.json"); // eslint-disable-line @typescript-eslint/no-var-requires 3 | import findAndLoadFiles from "./libs/findAndLoadFiles"; 4 | 5 | const finalize = (pages) => { 6 | const flatten = (arr) => 7 | arr.reduce((a, b) => { 8 | if (Array.isArray(b)) return a.concat(flatten(b)); 9 | return a.concat(b); 10 | }, []); 11 | return flatten(pages).filter((_) => _); 12 | }; 13 | 14 | program 15 | .version(settings.version) 16 | .usage("scrapbox-converter <...files>") 17 | .arguments("") 18 | .action(async (files) => { 19 | const pages = finalize(await findAndLoadFiles(files)); 20 | const json = { pages }; 21 | console.log(JSON.stringify(json)); 22 | }) 23 | .parse(process.argv); 24 | -------------------------------------------------------------------------------- /.github/workflows/npmpublish.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | concurrency: ${{ github.workflow }}-${{ github.ref }} 9 | 10 | jobs: 11 | release: 12 | name: Release 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Checkout Repo 16 | uses: actions/checkout@v3 17 | 18 | - name: Setup Node.js 22.x 19 | uses: actions/setup-node@v4 20 | with: 21 | node-version: 22.x 22 | - uses: pnpm/action-setup@v4 23 | with: 24 | run_install: true 25 | - run: pnpm run -r build 26 | - name: Create Release Pull Request or Publish to npm 27 | id: changesets 28 | uses: changesets/action@v1 29 | with: 30 | publish: pnpm publish -r 31 | env: 32 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 33 | NPM_TOKEN: ${{ secrets.npm_token }} 34 | -------------------------------------------------------------------------------- /packages/enex2sb/test/example.test.ts: -------------------------------------------------------------------------------- 1 | import * as fs from "node:fs"; 2 | import * as path from "node:path"; 3 | import { expect, test } from "vitest"; 4 | import enex2sb from "../src/main"; 5 | const uploadImage = () => 6 | new Promise((ok) => { 7 | // eslint-disable-next-line @typescript-eslint/camelcase 8 | ok({ 9 | data: { 10 | permalink_url: "https://gyazo.com/abcdef0123456789abcdef0123456789", 11 | }, 12 | }); 13 | }); 14 | 15 | test("convert example xml", async (t) => { 16 | const expected = fs 17 | .readFileSync(path.resolve("test/fixtures/example.sb.txt")) 18 | .toString(); 19 | const input = await enex2sb( 20 | uploadImage, 21 | fs.readFileSync(path.resolve("test/fixtures/example.enex")), 22 | ); 23 | expect(Array.isArray(input)).toBe(true); 24 | expect(input[0].lines.concat("")).toEqual(expected.split("\n")); 25 | expect(input[0].title).toBe("test"); 26 | }); 27 | -------------------------------------------------------------------------------- /packages/enex2sb/README.md: -------------------------------------------------------------------------------- 1 | # enex2sb 2 | Encode XML exported by Evernote to Scrapbox.io style importable json 3 | 4 | ## Usage 5 | 6 | ### CLI 7 | 8 | You should set `GYAZO_ACCESS_TOKEN` to OAuth access token get from https://gyazo.com/oauth/applications before exec command.(It uses to upload embeded images to gyazo.com) 9 | 10 | - `% pnpm install -g enex2sb` 11 | - `% enex2sb hoge.enex > hoge.json` 12 | - You can pass filename as option 13 | - `% cat hoge.enex | enex2sb > hoge.json` 14 | 15 | ### API 16 | 17 | TBW 18 | 19 | ## Development 20 | 21 | - Require 22 | - NodeJS 23 | 24 | 1. Fork it 25 | 2. `% git clone git@github.com:YOUR-NAME/enex2sb.git` 26 | 3. `% cd enex2sb` 27 | 4. `% git checkout -b YOUR_WORKING_BRANCH` 28 | 5. Write Some Code! 29 | - Build: `% pnpm run build` 30 | - Watch: `% pnpm run watch` 31 | - Test: `% pnpm test` 32 | - Test Watching: `% pnpm test:watch` 33 | 6. Commit your work 34 | 7. Open PR to this repository 🎉 35 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/styled-code.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": null, 3 | "children": [ 4 | { 5 | "type": "div", 6 | "children": [ 7 | { 8 | "type": "div", 9 | "children": [ 10 | { 11 | "type": "text", 12 | "text": "aaa" 13 | } 14 | ] 15 | }, 16 | { 17 | "type": "code", 18 | "text": "hello\nworld" 19 | }, 20 | { 21 | "type": "div", 22 | "children": [ 23 | { 24 | "type": "text", 25 | "text": "bbb" 26 | } 27 | ] 28 | } 29 | ] 30 | }, 31 | { 32 | "type": "code", 33 | "text": "hello2\nworld2" 34 | }, 35 | { 36 | "type": "div", 37 | "children": [ 38 | { 39 | "type": "text", 40 | "text": "ccc" 41 | } 42 | ] 43 | } 44 | ] 45 | } 46 | -------------------------------------------------------------------------------- /packages/md2sb/src/command/command.ts: -------------------------------------------------------------------------------- 1 | import * as fs from "node:fs"; 2 | import * as path from "node:path"; 3 | import { program } from "commander"; 4 | import md2sb from "./../main"; 5 | const settings = require("../../package.json"); // eslint-disable-line @typescript-eslint/no-var-requires 6 | 7 | let stdin = ""; 8 | 9 | program 10 | .version(settings.version) 11 | .description((settings as any).description) 12 | .usage("\n\tmd2sb [file] \n\tcat hoge.md | md2sb") 13 | .arguments("[file]") 14 | .action(async (file) => { 15 | const result = await md2sb(fs.readFileSync(path.resolve(file))); 16 | console.log(result); 17 | }); 18 | 19 | if (process.stdin.isTTY) { 20 | program.parse(process.argv); 21 | } else { 22 | process.stdin.on("readable", () => { 23 | const chunk = process.stdin.read(); 24 | if (chunk !== null) { 25 | stdin += chunk; 26 | } 27 | }); 28 | process.stdin.on("end", async () => { 29 | console.log(await md2sb(stdin)); 30 | }); 31 | } 32 | -------------------------------------------------------------------------------- /packages/html2sb/src/command/command.ts: -------------------------------------------------------------------------------- 1 | import * as fs from "node:fs"; 2 | import * as path from "node:path"; 3 | import { program } from "commander"; 4 | import html2sb from "./../main"; 5 | const settings = require("../../package.json"); // eslint-disable-line @typescript-eslint/no-var-requires 6 | 7 | let stdin = ""; 8 | 9 | program 10 | .version(settings.version) 11 | .description((settings as any).description) 12 | .usage("\nhtml2sb [file] \n\tcat hoge.html | html2sb") 13 | .arguments("[file]") 14 | .action(async (file) => { 15 | const result = await html2sb(fs.readFileSync(path.resolve(file))); 16 | console.log(result); 17 | }); 18 | 19 | if (process.stdin.isTTY) { 20 | program.parse(process.argv); 21 | } else { 22 | process.stdin.on("readable", () => { 23 | const chunk = process.stdin.read(); 24 | if (chunk !== null) { 25 | stdin += chunk; 26 | } 27 | }); 28 | process.stdin.on("end", async () => { 29 | console.log(await html2sb(stdin)); 30 | }); 31 | } 32 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/links.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": null, 3 | "children": [ 4 | { 5 | "type": "img", 6 | "href": "http://example.com", 7 | "src": "http://example.com/example.png" 8 | }, 9 | { 10 | "type": "br", 11 | "force": true 12 | }, 13 | { 14 | "type": "img", 15 | "src": "http://example.com/example2.png" 16 | }, 17 | { 18 | "type": "text", 19 | "href": "http://example.com", 20 | "children": [ 21 | { 22 | "type": "text", 23 | "text": "Some text other than an image" 24 | }, 25 | { 26 | "type": "text", 27 | "bold": true, 28 | "text": "and bold" 29 | }, 30 | { 31 | "type": "text", 32 | "text": "as well" 33 | } 34 | ] 35 | }, 36 | { 37 | "type": "br", 38 | "force": true 39 | }, 40 | { 41 | "type": "text", 42 | "text": "scroll to here" 43 | } 44 | ] 45 | } 46 | -------------------------------------------------------------------------------- /biome.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json", 3 | "vcs": { 4 | "enabled": false, 5 | "clientKind": "git", 6 | "useIgnoreFile": false 7 | }, 8 | "files": { 9 | "ignoreUnknown": false, 10 | "ignore": [ 11 | "dist/*" 12 | ] 13 | }, 14 | "formatter": { 15 | "enabled": true, 16 | "indentStyle": "space" 17 | }, 18 | "organizeImports": { 19 | "enabled": true 20 | }, 21 | "linter": { 22 | "enabled": true, 23 | "rules": { 24 | "recommended": true, 25 | "complexity": { 26 | "noUselessStringConcat": "off", 27 | "noForEach": "off" 28 | }, 29 | "style": { 30 | "useTemplate": "off", 31 | "noParameterAssign": "off" 32 | }, 33 | "suspicious": { 34 | "noExplicitAny": "off", 35 | "noImplicitAnyLet": "off", 36 | "noAssignInExpressions": "off" 37 | } 38 | } 39 | }, 40 | "javascript": { 41 | "formatter": { 42 | "quoteStyle": "double" 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /packages/md2sb/README.md: -------------------------------------------------------------------------------- 1 | # md2sb 2 | 3 | Encode Markdown to Scrapbox.io style text 4 | 5 | ## Sample Result 6 | 7 | [Sample Text(Ja)](test/fixtures/md/sample-ja.md) to https://scrapbox.io/pastak-pub/md2sb%20Sample 8 | 9 | (This sample text is from http://qiita.com/salchu/items/da81122ed50b35feda4d ([Revision](http://qiita.com/salchu/items/da81122ed50b35feda4d/revisions/6))) 10 | 11 | ## Usage 12 | 13 | ### CLI 14 | 15 | - `% npx md2sb hoge.md > hoge.txt` 16 | - You can pass filename as option 17 | - `% cat hoge.md | md2sb > hoge.txt` 18 | - You can pass markdown text via stdin 19 | 20 | ### API 21 | 22 | TBW 23 | 24 | ## Development 25 | 26 | - Require 27 | - NodeJS 28 | 29 | 1. Fork it 30 | 2. `% git clone git@github.com:YOUR-NAME/md2sb.git` 31 | 3. `cd md2sb` 32 | 4. `git checkout -b YOUR_WORKING_BRANCH` 33 | 5. Write Some Code! 34 | - Build: `% pnpm run build` 35 | - Watch: `% pnpm run watch` 36 | - Test: `% pnpm test` 37 | - Test Watching: `% pnpm test:watch` 38 | 6. Commit your work 39 | 7. Open PR to this repository 🎉 40 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/list-wrong-inheritance.html: -------------------------------------------------------------------------------- 1 |
      2 |
    1. 3 | France 4 |
    2. 5 |
        6 |
      • Baguette
      • 7 |
      • Camenbert
      • 8 |
      • Ganache
      • 9 |
      10 |
    3. 11 |
      12 | Italy 13 |
      14 |
    4. 15 |
        16 |
      • Parmegiano
      • 17 |
      • Prociutto
      • 18 |
      • Ricotta
      • 19 |
      20 |
    5. 21 | Norway 22 |
    6. 23 |
        24 |
      • Nothing
      • 25 |
      • comes
      • 26 |
      • to
      • 27 |
      • mind
      • 28 |
      29 |
    30 |
    31 |
      32 |
    • list item
    • 33 |
    • level1
    • 34 |
        35 |
      • level2
      • 36 |
          37 |
            38 |
          • level4
          • 39 |
          40 |
        41 |
      42 |
    43 |
    44 |
      45 |
    1. ordered list
    2. 46 |
    3. second
    4. 47 |
        48 |
      1. level2
      2. 49 |
      50 |
        51 |
      • non ordered
      • 52 |
      53 |
    54 |

    55 |
    56 |
    57 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/complex-paragraph.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": null, 3 | "children": [ 4 | { 5 | "type": "div", 6 | "children": [ 7 | { 8 | "type": "div", 9 | "children": [ 10 | { 11 | "type": "text", 12 | "text": "Hello" 13 | } 14 | ] 15 | }, 16 | { 17 | "type": "div", 18 | "children": [ 19 | { 20 | "type": "text", 21 | "text": "World" 22 | } 23 | ] 24 | } 25 | ] 26 | }, 27 | { 28 | "type": "div", 29 | "children": [ 30 | { 31 | "type": "div", 32 | "children": [ 33 | { 34 | "type": "text", 35 | "text": "paragraph 1" 36 | }, 37 | { 38 | "type": "br", 39 | "force": true 40 | }, 41 | { 42 | "type": "text", 43 | "text": "writing" 44 | } 45 | ] 46 | } 47 | ] 48 | } 49 | ] 50 | } 51 | -------------------------------------------------------------------------------- /packages/scrapbox-converter/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "scrapbox-converter", 3 | "version": "7.0.0", 4 | "bin": { 5 | "scrapbox-converter": "dist/index.js" 6 | }, 7 | "files": [ 8 | "dist" 9 | ], 10 | "homepage": "https://github.com/pastak/scrapbox-converter/tree/master/packages/scrapbox-converter#readme", 11 | "repository": { 12 | "type": "git", 13 | "url": "ssh://git@github.com/pastak/scrapbox-converter.git" 14 | }, 15 | "author": "pastak ", 16 | "license": "MIT", 17 | "scripts": { 18 | "build": "tsc --outDir dist --project .", 19 | "lint": "biome check ./src", 20 | "lint:fix": "biome check --write ./src", 21 | "lint:ci": "biome ci ./src", 22 | "watch": "pnpm run build -- --watch" 23 | }, 24 | "dependencies": { 25 | "@types/node": "20.14.8", 26 | "commander": "^5.1.0", 27 | "enex2sb": "workspace:*", 28 | "html2sb-compiler": "workspace:*", 29 | "lodash": "^4.17.19", 30 | "md2sb": "workspace:*" 31 | }, 32 | "devDependencies": { 33 | "@biomejs/biome": "^1.9.4", 34 | "typescript": "5.7.3", 35 | "vitest": "^3.0.5" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /packages/enex2sb/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 pastak 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/html2sb/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 pastak 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "html2sb-compiler", 3 | "version": "7.0.0", 4 | "main": "./dist/index.js", 5 | "files": [ 6 | "dist" 7 | ], 8 | "scripts": { 9 | "build": "tsc --outDir dist --project .", 10 | "lint": "biome check ./src ./test", 11 | "lint:fix": "biome check --write ./src ./test", 12 | "lint:ci": "biome ci ./src ./test", 13 | "test": "vitest run", 14 | "test:update-token": "UPDATE_TOKEN=1 pnpm run test" 15 | }, 16 | "homepage": "https://github.com/pastak/scrapbox-converter/tree/master/packages/html2sb-compiler#readme", 17 | "repository": { 18 | "type": "git", 19 | "url": "ssh://git@github.com/pastak/scrapbox-converter.git" 20 | }, 21 | "author": "pastak ", 22 | "license": "MIT", 23 | "dependencies": { 24 | "htmlparser2": "^4.0.0", 25 | "lodash.trim": "^4.5.1", 26 | "nano-md5": "^1.0.3", 27 | "style-parser": "^1.1.1" 28 | }, 29 | "devDependencies": { 30 | "@biomejs/biome": "^1.9.4", 31 | "@types/lodash.trim": "4.5.6", 32 | "@types/node": "20.14.8", 33 | "typescript": "5.7.3", 34 | "vitest": "^3.0.5" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /packages/scrapbox-converter/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 pastak 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/code.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": null, 3 | "children": [ 4 | { 5 | "type": "code", 6 | "text": "function myfunction (a, b) {\n  let x = a * 1.08\n  let y = b * 2\n  return x * y\n}" 7 | }, 8 | { 9 | "type": "div", 10 | "children": [ 11 | { 12 | "type": "text", 13 | "text": "Hello" 14 | } 15 | ] 16 | }, 17 | { 18 | "type": "text", 19 | "text": "Holla" 20 | }, 21 | { 22 | "type": "code", 23 | "text": "function myfunction (a, b) {\n  let x = a * 1.08\n  let y = b * 2\n  return x * y\n}" 24 | }, 25 | { 26 | "type": "div", 27 | "children": [ 28 | { 29 | "type": "text", 30 | "text": "muxa" 31 | } 32 | ] 33 | }, 34 | { 35 | "type": "code", 36 | "text": "function foo () {}" 37 | }, 38 | { 39 | "type": "code", 40 | "text": "function bar () {}" 41 | }, 42 | { 43 | "type": "code", 44 | "text": "warn $user->name;" 45 | }, 46 | { 47 | "type": "code", 48 | "text": "user.name" 49 | } 50 | ] 51 | } 52 | -------------------------------------------------------------------------------- /packages/html2sb/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "html2sb", 3 | "version": "7.0.0", 4 | "description": "Encode HTML to Scrapbox.io style text", 5 | "files": [ 6 | "dist" 7 | ], 8 | "main": "dist/index.js", 9 | "bin": { 10 | "html2sb": "dist/command/index.js" 11 | }, 12 | "scripts": { 13 | "build": "tsc --outDir dist --project .", 14 | "watch": "pnpm run build -- --watch", 15 | "test": "vitest run", 16 | "lint": "biome check ./src ./test", 17 | "lint:fix": "biome check --write ./src ./test", 18 | "lint:ci": "biome ci ./src ./test", 19 | "test:watch": "pnpm test -- --watch" 20 | }, 21 | "homepage": "https://github.com/pastak/scrapbox-converter/tree/master/packages/html2sb#readme", 22 | "repository": { 23 | "type": "git", 24 | "url": "ssh://git@github.com/pastak/scrapbox-converter.git" 25 | }, 26 | "author": "pastak ", 27 | "license": "MIT", 28 | "dependencies": { 29 | "@types/node": "20.14.8", 30 | "commander": "^5.1.0", 31 | "enex2sb": "workspace:*", 32 | "html2sb-compiler": "workspace:*" 33 | }, 34 | "devDependencies": { 35 | "@biomejs/biome": "^1.9.4", 36 | "typescript": "5.7.3", 37 | "vitest": "^3.0.5" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /packages/md2sb/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "md2sb", 3 | "version": "6.0.3", 4 | "description": "Encode Markdown to Scrapbox.io style text", 5 | "main": "dist/index.js", 6 | "bin": { 7 | "md2sb": "dist/command/index.js" 8 | }, 9 | "files": [ 10 | "dist" 11 | ], 12 | "homepage": "https://github.com/pastak/scrapbox-converter/tree/master/packages/md2sb#readme", 13 | "repository": { 14 | "type": "git", 15 | "url": "ssh://git@github.com/pastak/scrapbox-converter.git" 16 | }, 17 | "author": "pastak ", 18 | "license": "MIT", 19 | "scripts": { 20 | "build": "tsc --outDir dist --project .", 21 | "watch": "pnpm run build -- --watch", 22 | "test": "vitest run", 23 | "lint": "biome check ./src ./test", 24 | "lint:fix": "biome check --write ./src ./test", 25 | "lint:ci": "biome ci ./src ./test", 26 | "test:watch": "pnpm test -- --watch" 27 | }, 28 | "dependencies": { 29 | "@types/node": "20.14.8", 30 | "commander": "^5.1.0", 31 | "enex2sb": "workspace:*", 32 | "remark": "^13.0.0", 33 | "remark-gfm": "^1.0.0" 34 | }, 35 | "devDependencies": { 36 | "@biomejs/biome": "^1.9.4", 37 | "@types/unist": "^2.0.11", 38 | "typescript": "5.7.3", 39 | "vitest": "^3.0.5" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /packages/enex2sb/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "enex2sb", 3 | "version": "7.0.0", 4 | "description": "Encode Evernote's XML Export Format to Scrapbox.io style text", 5 | "main": "dist/index.js", 6 | "files": [ 7 | "dist" 8 | ], 9 | "bin": { 10 | "enex2sb": "dist/command/index.js" 11 | }, 12 | "scripts": { 13 | "build": "tsc --outDir dist --project .", 14 | "watch": "pnpm run build -- --watch", 15 | "lint": "biome check ./src ./test", 16 | "lint:fix": "biome check --write ./src ./test", 17 | "lint:ci": "biome ci ./src ./test", 18 | "test": "vitest run", 19 | "test:watch": "yarn test -- --watch" 20 | }, 21 | "homepage": "https://github.com/pastak/scrapbox-converter/tree/master/packages/enex2sb#readme", 22 | "repository": { 23 | "type": "git", 24 | "url": "ssh://git@github.com/pastak/scrapbox-converter.git" 25 | }, 26 | "author": "pastak ", 27 | "license": "MIT", 28 | "dependencies": { 29 | "commander": "^5.1.0", 30 | "gyazo-api": "^0.3.1", 31 | "gyazo-browser-upload": "^1.0.0", 32 | "html2sb-compiler": "workspace:*", 33 | "into-stream": "^3.1.0" 34 | }, 35 | "devDependencies": { 36 | "@biomejs/biome": "^1.9.4", 37 | "@types/node": "20.14.8", 38 | "typescript": "5.7.3", 39 | "vitest": "^3.0.5" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/table-in-div.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": null, 3 | "children": [ 4 | { 5 | "type": "div", 6 | "children": [ 7 | { 8 | "type": "table", 9 | "children": [ 10 | { 11 | "type": "tr", 12 | "children": [ 13 | { 14 | "type": "td", 15 | "children": [ 16 | { 17 | "type": "text", 18 | "text": "whos" 19 | } 20 | ] 21 | }, 22 | { 23 | "type": "td", 24 | "children": [ 25 | { 26 | "type": "text", 27 | "italic": true, 28 | "text": "house" 29 | } 30 | ] 31 | }, 32 | { 33 | "type": "td", 34 | "children": [ 35 | { 36 | "type": "text", 37 | "underline": true, 38 | "text": "runs" 39 | } 40 | ] 41 | } 42 | ] 43 | } 44 | ] 45 | } 46 | ] 47 | } 48 | ] 49 | } 50 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/formatting.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": null, 3 | "children": [ 4 | { 5 | "type": "div", 6 | "children": [ 7 | { 8 | "type": "text", 9 | "text": "super script" 10 | }, 11 | { 12 | "type": "text", 13 | "text": "works as well as" 14 | }, 15 | { 16 | "type": "text", 17 | "text": "subscript" 18 | }, 19 | { 20 | "type": "text", 21 | "text": "and" 22 | } 23 | ] 24 | }, 25 | { 26 | "type": "div", 27 | "children": [ 28 | { 29 | "type": "br", 30 | "force": true 31 | } 32 | ] 33 | }, 34 | { 35 | "type": "div", 36 | "children": [ 37 | { 38 | "type": "text", 39 | "text": "how about a little" 40 | }, 41 | { 42 | "type": "text", 43 | "text": "font change or" 44 | }, 45 | { 46 | "type": "text", 47 | "enlarge": 1, 48 | "bold": true, 49 | "text": "size change?" 50 | }, 51 | { 52 | "type": "text", 53 | "text": "colors can" 54 | }, 55 | { 56 | "type": "text", 57 | "text": "be changed as well" 58 | } 59 | ] 60 | } 61 | ] 62 | } 63 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/code.html: -------------------------------------------------------------------------------- 1 |
    function myfunction (a, b) {
      let x = a * 1.08
      let y = b * 2
      return x * y
    }
    2 |
    Hello
    3 | Holla
    function myfunction (a, b) {
      let x = a * 1.08
      let y = b * 2
      return x * y
    }
    4 |
    muxa
    5 |
     6 | function foo () {}
     7 | 
    8 | 9 | function bar () {} 10 | 11 | 12 | warn $user->name; 13 | 14 |
    15 |     user.name
    16 | 
    -------------------------------------------------------------------------------- /packages/enex2sb/src/command/command.ts: -------------------------------------------------------------------------------- 1 | import * as fs from "node:fs"; 2 | import * as path from "node:path"; 3 | import { program } from "commander"; 4 | import enex2sb from "./../node"; 5 | const settings = require("../../package.json"); // eslint-disable-line @typescript-eslint/no-var-requires 6 | 7 | let stdin = ""; 8 | 9 | program 10 | .version(settings.version) 11 | .description((settings as any).description) 12 | .usage("\n\tenex2sb [file] \n\tcat hoge.enex | enex2sb") 13 | .arguments("[file]") 14 | .action(async (file) => { 15 | if (!process.env.GYAZO_ACCESS_TOKEN) 16 | return console.error( 17 | "You should set env-value GYAZO_ACCESS_TOKEN to your Gyazo access token get from https://gyazo.com/oauth/applications", 18 | ); 19 | const pages = await enex2sb(fs.readFileSync(path.resolve(file))); 20 | console.log(JSON.stringify({ pages }, null, 2)); 21 | }); 22 | 23 | if (process.stdin.isTTY) { 24 | program.parse(process.argv); 25 | } else { 26 | process.stdin.on("readable", () => { 27 | const chunk = process.stdin.read(); 28 | if (chunk !== null) { 29 | stdin += chunk; 30 | } 31 | }); 32 | process.stdin.on("end", async () => { 33 | if (!process.env.GYAZO_ACCESS_TOKEN) 34 | return console.error( 35 | "You should set env-value GYAZO_ACCESS_TOKEN to your Gyazo access token get from https://gyazo.com/oauth/applications", 36 | ); 37 | console.log(JSON.stringify({ pages: await enex2sb(stdin) }, null, 2)); 38 | }); 39 | } 40 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/text-styles.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": null, 3 | "children": [ 4 | { 5 | "type": "text", 6 | "bold": true, 7 | "italic": true, 8 | "text": "text" 9 | }, 10 | { 11 | "type": "text", 12 | "italic": true, 13 | "bold": true, 14 | "text": "text" 15 | }, 16 | { 17 | "type": "text", 18 | "children": [ 19 | { 20 | "type": "text", 21 | "bold": true, 22 | "text": "a text" 23 | }, 24 | { 25 | "type": "text", 26 | "text": "not really but" 27 | }, 28 | { 29 | "type": "text", 30 | "bold": true, 31 | "text": "b text" 32 | } 33 | ], 34 | "italic": true 35 | }, 36 | { 37 | "type": "br", 38 | "force": true 39 | }, 40 | { 41 | "type": "text", 42 | "children": [ 43 | { 44 | "type": "text", 45 | "strike": true, 46 | "text": "Strikethrough text" 47 | }, 48 | { 49 | "type": "text", 50 | "text": "and" 51 | } 52 | ], 53 | "bold": true 54 | }, 55 | { 56 | "type": "br", 57 | "force": true 58 | }, 59 | { 60 | "type": "text", 61 | "italic": true, 62 | "underline": true, 63 | "text": "Italic underline text too!" 64 | }, 65 | { 66 | "type": "text", 67 | "text": "some unknown tag" 68 | } 69 | ] 70 | } 71 | -------------------------------------------------------------------------------- /packages/enex2sb/test/fixtures/multiple.enex: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ノート1 5 | 6 |
    本文1
    7 | ]]>
    20170415T151938Z20170415T151946Zkadoyaudesktop.mac0
    8 | ノート2 9 | 10 |
    本文2
    11 | ]]>
    20170415T151946Z20170415T151952Zkadoyaudesktop.mac0
    12 | ノート3 13 | 14 |
    本文3
    15 | ]]>
    20170415T151951Z20170415T152027Zkadoyaudesktop.mac0
    16 |
    17 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/evernote-multipage.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ノート1 5 | 6 |
    本文1
    7 | ]]>
    20170415T151938Z20170415T151946Zkadoyaudesktop.mac0
    8 | ノート2 9 | 10 |
    本文2
    11 | ]]>
    20170415T151946Z20170415T151952Zkadoyaudesktop.mac0
    12 | ノート3 13 | 14 |
    本文3
    15 | ]]>
    20170415T151951Z20170415T152027Zkadoyaudesktop.mac0
    16 |
    17 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/blocks.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": null, 3 | "children": [ 4 | { 5 | "type": "div", 6 | "children": [ 7 | { 8 | "type": "text", 9 | "text": "And some more text..." 10 | } 11 | ] 12 | }, 13 | { 14 | "type": "div", 15 | "children": [ 16 | { 17 | "type": "text", 18 | "text": "Lets align it right" 19 | } 20 | ] 21 | }, 22 | { 23 | "type": "div", 24 | "children": [ 25 | { 26 | "type": "text", 27 | "text": "and align it center" 28 | } 29 | ] 30 | }, 31 | { 32 | "type": "div", 33 | "children": [ 34 | { 35 | "type": "br", 36 | "force": true 37 | } 38 | ] 39 | }, 40 | { 41 | "type": "div", 42 | "children": [ 43 | { 44 | "type": "text", 45 | "text": "Indent a little" 46 | } 47 | ] 48 | }, 49 | { 50 | "type": "text", 51 | "children": [ 52 | { 53 | "type": "div", 54 | "children": [ 55 | { 56 | "type": "text", 57 | "text": "A little bit of this, a little bit of that, a little bit of everything lets me" 58 | } 59 | ] 60 | } 61 | ], 62 | "blockquote": 1 63 | }, 64 | { 65 | "type": "text", 66 | "children": [ 67 | { 68 | "type": "div", 69 | "children": [ 70 | { 71 | "type": "text", 72 | "text": "Indent a little further" 73 | } 74 | ] 75 | } 76 | ], 77 | "blockquote": 2 78 | } 79 | ] 80 | } 81 | -------------------------------------------------------------------------------- /packages/enex2sb/src/main.ts: -------------------------------------------------------------------------------- 1 | import { guessTitle, parse, toScrapbox } from "html2sb-compiler"; 2 | 3 | export default async ( 4 | uploadImage, 5 | input, 6 | options?: any, 7 | ): Promise< 8 | { 9 | title: string; 10 | lines: string[]; 11 | }[] 12 | > => { 13 | let xmlString = input; 14 | if (typeof input === "object") { 15 | if (input instanceof Buffer) { 16 | xmlString = input.toString(); 17 | } else if (typeof input !== "string") { 18 | throw new Error("It allows string or buffer"); 19 | } 20 | } 21 | 22 | const allNotes = parse(xmlString, { evernote: true }); 23 | return await Promise.all( 24 | allNotes.map(async (noteTokens) => { 25 | if (noteTokens.resources) { 26 | await Promise.all( 27 | Object.keys(noteTokens.resources) 28 | .map(async (resourceKey) => { 29 | const resource = noteTokens.resources[resourceKey]; 30 | const mimeType = resource.mime; 31 | if (/^image\/.*/.test(mimeType)) { 32 | const file = new Buffer(resource.data, "base64"); 33 | const res = await uploadImage(file, options); 34 | noteTokens.resources[resourceKey] = { 35 | type: "img", 36 | href: resource.href, 37 | src: res.data.permalink_url, 38 | }; 39 | } 40 | }) 41 | .filter(Boolean), 42 | ); 43 | } 44 | const sb: { 45 | title: string; 46 | lines: string[]; 47 | } = toScrapbox(noteTokens); 48 | sb.title = guessTitle( 49 | noteTokens, 50 | sb, 51 | (_pageTokens, foundTitle, template) => { 52 | const named = "Untitled"; 53 | return foundTitle || template(named) || named; 54 | }, 55 | ); 56 | return sb; 57 | }), 58 | ); 59 | }; 60 | -------------------------------------------------------------------------------- /packages/scrapbox-converter/src/libs/findAndLoadFiles.ts: -------------------------------------------------------------------------------- 1 | import * as fs from "node:fs"; 2 | import * as path from "node:path"; 3 | import loadEnexFile from "./loader/enex"; 4 | import loadHtmlFile from "./loader/html"; 5 | import loadMdFile from "./loader/md"; 6 | 7 | const findAndLoadFiles = async (files, basePath = "./") => 8 | await Promise.all( 9 | (Array.isArray(files) ? files : [files]).map(async (file) => { 10 | const fullPath = path.resolve(basePath, file); 11 | const stats = fs.lstatSync(fullPath); 12 | const isFile = stats.isFile(); 13 | const isDir = stats.isDirectory(); 14 | let title = ""; 15 | let lines = []; 16 | if (isFile) { 17 | const ext = path.extname(fullPath); 18 | if (/\.(?:markdown|md)/.test(ext)) { 19 | title = path.basename(fullPath, ext); 20 | const scrapboxStyleText = await loadMdFile(fullPath); 21 | lines = scrapboxStyleText.split("\n"); 22 | lines.unshift(title); 23 | } else if (/\.(?:html|htm)/.test(ext)) { 24 | const res = await loadHtmlFile(fullPath); 25 | title = res.title || path.basename(fullPath, ext); 26 | lines = res.lines; 27 | lines.unshift(title); 28 | } else if (/\.(?:enex)/.test(ext)) { 29 | try { 30 | return (await loadEnexFile(fullPath)).map((res) => { 31 | title = res.title || path.basename(fullPath, ext); 32 | lines = res.lines; 33 | lines.unshift(title); 34 | return { title, lines }; 35 | }); 36 | } catch (e) { 37 | console.log(e.message); 38 | process.exit(1); 39 | } 40 | } else { 41 | return; 42 | } 43 | return { title, lines }; 44 | // biome-ignore lint/style/noUselessElse: todo 45 | } else if (isDir) { 46 | return findAndLoadFiles(fs.readdirSync(fullPath), fullPath); 47 | } 48 | }), 49 | ); 50 | 51 | export default findAndLoadFiles; 52 | -------------------------------------------------------------------------------- /packages/html2sb/test/fixtures/html/evernote/test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | test
    This is text.

    bold

    Italic

    Underline

    red

    hightlighted

    font size large




    • list item
    • level1
      • level2
          • level4
    1. ordered list
    2. second
      1. level2
      • non ordered

    todo
    done





    1x11x2
    2x1
    2x2


    4 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/list.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": null, 3 | "children": [ 4 | { 5 | "type": "list", 6 | "variant": "ol", 7 | "children": [ 8 | { 9 | "type": "text", 10 | "text": "France" 11 | }, 12 | { 13 | "type": "text", 14 | "text": "Italy" 15 | }, 16 | { 17 | "type": "text", 18 | "text": "Norway" 19 | } 20 | ] 21 | }, 22 | { 23 | "type": "list", 24 | "variant": "ul", 25 | "children": [ 26 | { 27 | "type": "text", 28 | "text": "Tiles" 29 | }, 30 | { 31 | "type": "text", 32 | "text": "Bottles" 33 | }, 34 | { 35 | "type": "text", 36 | "text": "Sofas" 37 | } 38 | ] 39 | }, 40 | { 41 | "type": "list", 42 | "variant": "ul", 43 | "children": [ 44 | { 45 | "type": "check", 46 | "checked": false, 47 | "children": [ 48 | { 49 | "type": "text", 50 | "text": "Item 1" 51 | } 52 | ] 53 | }, 54 | { 55 | "type": "check", 56 | "checked": false, 57 | "children": [ 58 | { 59 | "type": "text", 60 | "text": "Item 2" 61 | } 62 | ] 63 | }, 64 | { 65 | "type": "check", 66 | "checked": true, 67 | "children": [ 68 | { 69 | "type": "text", 70 | "text": "Item 3 (selected)" 71 | } 72 | ] 73 | }, 74 | { 75 | "type": "check", 76 | "checked": false, 77 | "children": [ 78 | { 79 | "type": "text", 80 | "text": "Item 4" 81 | } 82 | ] 83 | } 84 | ] 85 | }, 86 | { 87 | "type": "list", 88 | "variant": "ul", 89 | "children": [ 90 | { 91 | "type": "img", 92 | "src": "https://test.com/image1.png" 93 | }, 94 | { 95 | "type": "img", 96 | "src": "https://test.com/image2.png" 97 | } 98 | ] 99 | } 100 | ] 101 | } 102 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | ## 7.0.0 4 | 5 | ### Major Changes 6 | 7 | - 65f02c8: Change default table title `_` to ` ` 8 | 9 | ## 6.0.2 10 | 11 | ### Patch Changes 12 | 13 | - 1780f6a: fix linting with CI 14 | 15 | ## 6.0.1 16 | 17 | ### Patch Changes 18 | 19 | - 7ca3583: Organize `package.json`s 20 | 21 | ## 6.0.0 22 | 23 | ### Major Changes 24 | 25 | - 8106162: Drop support node 16 and 18 26 | 27 | ### Minor Changes 28 | 29 | - 8106162: refresh or upgrade dependencies 30 | 31 | All notable changes to this project will be documented in this file. 32 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 33 | 34 | ## [5.1.2](https://github.com/pastak/scrapbox-converter/compare/v5.1.1...v5.1.2) (2021-06-14) 35 | 36 | **Note:** Version bump only for package html2sb-compiler 37 | 38 | ## [5.1.1](https://github.com/pastak/scrapbox-converter/compare/v5.1.0...v5.1.1) (2020-11-26) 39 | 40 | **Note:** Version bump only for package html2sb-compiler 41 | 42 | # [5.1.0](https://github.com/pastak/scrapbox-converter/compare/v5.0.0...v5.1.0) (2020-11-26) 43 | 44 | **Note:** Version bump only for package html2sb-compiler 45 | 46 | # [5.0.0](https://github.com/pastak/scrapbox-converter/compare/v4.0.5...v5.0.0) (2020-05-11) 47 | 48 | ### Features 49 | 50 | - upgrade ava and install ava/typescript ([94e21d5](https://github.com/pastak/scrapbox-converter/commit/94e21d57bbbee9bf907769fed5599001b0d9fac2)) 51 | 52 | - introduce typescript ([ecceaba](https://github.com/pastak/scrapbox-converter/commit/ecceabac3882acfbcb3ce4c6861954d5e2a93d95)) 53 | - uninstall babel and rm .babelrc ([d78699d](https://github.com/pastak/scrapbox-converter/commit/d78699d1a0e0bc4f3dda44b3d00902cf7fa9e6b5)) 54 | 55 | ### BREAKING CHANGES 56 | 57 | - Full replacement with TypeScript 58 | - no implements with babel 59 | 60 | ## [4.0.5](https://github.com/pastak/scrapbox-converter/compare/v4.0.4...v4.0.5) (2020-03-13) 61 | 62 | **Note:** Version bump only for package html2sb-compiler 63 | 64 | ## [4.0.4](https://github.com/pastak/scrapbox-converter/compare/v4.0.3...v4.0.4) (2020-03-11) 65 | 66 | **Note:** Version bump only for package html2sb-compiler 67 | 68 | ## [4.0.3](https://github.com/pastak/scrapbox-converter/compare/v4.0.2...v4.0.3) (2020-03-11) 69 | 70 | **Note:** Version bump only for package html2sb-compiler 71 | 72 | ## [4.0.2](https://github.com/pastak/scrapbox-converter/compare/v4.0.1...v4.0.2) (2020-01-20) 73 | 74 | **Note:** Version bump only for package html2sb-compiler 75 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/complex.txt: -------------------------------------------------------------------------------- 1 | This is a fancy titled 2 | Don't you think? 3 | 4 | 5 | Regular Text 6 | 7 | Header 5 8 | 9 | [** Header 4] 10 | 11 | [*** Header 3] 12 | 13 | [**** Header 2] 14 | 15 | [***** Header 1] 16 | 17 | [****** Header 0] 18 | 19 | 20 | I think this [* bold] text is better than the [/ italic] one, but certainly its better than the [_ underlined] or [- striked-through] one. 21 | 22 | 23 | [/ This is italic text with some [* bold] text in-between, maybe [* twice] .] 24 | 25 | 26 | [* This is bold text with some [/ italic] text in-between, maybe [/ twice] .] 27 | 28 | 29 | [_ This is underline text with some [* bold,] some [/ italic] and some [- strike-through] text.] 30 | 31 | 32 | [*/-_ Super fancy text 1 order: underline, italic, strike-through, bold] 33 | 34 | 35 | [*/-_ Super fancy text 2 order: bold, strike through, italic, underline] 36 | 37 | 38 | This is some [_ marked text] to remember! 39 | 40 | 41 | A block of code 42 | 43 | 44 | code:_ 45 | function myfunction (a, b) { 46 | let x = a * 1.08 47 | let y = b * 2 48 | return x * y 49 | } 50 | 51 | 52 | could be as cool as a checkbox: 53 | 54 | 55 | ⬜ Item 1 56 | ⬜ Item 2 57 | ✅ Item 3 (selected) 58 | ⬜ Item 4 59 | 60 | 61 | an ordered list 62 | 63 | 64 | 1. France 65 | 2. Italy 66 | 3. Norway 67 | 68 | 69 | or an unordered list 70 | 71 | cheese 72 | 1. Camembert 73 | 2. Gauda 74 | 3. Blue Cheese 75 | Roquefort 76 | Gorgonzola 77 | Blue Stilton 78 | 4. Emmental 79 | wine 80 | barbeque 81 | 82 | 83 | Links should work as well, to [http://google.com google] for example. 84 | 85 | 86 | Two images, attached: 87 | 88 | 89 | 90 | [data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA] 91 | 92 | [data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAs] 93 | 94 | 95 | And a table 96 | 97 | 98 | 99 | table: 100 | whos [/ house] [_ runs] 101 | [* house] I [http://test.com said] 102 | 103 | whos house? 104 | 105 | 106 | Some horizontal spacer 107 | 108 | 109 | 110 | [/icons/hr.icon] 111 | 112 | 113 | And some more text... 114 | 115 | Lets align it right 116 | 117 | and align it center 118 | 119 | 120 | Indent a little 121 | 122 | > A little bit of this, a little bit of that, a little bit of everything lets me 123 | 124 | >> Indent a little further 125 | 126 | 127 | super script works ass well as subscript and 128 | 129 | 130 | how about a little font change or [** size change?] colors can be changed too 131 | 132 | 133 | #fancy #house #in #inaka 134 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/header.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": null, 3 | "children": [ 4 | { 5 | "type": "div", 6 | "children": [ 7 | { 8 | "type": "text", 9 | "text": "Regular Text" 10 | } 11 | ] 12 | }, 13 | { 14 | "type": "div", 15 | "children": [ 16 | { 17 | "type": "text", 18 | "text": "Header 5" 19 | } 20 | ] 21 | }, 22 | { 23 | "type": "div", 24 | "children": [ 25 | { 26 | "type": "text", 27 | "enlarge": 1, 28 | "bold": true, 29 | "text": "Header 4" 30 | } 31 | ] 32 | }, 33 | { 34 | "type": "div", 35 | "children": [ 36 | { 37 | "type": "text", 38 | "enlarge": 2, 39 | "bold": true, 40 | "text": "Header 3" 41 | } 42 | ] 43 | }, 44 | { 45 | "type": "div", 46 | "children": [ 47 | { 48 | "type": "text", 49 | "enlarge": 3, 50 | "bold": true, 51 | "text": "Header 2" 52 | } 53 | ] 54 | }, 55 | { 56 | "type": "div", 57 | "children": [ 58 | { 59 | "type": "text", 60 | "enlarge": 4, 61 | "bold": true, 62 | "text": "Header 1" 63 | } 64 | ] 65 | }, 66 | { 67 | "type": "div", 68 | "children": [ 69 | { 70 | "type": "text", 71 | "enlarge": 5, 72 | "bold": true, 73 | "text": "Header 0" 74 | } 75 | ] 76 | }, 77 | { 78 | "type": "br" 79 | }, 80 | { 81 | "type": "text", 82 | "bold": true, 83 | "enlarge": 5, 84 | "text": "Header 0" 85 | }, 86 | { 87 | "type": "br" 88 | }, 89 | { 90 | "type": "text", 91 | "bold": true, 92 | "enlarge": 4, 93 | "text": "Header 1" 94 | }, 95 | { 96 | "type": "br" 97 | }, 98 | { 99 | "type": "text", 100 | "bold": true, 101 | "enlarge": 3, 102 | "text": "Header 2" 103 | }, 104 | { 105 | "type": "br" 106 | }, 107 | { 108 | "type": "text", 109 | "bold": true, 110 | "enlarge": 2, 111 | "text": "Header 3" 112 | }, 113 | { 114 | "type": "br" 115 | }, 116 | { 117 | "type": "text", 118 | "bold": true, 119 | "enlarge": 1, 120 | "text": "Header 4" 121 | } 122 | ] 123 | } 124 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## [5.1.2](https://github.com/pastak/scrapbox-converter/compare/v5.1.1...v5.1.2) (2021-06-14) 7 | 8 | **Note:** Version bump only for package scrapbox-converter-monorepo 9 | 10 | 11 | 12 | 13 | 14 | ## [5.1.1](https://github.com/pastak/scrapbox-converter/compare/v5.1.0...v5.1.1) (2020-11-26) 15 | 16 | **Note:** Version bump only for package scrapbox-converter-monorepo 17 | 18 | This version is same as 5.1.0 because of author's fault 19 | 20 | 21 | 22 | # [5.1.0](https://github.com/pastak/scrapbox-converter/compare/v5.0.0...v5.1.0) (2020-11-26) 23 | 24 | 25 | ### Features 26 | 27 | * bump remark v13 and install remark-gfm ([66dd5cd](https://github.com/pastak/scrapbox-converter/commit/66dd5cdd4b743dcdc733b047ed9caf9d43f1904d)) 28 | * nested list ([53245db](https://github.com/pastak/scrapbox-converter/commit/53245db08792871444ababab290d533eff91a611)) 29 | 30 | 31 | 32 | 33 | 34 | # [5.0.0](https://github.com/pastak/scrapbox-converter/compare/v4.0.5...v5.0.0) (2020-05-11) 35 | 36 | 37 | ### Features 38 | 39 | * bump remark ([787ca94](https://github.com/pastak/scrapbox-converter/commit/787ca94ac0a46d8ae4eca9f083e80c95b43ad5ec)) 40 | * upgrade ava and install ava/typescript ([94e21d5](https://github.com/pastak/scrapbox-converter/commit/94e21d57bbbee9bf907769fed5599001b0d9fac2)) 41 | 42 | 43 | * introduce typescript ([ecceaba](https://github.com/pastak/scrapbox-converter/commit/ecceabac3882acfbcb3ce4c6861954d5e2a93d95)) 44 | * uninstall babel and rm .babelrc ([d78699d](https://github.com/pastak/scrapbox-converter/commit/d78699d1a0e0bc4f3dda44b3d00902cf7fa9e6b5)) 45 | 46 | 47 | ### BREAKING CHANGES 48 | 49 | * Full replacement with TypeScript 50 | * no implements with babel 51 | 52 | 53 | 54 | 55 | 56 | ## [4.0.5](https://github.com/pastak/scrapbox-converter/compare/v4.0.4...v4.0.5) (2020-03-13) 57 | 58 | **Note:** Version bump only for package scrapbox-converter-monorepo 59 | 60 | 61 | 62 | 63 | 64 | ## [4.0.4](https://github.com/pastak/scrapbox-converter/compare/v4.0.3...v4.0.4) (2020-03-11) 65 | 66 | **Note:** Version bump only for package scrapbox-converter-monorepo 67 | 68 | 69 | 70 | 71 | 72 | ## [4.0.3](https://github.com/pastak/scrapbox-converter/compare/v4.0.2...v4.0.3) (2020-03-11) 73 | 74 | **Note:** Version bump only for package scrapbox-converter-monorepo 75 | 76 | 77 | 78 | 79 | 80 | ## [4.0.2](https://github.com/pastak/scrapbox-converter/compare/v4.0.1...v4.0.2) (2020-01-20) 81 | 82 | **Note:** Version bump only for package scrapbox-converter-monorepo 83 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/list-in-list.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": null, 3 | "children": [ 4 | { 5 | "type": "list", 6 | "variant": "ol", 7 | "children": [ 8 | { 9 | "type": "text", 10 | "children": [ 11 | { 12 | "type": "text", 13 | "bold": true, 14 | "text": "France" 15 | }, 16 | { 17 | "type": "br", 18 | "force": true 19 | }, 20 | { 21 | "type": "list", 22 | "variant": "ul", 23 | "children": [ 24 | { 25 | "type": "text", 26 | "text": "Baguette" 27 | }, 28 | { 29 | "type": "text", 30 | "text": "Camenbert" 31 | }, 32 | { 33 | "type": "text", 34 | "text": "Ganache" 35 | } 36 | ] 37 | } 38 | ] 39 | }, 40 | { 41 | "type": "text", 42 | "children": [ 43 | { 44 | "type": "text", 45 | "text": "Italy" 46 | }, 47 | { 48 | "type": "list", 49 | "variant": "ul", 50 | "children": [ 51 | { 52 | "type": "text", 53 | "text": "Parmegiano" 54 | }, 55 | { 56 | "type": "text", 57 | "text": "Prociutto" 58 | }, 59 | { 60 | "type": "text", 61 | "text": "Ricotta" 62 | } 63 | ] 64 | } 65 | ] 66 | }, 67 | { 68 | "type": "text", 69 | "children": [ 70 | { 71 | "type": "text", 72 | "text": "Norway" 73 | }, 74 | { 75 | "type": "list", 76 | "variant": "ul", 77 | "children": [ 78 | { 79 | "type": "text", 80 | "text": "Nothing" 81 | }, 82 | { 83 | "type": "text", 84 | "text": "comes" 85 | }, 86 | { 87 | "type": "text", 88 | "text": "to" 89 | }, 90 | { 91 | "type": "text", 92 | "text": "mind" 93 | } 94 | ] 95 | } 96 | ] 97 | } 98 | ] 99 | } 100 | ] 101 | } 102 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures.test.ts: -------------------------------------------------------------------------------- 1 | import fs from "node:fs"; 2 | import path from "node:path"; 3 | import { expect, test } from "vitest"; 4 | import { guessTitle, parse, toScrapbox } from "../src"; 5 | 6 | const updateToken = !!process.env.UPDATE_TOKEN; 7 | 8 | function readFixture(file) { 9 | return fs.readFileSync(path.join(__dirname, "fixtures", file), "utf8"); 10 | } 11 | 12 | function testFixture(file) { 13 | const allPages = parse(readFixture(file + ".html"), { 14 | evernote: true, 15 | }); 16 | if (!allPages?.length) return; 17 | for (let index = 0; index < allPages.length; index++) { 18 | const pageFile = allPages.length === 1 ? file : file + "-" + (index + 1); 19 | const pageTokens = allPages[index]; 20 | let expectedTokens; 21 | 22 | try { 23 | expectedTokens = JSON.parse(readFixture(pageFile + ".json")); 24 | } catch (e) { 25 | throw new Error( 26 | pageFile + 27 | ".json is not well formatted:\n" + 28 | (e.stack || e.message || e), 29 | ); 30 | } 31 | const expectedOutput = readFixture(pageFile + ".txt"); 32 | const sb = toScrapbox(pageTokens); 33 | if (updateToken) 34 | fs.writeFileSync( 35 | path.join(__dirname, "fixtures", pageFile + ".json"), 36 | JSON.stringify(pageTokens, null, 2), 37 | ); 38 | if (process.env.SHOW_TOKEN) 39 | console.log(JSON.stringify(pageTokens, null, 2)); 40 | if (!process.env.IGNORE_TOKEN_TEST && !updateToken) 41 | expect(pageTokens, file + "#tokens").toEqual(expectedTokens); 42 | sb.title = guessTitle( 43 | pageTokens, 44 | sb, 45 | (_pageTokens, foundTitle, template) => { 46 | const named = "Untitled"; 47 | return foundTitle || template(named) || named; 48 | }, 49 | ); 50 | expect( 51 | (sb.title ? sb.title + "\n" : "") + sb.lines.join("\n") + "\n", 52 | file + "#output", 53 | ).toBe(expectedOutput); 54 | } 55 | } 56 | 57 | [ 58 | "formatting", 59 | "evernote", 60 | "evernote-multipage", 61 | "blocks", 62 | "code", 63 | "list", 64 | "list-in-list", 65 | "list-of-links", 66 | "header", 67 | "hr", 68 | "images", 69 | "table", 70 | "table-in-div", 71 | "complex", 72 | "links", 73 | "links-empty", 74 | "text-styles", 75 | "list-skipped-inheritance", 76 | "list-wrong-inheritance", 77 | "simple-paragraph", 78 | "entities", 79 | "complex-paragraph", 80 | "styled-code", 81 | "styled-code", 82 | "invalid-list-in-list", 83 | "invalid-text-in-table", 84 | ] 85 | .filter((pageFile) => { 86 | if (process.env.TEST_ONLY_RUN) { 87 | return process.env.TEST_ONLY_RUN === pageFile; 88 | } 89 | return true; 90 | }) 91 | .forEach((type) => { 92 | test("convert " + type, () => testFixture(type)); 93 | }); 94 | -------------------------------------------------------------------------------- /packages/enex2sb/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | ## 7.0.0 4 | 5 | ### Major Changes 6 | 7 | - 65f02c8: Change default table title `_` to ` ` 8 | 9 | ### Patch Changes 10 | 11 | - Updated dependencies [65f02c8] 12 | - html2sb-compiler@7.0.0 13 | 14 | ## 6.0.2 15 | 16 | ### Patch Changes 17 | 18 | - 1780f6a: fix linting with CI 19 | - Updated dependencies [1780f6a] 20 | - html2sb-compiler@6.0.2 21 | 22 | ## 6.0.1 23 | 24 | ### Patch Changes 25 | 26 | - 7ca3583: Organize `package.json`s 27 | - Updated dependencies [7ca3583] 28 | - html2sb-compiler@6.0.1 29 | 30 | ## 6.0.0 31 | 32 | ### Major Changes 33 | 34 | - 8106162: Drop support node 16 and 18 35 | 36 | ### Minor Changes 37 | 38 | - 8106162: refresh or upgrade dependencies 39 | 40 | ### Patch Changes 41 | 42 | - Updated dependencies [8106162] 43 | - Updated dependencies [8106162] 44 | - html2sb-compiler@6.0.0 45 | 46 | All notable changes to this project will be documented in this file. 47 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 48 | 49 | ## [5.1.2](https://github.com/pastak/scrapbox-converter/compare/v5.1.1...v5.1.2) (2021-06-14) 50 | 51 | **Note:** Version bump only for package enex2sb 52 | 53 | ## [5.1.1](https://github.com/pastak/scrapbox-converter/compare/v5.1.0...v5.1.1) (2020-11-26) 54 | 55 | **Note:** Version bump only for package enex2sb 56 | 57 | # [5.1.0](https://github.com/pastak/scrapbox-converter/compare/v5.0.0...v5.1.0) (2020-11-26) 58 | 59 | **Note:** Version bump only for package enex2sb 60 | 61 | # [5.0.0](https://github.com/pastak/scrapbox-converter/compare/v4.0.5...v5.0.0) (2020-05-11) 62 | 63 | ### Features 64 | 65 | - upgrade ava and install ava/typescript ([94e21d5](https://github.com/pastak/scrapbox-converter/commit/94e21d57bbbee9bf907769fed5599001b0d9fac2)) 66 | 67 | - introduce typescript ([ecceaba](https://github.com/pastak/scrapbox-converter/commit/ecceabac3882acfbcb3ce4c6861954d5e2a93d95)) 68 | - uninstall babel and rm .babelrc ([d78699d](https://github.com/pastak/scrapbox-converter/commit/d78699d1a0e0bc4f3dda44b3d00902cf7fa9e6b5)) 69 | 70 | ### BREAKING CHANGES 71 | 72 | - Full replacement with TypeScript 73 | - no implements with babel 74 | 75 | ## [4.0.5](https://github.com/pastak/scrapbox-converter/compare/v4.0.4...v4.0.5) (2020-03-13) 76 | 77 | **Note:** Version bump only for package enex2sb 78 | 79 | ## [4.0.4](https://github.com/pastak/scrapbox-converter/compare/v4.0.3...v4.0.4) (2020-03-11) 80 | 81 | **Note:** Version bump only for package enex2sb 82 | 83 | ## [4.0.3](https://github.com/pastak/scrapbox-converter/compare/v4.0.2...v4.0.3) (2020-03-11) 84 | 85 | **Note:** Version bump only for package enex2sb 86 | 87 | ## [4.0.2](https://github.com/pastak/scrapbox-converter/compare/v4.0.1...v4.0.2) (2020-01-20) 88 | 89 | **Note:** Version bump only for package enex2sb 90 | -------------------------------------------------------------------------------- /packages/html2sb/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | ## 7.0.0 4 | 5 | ### Major Changes 6 | 7 | - 65f02c8: Change default table title `_` to ` ` 8 | 9 | ### Patch Changes 10 | 11 | - Updated dependencies [65f02c8] 12 | - enex2sb@7.0.0 13 | - html2sb-compiler@7.0.0 14 | 15 | ## 6.0.2 16 | 17 | ### Patch Changes 18 | 19 | - 1780f6a: fix linting with CI 20 | - Updated dependencies [1780f6a] 21 | - html2sb-compiler@6.0.2 22 | - enex2sb@6.0.2 23 | 24 | ## 6.0.1 25 | 26 | ### Patch Changes 27 | 28 | - 7ca3583: Organize `package.json`s 29 | - Updated dependencies [7ca3583] 30 | - html2sb-compiler@6.0.1 31 | - enex2sb@6.0.1 32 | 33 | ## 6.0.0 34 | 35 | ### Major Changes 36 | 37 | - 8106162: Drop support node 16 and 18 38 | 39 | ### Minor Changes 40 | 41 | - 8106162: refresh or upgrade dependencies 42 | 43 | ### Patch Changes 44 | 45 | - Updated dependencies [8106162] 46 | - Updated dependencies [8106162] 47 | - enex2sb@6.0.0 48 | - html2sb-compiler@6.0.0 49 | 50 | All notable changes to this project will be documented in this file. 51 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 52 | 53 | ## [5.1.2](https://github.com/pastak/scrapbox-converter/compare/v5.1.1...v5.1.2) (2021-06-14) 54 | 55 | **Note:** Version bump only for package html2sb 56 | 57 | ## [5.1.1](https://github.com/pastak/scrapbox-converter/compare/v5.1.0...v5.1.1) (2020-11-26) 58 | 59 | **Note:** Version bump only for package html2sb 60 | 61 | # [5.1.0](https://github.com/pastak/scrapbox-converter/compare/v5.0.0...v5.1.0) (2020-11-26) 62 | 63 | **Note:** Version bump only for package html2sb 64 | 65 | # [5.0.0](https://github.com/pastak/scrapbox-converter/compare/v4.0.5...v5.0.0) (2020-05-11) 66 | 67 | ### Features 68 | 69 | - upgrade ava and install ava/typescript ([94e21d5](https://github.com/pastak/scrapbox-converter/commit/94e21d57bbbee9bf907769fed5599001b0d9fac2)) 70 | 71 | - introduce typescript ([ecceaba](https://github.com/pastak/scrapbox-converter/commit/ecceabac3882acfbcb3ce4c6861954d5e2a93d95)) 72 | - uninstall babel and rm .babelrc ([d78699d](https://github.com/pastak/scrapbox-converter/commit/d78699d1a0e0bc4f3dda44b3d00902cf7fa9e6b5)) 73 | 74 | ### BREAKING CHANGES 75 | 76 | - Full replacement with TypeScript 77 | - no implements with babel 78 | 79 | ## [4.0.5](https://github.com/pastak/scrapbox-converter/compare/v4.0.4...v4.0.5) (2020-03-13) 80 | 81 | **Note:** Version bump only for package html2sb 82 | 83 | ## [4.0.4](https://github.com/pastak/scrapbox-converter/compare/v4.0.3...v4.0.4) (2020-03-11) 84 | 85 | **Note:** Version bump only for package html2sb 86 | 87 | ## [4.0.3](https://github.com/pastak/scrapbox-converter/compare/v4.0.2...v4.0.3) (2020-03-11) 88 | 89 | **Note:** Version bump only for package html2sb 90 | 91 | ## [4.0.2](https://github.com/pastak/scrapbox-converter/compare/v4.0.1...v4.0.2) (2020-01-20) 92 | 93 | **Note:** Version bump only for package html2sb 94 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/list-skipped-inheritance.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": null, 3 | "children": [ 4 | { 5 | "type": "list", 6 | "variant": "ul", 7 | "children": [ 8 | { 9 | "type": "text", 10 | "text": "list item" 11 | }, 12 | { 13 | "type": "text", 14 | "children": [ 15 | { 16 | "type": "text", 17 | "text": "level1" 18 | }, 19 | { 20 | "type": "list", 21 | "variant": "ul", 22 | "children": [ 23 | { 24 | "type": "text", 25 | "children": [ 26 | { 27 | "type": "text", 28 | "text": "level2" 29 | }, 30 | { 31 | "type": "list", 32 | "variant": "ul", 33 | "children": [ 34 | { 35 | "type": "text", 36 | "children": [ 37 | { 38 | "type": "list", 39 | "variant": "ul", 40 | "children": [ 41 | { 42 | "type": "text", 43 | "text": "level4" 44 | } 45 | ] 46 | } 47 | ] 48 | } 49 | ] 50 | } 51 | ] 52 | } 53 | ] 54 | } 55 | ] 56 | } 57 | ] 58 | }, 59 | { 60 | "type": "list", 61 | "variant": "ol", 62 | "children": [ 63 | { 64 | "type": "text", 65 | "text": "ordered list" 66 | }, 67 | { 68 | "type": "text", 69 | "children": [ 70 | { 71 | "type": "text", 72 | "text": "second" 73 | }, 74 | { 75 | "type": "list", 76 | "variant": "ol", 77 | "children": [ 78 | { 79 | "type": "text", 80 | "text": "level2" 81 | } 82 | ] 83 | } 84 | ] 85 | }, 86 | { 87 | "type": "text", 88 | "children": [ 89 | { 90 | "type": "list", 91 | "variant": "ul", 92 | "children": [ 93 | { 94 | "type": "text", 95 | "text": "non ordered" 96 | } 97 | ] 98 | } 99 | ] 100 | } 101 | ] 102 | } 103 | ] 104 | } 105 | -------------------------------------------------------------------------------- /packages/md2sb/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | ## 6.0.3 4 | 5 | ### Patch Changes 6 | 7 | - Updated dependencies [65f02c8] 8 | - enex2sb@7.0.0 9 | 10 | ## 6.0.2 11 | 12 | ### Patch Changes 13 | 14 | - 1780f6a: fix linting with CI 15 | - Updated dependencies [1780f6a] 16 | - enex2sb@6.0.2 17 | 18 | ## 6.0.1 19 | 20 | ### Patch Changes 21 | 22 | - 7ca3583: Organize `package.json`s 23 | - Updated dependencies [7ca3583] 24 | - enex2sb@6.0.1 25 | 26 | ## 6.0.0 27 | 28 | ### Major Changes 29 | 30 | - 8106162: Drop support node 16 and 18 31 | 32 | ### Minor Changes 33 | 34 | - 8106162: refresh or upgrade dependencies 35 | 36 | ### Patch Changes 37 | 38 | - Updated dependencies [8106162] 39 | - Updated dependencies [8106162] 40 | - enex2sb@6.0.0 41 | 42 | All notable changes to this project will be documented in this file. 43 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 44 | 45 | ## [5.1.2](https://github.com/pastak/scrapbox-converter/compare/v5.1.1...v5.1.2) (2021-06-14) 46 | 47 | **Note:** Version bump only for package md2sb 48 | 49 | ## [5.1.1](https://github.com/pastak/scrapbox-converter/compare/v5.1.0...v5.1.1) (2020-11-26) 50 | 51 | **Note:** Version bump only for package md2sb 52 | 53 | # [5.1.0](https://github.com/pastak/scrapbox-converter/compare/v5.0.0...v5.1.0) (2020-11-26) 54 | 55 | ### Features 56 | 57 | - bump remark v13 and install remark-gfm ([66dd5cd](https://github.com/pastak/scrapbox-converter/commit/66dd5cdd4b743dcdc733b047ed9caf9d43f1904d)) 58 | - nested list ([53245db](https://github.com/pastak/scrapbox-converter/commit/53245db08792871444ababab290d533eff91a611)) 59 | 60 | # [5.0.0](https://github.com/pastak/scrapbox-converter/compare/v4.0.5...v5.0.0) (2020-05-11) 61 | 62 | ### Features 63 | 64 | - bump remark ([787ca94](https://github.com/pastak/scrapbox-converter/commit/787ca94ac0a46d8ae4eca9f083e80c95b43ad5ec)) 65 | - upgrade ava and install ava/typescript ([94e21d5](https://github.com/pastak/scrapbox-converter/commit/94e21d57bbbee9bf907769fed5599001b0d9fac2)) 66 | 67 | - introduce typescript ([ecceaba](https://github.com/pastak/scrapbox-converter/commit/ecceabac3882acfbcb3ce4c6861954d5e2a93d95)) 68 | - uninstall babel and rm .babelrc ([d78699d](https://github.com/pastak/scrapbox-converter/commit/d78699d1a0e0bc4f3dda44b3d00902cf7fa9e6b5)) 69 | 70 | ### BREAKING CHANGES 71 | 72 | - Full replacement with TypeScript 73 | - no implements with babel 74 | 75 | ## [4.0.5](https://github.com/pastak/scrapbox-converter/compare/v4.0.4...v4.0.5) (2020-03-13) 76 | 77 | **Note:** Version bump only for package md2sb 78 | 79 | ## [4.0.4](https://github.com/pastak/scrapbox-converter/compare/v4.0.3...v4.0.4) (2020-03-11) 80 | 81 | **Note:** Version bump only for package md2sb 82 | 83 | ## [4.0.3](https://github.com/pastak/scrapbox-converter/compare/v4.0.2...v4.0.3) (2020-03-11) 84 | 85 | **Note:** Version bump only for package md2sb 86 | 87 | ## [4.0.2](https://github.com/pastak/scrapbox-converter/compare/v4.0.1...v4.0.2) (2020-01-20) 88 | 89 | **Note:** Version bump only for package md2sb 90 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/src/guessTitle.ts: -------------------------------------------------------------------------------- 1 | import { toSimpleText } from "./toScrapbox"; 2 | const CONTINUE = false; 3 | const ABORT = true; 4 | 5 | function iterateTokenRaw(iterator, level, token) { 6 | if (iterator(token, level) === ABORT) { 7 | return ABORT; 8 | } 9 | if (token.children) { 10 | return token.children.map((child) => 11 | iterateTokenRaw.bind(null, iterator, level + 1, child), 12 | ); 13 | } 14 | return CONTINUE; 15 | } 16 | 17 | function iterateToken(iterator, token) { 18 | let nextTasks = iterateTokenRaw(iterator, 0, token); 19 | if (nextTasks === ABORT) { 20 | return; 21 | } 22 | while (nextTasks && nextTasks.length > 0) { 23 | const taskToExecute = nextTasks.shift(); 24 | const moreTasks = taskToExecute(); 25 | if (moreTasks === ABORT) { 26 | return; 27 | } 28 | if (moreTasks) { 29 | nextTasks = nextTasks.concat(moreTasks); 30 | } 31 | } 32 | } 33 | 34 | function defaultTemplate(userTitle) { 35 | return String(userTitle); 36 | } 37 | 38 | function getTemplate(parent) { 39 | let bestEnlarged; 40 | let firstEntry; 41 | let onlyEntry = true; 42 | let base; 43 | while ( 44 | (parent.type === "div" || parent.type === undefined) && 45 | parent.children && 46 | parent.children.length === 1 47 | ) { 48 | parent = parent.children[0]; 49 | } 50 | iterateToken((token, level) => { 51 | if (level === 1 && onlyEntry) { 52 | base = firstEntry; 53 | } 54 | if (firstEntry === undefined) { 55 | firstEntry = token; 56 | } else { 57 | onlyEntry = false; 58 | } 59 | if (token.type === "text" && token.enlarge > 2) { 60 | if (!bestEnlarged || token.enlarge > bestEnlarged.enlarge) { 61 | bestEnlarged = token; 62 | } 63 | } 64 | return CONTINUE; 65 | }, parent); 66 | if (base) { 67 | if (base.type === "table") { 68 | return function tableTitle(userTitle) { 69 | return "Table (" + userTitle + ")"; 70 | }; 71 | } 72 | if (base.type === "list") { 73 | return function listTitle(userTitle) { 74 | if (base.children.length > 0 && base.children[0].children) { 75 | userTitle = 76 | base.children 77 | .map((token) => { 78 | const max = 15; 79 | let text = toSimpleText(token).replace(/[\t\n]/gi, ""); 80 | if (text.length > max) { 81 | text = text.substr(0, max - 3) + "..."; 82 | } 83 | return text; 84 | }) 85 | .join(", ") || "?"; 86 | } 87 | return "List (" + userTitle + ")"; 88 | }; 89 | } 90 | } 91 | if (bestEnlarged) { 92 | return function enlargedTitle(userTitle) { 93 | return String(bestEnlarged.text || userTitle); 94 | }; 95 | } 96 | return defaultTemplate; 97 | } 98 | 99 | export const guessTitle = (parsed, sb, evaluateTitle) => { 100 | if (typeof evaluateTitle === "function") { 101 | return evaluateTitle(parsed, sb.title, (userTitle) => { 102 | const template = getTemplate(parsed); 103 | return template(userTitle); 104 | }); 105 | } 106 | return sb.title || getTemplate(parsed)(""); 107 | }; 108 | -------------------------------------------------------------------------------- /packages/scrapbox-converter/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | ## 7.0.0 4 | 5 | ### Major Changes 6 | 7 | - 65f02c8: Change default table title `_` to ` ` 8 | 9 | ### Patch Changes 10 | 11 | - Updated dependencies [65f02c8] 12 | - enex2sb@7.0.0 13 | - html2sb-compiler@7.0.0 14 | - md2sb@6.0.3 15 | 16 | ## 6.0.2 17 | 18 | ### Patch Changes 19 | 20 | - 1780f6a: fix linting with CI 21 | - Updated dependencies [1780f6a] 22 | - html2sb-compiler@6.0.2 23 | - enex2sb@6.0.2 24 | - md2sb@6.0.2 25 | 26 | ## 6.0.1 27 | 28 | ### Patch Changes 29 | 30 | - 7ca3583: Organize `package.json`s 31 | - Updated dependencies [7ca3583] 32 | - html2sb-compiler@6.0.1 33 | - enex2sb@6.0.1 34 | - md2sb@6.0.1 35 | 36 | ## 6.0.0 37 | 38 | ### Major Changes 39 | 40 | - 8106162: Drop support node 16 and 18 41 | 42 | ### Minor Changes 43 | 44 | - 8106162: refresh or upgrade dependencies 45 | 46 | ### Patch Changes 47 | 48 | - Updated dependencies [8106162] 49 | - Updated dependencies [8106162] 50 | - enex2sb@6.0.0 51 | - html2sb-compiler@6.0.0 52 | - md2sb@6.0.0 53 | 54 | All notable changes to this project will be documented in this file. 55 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 56 | 57 | ## [5.1.2](https://github.com/pastak/scrapbox-converter/compare/v5.1.1...v5.1.2) (2021-06-14) 58 | 59 | ### Bug Fix 60 | 61 | - Fix Error: could not execute `scrapbox-converter` because of TypeError 62 | 63 | ## [5.1.1](https://github.com/pastak/scrapbox-converter/compare/v5.1.0...v5.1.1) (2020-11-26) 64 | 65 | **Note:** Version bump only for package scrapbox-converter 66 | 67 | # [5.1.0](https://github.com/pastak/scrapbox-converter/compare/v5.0.0...v5.1.0) (2020-11-26) 68 | 69 | ### Features 70 | 71 | - bump remark v13 and install remark-gfm ([66dd5cd](https://github.com/pastak/scrapbox-converter/commit/66dd5cdd4b743dcdc733b047ed9caf9d43f1904d)) 72 | 73 | # [5.0.0](https://github.com/pastak/scrapbox-converter/compare/v4.0.5...v5.0.0) (2020-05-11) 74 | 75 | ### Features 76 | 77 | - upgrade ava and install ava/typescript ([94e21d5](https://github.com/pastak/scrapbox-converter/commit/94e21d57bbbee9bf907769fed5599001b0d9fac2)) 78 | 79 | - introduce typescript ([ecceaba](https://github.com/pastak/scrapbox-converter/commit/ecceabac3882acfbcb3ce4c6861954d5e2a93d95)) 80 | - uninstall babel and rm .babelrc ([d78699d](https://github.com/pastak/scrapbox-converter/commit/d78699d1a0e0bc4f3dda44b3d00902cf7fa9e6b5)) 81 | 82 | ### BREAKING CHANGES 83 | 84 | - Full replacement with TypeScript 85 | - no implements with babel 86 | 87 | ## [4.0.5](https://github.com/pastak/scrapbox-converter/compare/v4.0.4...v4.0.5) (2020-03-13) 88 | 89 | **Note:** Version bump only for package scrapbox-converter 90 | 91 | ## [4.0.4](https://github.com/pastak/scrapbox-converter/compare/v4.0.3...v4.0.4) (2020-03-11) 92 | 93 | **Note:** Version bump only for package scrapbox-converter 94 | 95 | ## [4.0.3](https://github.com/pastak/scrapbox-converter/compare/v4.0.2...v4.0.3) (2020-03-11) 96 | 97 | **Note:** Version bump only for package scrapbox-converter 98 | 99 | ## [4.0.2](https://github.com/pastak/scrapbox-converter/compare/v4.0.1...v4.0.2) (2020-01-20) 100 | 101 | **Note:** Version bump only for package scrapbox-converter 102 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/table.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": null, 3 | "children": [ 4 | { 5 | "type": "table", 6 | "children": [ 7 | { 8 | "type": "tr", 9 | "children": [ 10 | { 11 | "type": "td", 12 | "children": [ 13 | { 14 | "type": "text", 15 | "text": "whos" 16 | } 17 | ] 18 | }, 19 | { 20 | "type": "td", 21 | "children": [ 22 | { 23 | "type": "text", 24 | "italic": true, 25 | "text": "house" 26 | } 27 | ] 28 | }, 29 | { 30 | "type": "td", 31 | "children": [ 32 | { 33 | "type": "text", 34 | "underline": true, 35 | "text": "runs" 36 | } 37 | ] 38 | } 39 | ] 40 | }, 41 | { 42 | "type": "tr", 43 | "children": [ 44 | { 45 | "type": "td", 46 | "children": [ 47 | { 48 | "type": "div", 49 | "children": [ 50 | { 51 | "type": "text", 52 | "bold": true, 53 | "text": "superhouse" 54 | } 55 | ] 56 | } 57 | ] 58 | }, 59 | { 60 | "type": "td", 61 | "children": [ 62 | { 63 | "type": "div", 64 | "children": [ 65 | { 66 | "type": "text", 67 | "text": "I" 68 | } 69 | ] 70 | } 71 | ] 72 | }, 73 | { 74 | "type": "td", 75 | "children": [ 76 | { 77 | "type": "div", 78 | "children": [ 79 | { 80 | "type": "text", 81 | "href": "http://test.com", 82 | "text": "said" 83 | } 84 | ] 85 | } 86 | ] 87 | } 88 | ] 89 | }, 90 | { 91 | "type": "tr", 92 | "children": [ 93 | { 94 | "type": "td", 95 | "children": [ 96 | { 97 | "type": "div", 98 | "children": [ 99 | { 100 | "type": "br", 101 | "force": true 102 | } 103 | ] 104 | } 105 | ] 106 | }, 107 | { 108 | "type": "td" 109 | }, 110 | { 111 | "type": "td", 112 | "children": [ 113 | { 114 | "type": "div", 115 | "children": [ 116 | { 117 | "type": "br", 118 | "force": true 119 | } 120 | ] 121 | } 122 | ] 123 | } 124 | ] 125 | }, 126 | { 127 | "type": "tr", 128 | "children": [ 129 | { 130 | "type": "td", 131 | "children": [ 132 | { 133 | "type": "div", 134 | "children": [ 135 | { 136 | "type": "br", 137 | "force": true 138 | } 139 | ] 140 | } 141 | ] 142 | }, 143 | { 144 | "type": "td", 145 | "children": [ 146 | { 147 | "type": "div", 148 | "children": [ 149 | { 150 | "type": "text", 151 | "text": "whos" 152 | } 153 | ] 154 | } 155 | ] 156 | }, 157 | { 158 | "type": "td", 159 | "children": [ 160 | { 161 | "type": "text", 162 | "text": "house?" 163 | } 164 | ] 165 | } 166 | ] 167 | } 168 | ] 169 | } 170 | ] 171 | } 172 | -------------------------------------------------------------------------------- /packages/md2sb/test/fixtures/md/sample-ja.md: -------------------------------------------------------------------------------- 1 | Markdown記法 表示確認用サンプル 2 | 3 | 任意のアプリケーションで表示確認するためのサンプルです。 4 | 自分用ですが、よろしければ置いておきますのでどうぞ。 5 | 【2016/03/24】 Markdown Extra, GFM(GitHub Flavored Markdown)の記法を追加しました 6 | 7 | [forapp-markdown-sample.md - Dropbox](https://www.dropbox.com/s/4z6kot27jmikhx5/forapp-markdown-sample.md "forapp-markdown-sample.md - Dropbox") 8 | 9 | # 見出し1(h1) 10 | 11 | 見出し1(h1) 12 | ============= 13 | 14 | ## 見出し2(h2) 15 | 16 | 見出し2(h2) 17 | ------------- 18 | 19 | ### 見出し3 20 | 21 | #### 見出し4 22 | 23 | ##### 見出し5 24 | 25 | ###### 見出し6 26 | 27 | --- 28 | 29 | ここは段落です。♪もーもたろさん もーもたーろさん おっこしーにつっけたーちーびまーるこー 30 | 31 | ここは段落です。 32 | ↑半角スペース2個で強制改行しています。 33 | ♪もーもたろさん もーもたーろさん おっこしーにつっけたーちーんあーなごー 34 | 35 | - **強い強調(strong)です。** __これも強い強調です。__ ``strongタグです。`` 36 | - *強調(em)です。* _これも強調です。_ 斜体の``タグになります。 37 | - ***強調斜体です。*** ___強調斜体です。___ ``+``タグになります。 38 | 39 | 40 | > 引用(Blockquote)です 41 | 42 | > > 引用のネストです 43 | 44 | > 上に一行空けないとネストのままです 45 | 46 | 引用(Blockquote)の中にはMarkdown要素を入れられます 47 | 48 | > ## 見出し 49 | > 50 | > 1. 数字リスト 51 | > 2. 数字リスト 52 | 53 | ## エスケープ文字 54 | 55 | \*アスタリスクをバックスラッシュでエスケープ\* 56 | 57 | \## 見出しハッシュ文字をエスケープ 58 | 59 | HTMLタグをバックスラッシュでエスケープ→(\

    ) 60 | 61 | HTMLをバッククォートでインラインコード→(`

    `) 62 | 63 | ## 水平線(`


    `)各種 64 | 65 | アスタリスク3個半角スペース空けて 66 | 67 | * * * 68 | アスタリスク3個以上 69 | 70 | ****** 71 | ハイフン半角スペース空けて 72 | 73 | - - - 74 | 続けてハイフン3個以上 75 | 76 | ------------------- 77 | 78 | ## リスト 79 | 80 | - ハイフン箇条書きリスト 81 | + プラス箇条書きリスト 82 | * 米印箇条書きリスト 83 | - 二階層め・箇条書きリスト 84 | - 三階層め・箇条書きリスト 85 | - 四階層め・箇条書きリスト 86 | - 箇条書きリスト 87 | 88 | --- 89 | 90 | 1. 番号付きリスト 91 | 1. 二階層め・番号付きリスト1 92 | 1. 二階層め・番号付きリスト2 93 | 1. 番号付きリスト2 94 | 1. 二階層め・番号付きリスト1 95 | 1. 三階層め・番号付きリスト1 96 | 1. 三階層め・番号付きリスト2 97 | 1. 四階層め・番号付きリスト1 98 | 1. 二階層め・番号付きリスト2 99 | 1. 番号付きリスト3 100 | 101 | 102 | 定義リストタイトル 103 | : 定義リスト要素1 104 | : 定義リスト要素2 105 | : 定義リスト要素3 106 | 107 | ## コードブロック 108 | 109 | ``` 110 | バッククォート or 半角チルダ3個でくくります。 111 | ###ここにはMarkdown書式は効きません 112 | /* コメント */ 113 | testtest // コメント 114 | ``` 115 | 116 | ~~~ 117 | 118 | 119 | 120 | ニョロニョロ囲みhtml 121 | /* コメント */ 122 | ~~~ 123 | 124 | ``` 125 | 126 | 127 | 128 | バッククォート囲みhtml 129 | ``` 130 | 131 | ``` 132 | body { display: none; } /* バッククォート囲みcss */ 133 | // コメント 134 | ``` 135 | 136 | // 先頭に半角スペース4つでcode囲い 137 | 138 | 139 | バッククォート1個ずつで囲むとインラインのコード(``)です。`body { visibility: hidden; }` 140 | 141 | ## リンク 142 | 143 | markdownでテキストリンク [WIRED.jp](http://wired.jp/ "WIRED.jp") 144 | 145 | <カッコ>でくくってリンク 146 | 147 | 定義参照リンクです。SNSには [Twitter] [1] や [Facebook] [2] や [Google+] [3] などがあります。 148 | 149 | [1]: https://twitter.com/ "Twitter" 150 | [2]: https://ja-jp.facebook.com/ "Facebook" 151 | [3]: https://plus.google.com/ "Google+" 152 | 153 | ## 画像 154 | 155 | ![うきっ!](http://mkb.salchu.net/image/salchu_image02.jpg "salchu_image02.jpg") 156 | 157 | ## table 158 | 159 | | Left align | Right align | Center align | 160 | |:-----------|------------:|:------------:| 161 | | This | This | This | 162 | | column | column | column | 163 | | will | will | will | 164 | | be | be | be | 165 | | left | right | center | 166 | | aligned | aligned | aligned | 167 | 168 | (Kobitoのヘルプmdから拝借しました) 169 | 170 | # GFM 171 | 172 | ## リンク 173 | 174 | URLそのまま貼り付け http://wired.jp/ 175 | 176 | ## 段落中の改行 177 | 178 | ここは段落です。 179 | ↑returnで改行しています。 180 | ♪もーもたろさん もーもたーろさん おっこしーにつっけたーちー○○ー○○ー 181 | 182 | ## コードブロック 183 | 184 | バッククォートの開始囲みに続けて拡張子でシンタックスハイライト 185 | 186 | ```html 187 | 188 | 189 | 190 | バッククォート囲みに拡張子付きhtml 191 | /* コメント */ 192 | ``` 193 | 194 | ```css 195 | body { display: none; } /* コメント */ 196 | ``` 197 | 198 | ```php 199 | 200 | ``` 201 | 202 | ## 取り消し線 203 | 204 | ~~取り消し線(GFM記法)~~ 205 | sタグです。 206 | 207 | ## 単語中のアンダースコアの無効 208 | 209 | GitHub_Flavored_Markdown_test_test 210 | 211 | ## tasklist 212 | 213 | - [ ] task1 214 | - [ ] task2 215 | - [x] completed task 216 | 217 | 218 | 219 | # 参考URL 220 | 221 | - [Daring Fireball: Markdown Syntax Documentation](http://daringfireball.net/projects/markdown/syntax.php) 222 | - [はてなブログで「Markdown記法一覧」を書いてみるテスト - そっと、はてなブログ](http://mametanuki.hateblo.jp/entry/2012/09/22/MarkdownList#Links) 223 | - [Markdownで行こう! · GitHub](https://gist.github.com/wate/7072365) 224 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/list-wrong-inheritance.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": null, 3 | "children": [ 4 | { 5 | "type": "list", 6 | "variant": "ol", 7 | "children": [ 8 | { 9 | "type": "text", 10 | "children": [ 11 | { 12 | "type": "text", 13 | "bold": true, 14 | "text": "France" 15 | }, 16 | { 17 | "type": "br" 18 | }, 19 | { 20 | "type": "list", 21 | "variant": "ul", 22 | "children": [ 23 | { 24 | "type": "text", 25 | "text": "Baguette" 26 | }, 27 | { 28 | "type": "text", 29 | "text": "Camenbert" 30 | }, 31 | { 32 | "type": "text", 33 | "text": "Ganache" 34 | } 35 | ] 36 | } 37 | ] 38 | }, 39 | { 40 | "type": "text", 41 | "children": [ 42 | { 43 | "type": "div", 44 | "children": [ 45 | { 46 | "type": "text", 47 | "text": "Italy" 48 | } 49 | ] 50 | }, 51 | { 52 | "type": "br" 53 | }, 54 | { 55 | "type": "list", 56 | "variant": "ul", 57 | "children": [ 58 | { 59 | "type": "text", 60 | "text": "Parmegiano" 61 | }, 62 | { 63 | "type": "text", 64 | "text": "Prociutto" 65 | }, 66 | { 67 | "type": "text", 68 | "text": "Ricotta" 69 | } 70 | ] 71 | } 72 | ] 73 | }, 74 | { 75 | "type": "text", 76 | "children": [ 77 | { 78 | "type": "text", 79 | "text": "Norway" 80 | }, 81 | { 82 | "type": "br" 83 | }, 84 | { 85 | "type": "list", 86 | "variant": "ul", 87 | "children": [ 88 | { 89 | "type": "text", 90 | "text": "Nothing" 91 | }, 92 | { 93 | "type": "text", 94 | "text": "comes" 95 | }, 96 | { 97 | "type": "text", 98 | "text": "to" 99 | }, 100 | { 101 | "type": "text", 102 | "text": "mind" 103 | } 104 | ] 105 | } 106 | ] 107 | } 108 | ] 109 | }, 110 | { 111 | "type": "div", 112 | "children": [ 113 | { 114 | "type": "list", 115 | "variant": "ul", 116 | "children": [ 117 | { 118 | "type": "text", 119 | "text": "list item" 120 | }, 121 | { 122 | "type": "text", 123 | "children": [ 124 | { 125 | "type": "text", 126 | "text": "level1" 127 | }, 128 | { 129 | "type": "br" 130 | }, 131 | { 132 | "type": "list", 133 | "variant": "ul", 134 | "children": [ 135 | { 136 | "type": "text", 137 | "text": "level2" 138 | }, 139 | { 140 | "type": "list", 141 | "variant": "ul", 142 | "children": [ 143 | { 144 | "type": "text", 145 | "children": [ 146 | { 147 | "type": "br" 148 | }, 149 | { 150 | "type": "list", 151 | "variant": "ul", 152 | "children": [ 153 | { 154 | "type": "text", 155 | "text": "level4" 156 | } 157 | ] 158 | } 159 | ] 160 | } 161 | ] 162 | } 163 | ] 164 | } 165 | ] 166 | } 167 | ] 168 | }, 169 | { 170 | "type": "div", 171 | "children": [ 172 | { 173 | "type": "list", 174 | "variant": "ol", 175 | "children": [ 176 | { 177 | "type": "text", 178 | "text": "ordered list" 179 | }, 180 | { 181 | "type": "text", 182 | "children": [ 183 | { 184 | "type": "text", 185 | "text": "second" 186 | }, 187 | { 188 | "type": "br" 189 | }, 190 | { 191 | "type": "list", 192 | "variant": "ol", 193 | "children": [ 194 | { 195 | "type": "text", 196 | "text": "level2" 197 | } 198 | ] 199 | }, 200 | { 201 | "type": "br" 202 | }, 203 | { 204 | "type": "list", 205 | "variant": "ul", 206 | "children": [ 207 | { 208 | "type": "text", 209 | "text": "non ordered" 210 | } 211 | ] 212 | } 213 | ] 214 | } 215 | ] 216 | }, 217 | { 218 | "type": "div", 219 | "children": [ 220 | { 221 | "type": "br", 222 | "force": true 223 | } 224 | ] 225 | } 226 | ] 227 | } 228 | ] 229 | } 230 | ] 231 | } 232 | -------------------------------------------------------------------------------- /packages/md2sb/src/libs/compiler.ts: -------------------------------------------------------------------------------- 1 | import type { Node, Parent } from "unist"; 2 | import addListItemCount from "./addListItemCount"; 3 | import generateCodeBlock from "./generateCodeBlock"; 4 | 5 | type Context = { 6 | parents: string[]; 7 | listItemCount?: number; 8 | listDepth?: number; 9 | }; 10 | 11 | class Compiler { 12 | lastElmEndLine: number; 13 | decorate: string[]; 14 | 15 | constructor() { 16 | this.lastElmEndLine = 1; 17 | this.decorate = []; 18 | } 19 | 20 | isDecorateElement(node): boolean { 21 | return ["emphasis", "delete", "strong", "heading"].includes( 22 | typeof node === "string" ? node : node.type, 23 | ); 24 | } 25 | 26 | compile( 27 | ast, 28 | _context: Omit & { parents: string[] | undefined } = { 29 | parents: undefined, 30 | }, 31 | ): string { 32 | let result = [...(ast.children || ast)] 33 | .map((node) => 34 | this.node2SbText(node, { 35 | ..._context, 36 | parents: (_context.parents ?? []).slice(0), 37 | }), 38 | ) 39 | .join(""); 40 | if ( 41 | ast.type && 42 | ast.type === "root" && 43 | result.charAt(result.length - 1) !== "\n" 44 | ) { 45 | result += "\n"; 46 | } 47 | return result; 48 | } 49 | 50 | node2SbText(node: Parent, context: Context): string { 51 | let result = ""; 52 | if (context.parents.length === 0 && node.type !== "heading") { 53 | result += "\n".repeat(node.position.start.line - this.lastElmEndLine); 54 | this.lastElmEndLine = node.position.end.line; 55 | } else if (node.type === "listItem") { 56 | const lineBreak = Math.max( 57 | node.position.start.line - this.lastElmEndLine - 1, 58 | 0, 59 | ); 60 | result += "\n".repeat(lineBreak); 61 | this.lastElmEndLine = node.position.end.line; 62 | } 63 | context.parents.push(node.type); 64 | switch (node.type) { 65 | case "thematicBreak": 66 | result += "[/icons/hr.icon]"; 67 | break; 68 | case "emphasis": 69 | this.decorate.push("/"); 70 | result += this.compile(node.children, context); 71 | break; 72 | case "delete": 73 | this.decorate.push("-"); 74 | result += this.compile(node.children, context); 75 | break; 76 | case "strong": 77 | this.decorate.push("*"); 78 | result += this.compile(node.children, context); 79 | break; 80 | case "heading": 81 | if (!("depth" in node)) break; 82 | this.decorate.push("*".repeat(Math.max(1, 5 - (node.depth as number)))); 83 | result += this.compile(node.children, context); 84 | break; 85 | case "link": 86 | if (!("url" in node)) break; 87 | if ( 88 | (node.children as Node[]).filter((_) => _.type === "image").length 89 | ) { 90 | result += (node.children as Node[]) 91 | .map((n) => { 92 | if (n.type === "image" && "url" in n) 93 | return `[${n.url} ${node.url}]`; 94 | return `[${this.compile(n, context)} ${node.url}]`; 95 | }) 96 | .join(""); 97 | } else { 98 | result += `[${this.compile(node.children, context)} ${node.url}]`; 99 | } 100 | break; 101 | case "image": 102 | if ("url" in node) result += `[${node.url}]`; 103 | break; 104 | case "inlineCode": 105 | if ("value" in node) result += `\`${node.value}\``; 106 | break; 107 | case "blockquote": 108 | { 109 | const depth = context.parents.filter( 110 | (p) => p === "blockquote", 111 | ).length; 112 | const quoteMark = "> ".repeat(Math.max(depth - 1, 1)); 113 | result += 114 | (depth === 1 ? "" : "\n") + 115 | quoteMark + 116 | this.compile(node.children, context) 117 | .split(/\n/) 118 | .join("\n" + quoteMark); 119 | } 120 | break; 121 | case "code": 122 | result += generateCodeBlock(node); 123 | break; 124 | case "table": 125 | result += "table:table\n"; 126 | result += 127 | " " + 128 | (node.children as Node[]) 129 | .map((tableRow: Parent) => 130 | (tableRow.children as Node[]) 131 | .map((tableCell: Parent) => { 132 | context.parents.push("tableCell"); 133 | return this.compile(tableCell.children, context); 134 | }) 135 | .join("\t"), 136 | ) 137 | .join("\n "); 138 | break; 139 | case "list": 140 | { 141 | const tagName = "ordered" in node && node.ordered ? "ol" : "ul"; 142 | context.listItemCount = 0; 143 | context.parents[context.parents.length - 1] = tagName; 144 | result += this.compile( 145 | tagName === "ol" ? addListItemCount(node.children) : node.children, 146 | context, 147 | ); 148 | } 149 | break; 150 | case "listItem": { 151 | const depth = context.parents.filter( 152 | (i) => i === "ol" || i === "ul", 153 | ).length; 154 | const isChangedDepth = 2 <= depth && (context.listDepth ?? 0) < depth; 155 | const inner = this.compile(node.children, { 156 | ...context, 157 | listDepth: depth, 158 | }); 159 | result += 160 | (isChangedDepth ? "\n" : "") + 161 | " ".repeat(depth) + 162 | ("listItemCount" in node && node.listItemCount 163 | ? node.listItemCount + ". " 164 | : "") + 165 | inner + 166 | (isChangedDepth ? "" : "\n"); 167 | break; 168 | } 169 | case "paragraph": 170 | result += this.compile(node.children, context); 171 | break; 172 | case "text": 173 | { 174 | if (!("value" in node)) break; 175 | let textValue = node.value; 176 | if (context.parents.includes("tableCell")) 177 | textValue = (node.value as string).replace(/(\s|\t)+$/, ""); 178 | if (context.parents.includes("listItem")) textValue = node.value; 179 | result += textValue; 180 | } 181 | break; 182 | } 183 | if ( 184 | this.isDecorateElement(node) && 185 | !context.parents.slice(0, -1).filter((_) => this.isDecorateElement(_)) 186 | .length 187 | ) { 188 | result = `[${this.decorate.join("")} ${result}]`; 189 | if (node.type === "heading") { 190 | result = 191 | "\n".repeat( 192 | Math.max(node.position.start.line - this.lastElmEndLine, 0), 193 | ) + result; 194 | this.lastElmEndLine = node.position.end.line; 195 | } 196 | this.decorate = []; 197 | } 198 | return result; 199 | } 200 | } 201 | 202 | export function compiler(): void { 203 | const compile = new Compiler(); 204 | this.Compiler = compile.compile.bind(compile); 205 | } 206 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/src/toScrapbox.ts: -------------------------------------------------------------------------------- 1 | const NO_LINE_BREAK = false; 2 | const SOFT_LINE_BREAK = 2; 3 | const HARD_LINE_BREAK = 3; 4 | 5 | function toSimpleText(node) { 6 | if (node.type === "list") { 7 | return "\n" + processList(node); 8 | } 9 | if (node.type === "img") { 10 | let src = ""; 11 | if (node.src) { 12 | src = node.src; 13 | if ( 14 | !/^https?:\/\/gyazo.com\//.test(src) && 15 | !/\.(png|jpe?g|gif|svg|webp)$/.test(src) 16 | ) { 17 | src = src + "#.png"; 18 | } 19 | } else { 20 | src = "data:" + node.mime + ";base64," + node.data; 21 | } 22 | if (node.href) { 23 | return "[" + node.href + " " + src + "]"; 24 | } 25 | return "[" + src + "]"; 26 | } 27 | 28 | let before = ""; 29 | if (node.bold) { 30 | before += "*"; 31 | } 32 | if (node.enlarge) { 33 | before += new Array(node.enlarge + 1).join("*"); 34 | } 35 | if (node.italic) { 36 | before += "/"; 37 | } 38 | if (node.strike) { 39 | before += "-"; 40 | } 41 | if (node.underline) { 42 | before += "_"; 43 | } 44 | let content; 45 | if (node.children) { 46 | content = node.children.map(toSimpleText).filter(Boolean).join(" "); 47 | } else { 48 | content = node.text || ""; 49 | } 50 | let check = ""; 51 | if (node.type === "check") { 52 | check = (node.checked ? "✅" : "⬜") + " "; 53 | } 54 | let inner; 55 | if (before !== "") { 56 | inner = check + "[" + before + " " + content + "]"; 57 | } else { 58 | inner = check + content; 59 | } 60 | 61 | if (node.href) { 62 | if (node.href === "___SELF_WIKI_LINK___") { 63 | inner = inner.replace(/^\s+/, "").replace(/\s+$/, "").replace(/\s/g, "_"); 64 | return "[" + inner + "]"; 65 | } 66 | return "[" + node.href + " " + inner + "]"; 67 | } 68 | return inner; 69 | } 70 | 71 | function processList(node, _?: null, __?: null, indent?: string) { 72 | if (!indent) { 73 | indent = ""; 74 | } 75 | indent += "\t"; 76 | const children = node.children ?? []; 77 | const listAsText = children 78 | .map((listEntry, nr) => { 79 | let children; 80 | if (listEntry.children) { 81 | children = listEntry.children.concat(); 82 | } else { 83 | children = [listEntry]; 84 | } 85 | let lastEntry; 86 | if (children[children.length - 1].type === "list") { 87 | lastEntry = children.pop(); 88 | } 89 | let data = toSimpleText({ 90 | type: listEntry.type, 91 | src: listEntry.src, 92 | checked: listEntry.checked, 93 | children: children, 94 | }); 95 | if (data !== "") { 96 | const lines = data 97 | .split("\n") 98 | .map((line, index) => { 99 | if (index === 0 && node.variant === "ol") { 100 | return indent + (nr + 1) + ". " + line; 101 | } 102 | return indent + line; 103 | }) 104 | .filter((line) => !/^\s*$/.test(line)); 105 | if (lines.length === 0) { 106 | data = ""; 107 | } else { 108 | data = lines.join("\n") + "\n"; 109 | } 110 | } 111 | if (lastEntry) { 112 | data = data + processList(lastEntry, null, null, indent) + "\n"; 113 | } 114 | return data; 115 | }) 116 | .join(""); 117 | return listAsText.substr(0, listAsText.length - 1); 118 | } 119 | 120 | const stringifier = { 121 | link: (node, line) => { 122 | line.push(toSimpleText(node)); 123 | return NO_LINE_BREAK; 124 | }, 125 | list: processList, 126 | hr: () => "[/icons/hr.icon]", 127 | div: (node, _, resources) => { 128 | let result = []; 129 | if (node.children) { 130 | result = stringifyNodes(node, result, resources, true); 131 | } 132 | return result; 133 | }, 134 | table: (node) => 135 | "table: \n" + 136 | node.children 137 | .map((row) => { 138 | const children = row.children ?? []; 139 | return "\t" + children.map((td) => toSimpleText(td)).join("\t"); 140 | }) 141 | .join("\n"), 142 | code: (node) => 143 | "code:_" + 144 | node.text 145 | .split("\n") 146 | .map((codeLine) => "\n\t" + codeLine.split(" ").join(" ")) 147 | .join(""), 148 | img: (node, line) => { 149 | line.push(toSimpleText(node)); 150 | return NO_LINE_BREAK; 151 | }, 152 | br: (node) => (node.force ? HARD_LINE_BREAK : SOFT_LINE_BREAK), 153 | text: (node, line) => { 154 | if (node.blockquote) { 155 | return ( 156 | new Array(node.blockquote + 1).join(">") + " " + toSimpleText(node) 157 | ); 158 | } 159 | line.push(toSimpleText(node)); 160 | return NO_LINE_BREAK; 161 | }, 162 | reference: (node, line, resources) => { 163 | if (resources) { 164 | return stringifyNode(resources[node.hash], line, resources); 165 | } 166 | }, 167 | }; 168 | 169 | function stringifyNode(child, line, resources) { 170 | const nodeStringifier = stringifier[child.type]; 171 | if (!nodeStringifier) { 172 | console.warn("Unknown stringifier for node type: " + child.type); 173 | console.log(child); 174 | return; 175 | } 176 | return nodeStringifier(child, line, resources); 177 | } 178 | 179 | function stringifyNodes(tokens, result, resources, nested = false) { 180 | let line = []; 181 | tokens.children.forEach((child) => { 182 | const block = stringifyNode(child, line, resources); 183 | const isLineBreak = block === SOFT_LINE_BREAK || block === HARD_LINE_BREAK; 184 | if (block === NO_LINE_BREAK) { 185 | return; 186 | } 187 | if (line.length > 0) { 188 | result.push(line.join(" ")); 189 | if (!isLineBreak) { 190 | result.push(""); 191 | } 192 | line = []; 193 | } 194 | if (Array.isArray(block)) { 195 | result = result.concat(block); 196 | } else if (!isLineBreak) { 197 | result.push(block); 198 | result.push(""); 199 | } 200 | if (child.type === "div" && !nested) { 201 | result.push("\n"); 202 | } 203 | }); 204 | if (line.length > 0) { 205 | result.push(line.join(" ")); 206 | } 207 | return result; 208 | } 209 | 210 | function toScrapbox(tokens): { 211 | title: string; 212 | lines: string[]; 213 | } { 214 | let result: string[] = []; 215 | result = stringifyNodes(tokens, result, tokens.resources); 216 | if (tokens.tags) { 217 | result.push(""); 218 | result.push(tokens.tags.map((tag) => "#" + tag).join(" ")); 219 | } 220 | let formerWasEmpty = false; 221 | result = result.filter(function removeMultipleLineBreaks(block) { 222 | if (block === "") { 223 | if (formerWasEmpty) { 224 | return false; 225 | } 226 | formerWasEmpty = true; 227 | } else { 228 | formerWasEmpty = false; 229 | } 230 | return true; 231 | }); 232 | let last = result.length - 1; 233 | while ((result[last] === "" || result[last] === null) && last > 0) { 234 | last -= 1; 235 | } 236 | 237 | const lines = result.slice(0, last + 1).reduce((lines, line) => { 238 | if (line.indexOf("\n") !== -1) { 239 | if (line === "\n") { 240 | line = ""; 241 | } else { 242 | return lines.concat(line.split("\n")); 243 | } 244 | } 245 | lines.push(line); 246 | return lines; 247 | }, [] as string[]); 248 | 249 | let lastLineIsBreak = false; 250 | for (let i = lines.length - 1; i >= 0; i--) { 251 | if (lastLineIsBreak) { 252 | lines.pop(); 253 | } 254 | if (lines[i] !== "") { 255 | break; 256 | } 257 | lastLineIsBreak = true; 258 | } 259 | 260 | return { 261 | title: tokens.title, 262 | lines: lines, 263 | }; 264 | } 265 | 266 | toScrapbox.toSimpleText = toSimpleText; 267 | 268 | export { toSimpleText, toScrapbox }; 269 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/test/fixtures/complex.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | This is a fancy titled 5 | 6 |
    Don't you think?

    Regular Text
    Header 5
    Header 4
    Header 3
    Header 2
    Header 1
    Header 0

    I think this bold text is better than the italic one, but certainly its better than the underlined or striked-through one.

    This is italic text with some bold text in-between, maybe twice.

    This is bold text with some italic text in-between, maybe twice.

    This is underline text with some bold, some italic and some strike-through text.

    Super fancy text 1 order: underline, italic, strike-through, bold

    Super fancy text 2 order: bold, strike through, italic, underline

    This is some marked text to remember!

    A block of code

    function myfunction (a, b) {
      let x = a * 1.08
      let y = b * 2
      return x * y
    }

    could be as cool as a checkbox:

    Item 1
    Item 2
    Item 3 (selected)
    Item 4

    an ordered list

    1. France
    2. Italy
    3. Norway

    or an unordered list
    • cheese
      1. Camembert
      2. Gauda
      3. Blue Cheese
        • Roquefort
        • Gorgonzola
        • Blue Stilton
      4. Emmental
    • wine
    • barbeque

    Links should work as well, to google for example.

    Two images, attached:





    And a table


    whos
    house
    runs
    house
    I




    whos
    house?

    Some horizontal spacer




    And some more text...
    Lets align it right
    and align it center

    Indent a little
    A little bit of this, a little bit of that, a little bit of everything lets me
    Indent a little further

    super script works ass well as subscript and

    how about a little font change or size change?  colors can be changed too
    7 | ]]>
    20170425T045757Z20170515T153011Zininakahousefancymh579149309721100020170420T230000ZiVBORw0KGgoAAAANSUhEUgAAimage/png7181940 8 | 9 | YouneedtohaveadminaccesstobuildthisprojectzoilZOZOzooZoe]]>19700101T000000ZunknownScreen Shot 2016-12-27 at 17.56.03.pngiVBORw0KGgoAAAANSUhEUgAAAsimage/png10766720 10 | 11 | Thissitecannotbeloadedleadedduetocertificateerror:https ?https://aO.awsstatic.com/main/css/1https://aO.awsstatic.com/main/css/1.0.278/style.csshttp ?.0.278/style.css00.2781511 e. css00.2781511 e, css00.2781511 a. css00.278151! e. css00.2781511 a, css00.278151! e, css00.278151! a. css00.278151! a, cssnet::ERRnet:ERRnet: ERRmetersceterameteorCERTIFICATETRANSPARENCYREQUIREDBackSacktosafetysafelyAdvancedAdvancesettings]]>19700101T000000ZunknownScreen Shot 2016-12-15 at 14.28.22.png
    12 |
    13 | -------------------------------------------------------------------------------- /packages/html2sb-compiler/src/parse.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-use-before-define */ 2 | import { Parser } from "htmlparser2"; 3 | const styleParser = require("style-parser"); // eslint-disable-line @typescript-eslint/no-var-requires 4 | import trim from "lodash.trim"; 5 | let md5: any; 6 | 7 | interface SimpleTag { 8 | bold?: boolean; 9 | underline?: boolean; 10 | italic?: boolean; 11 | strike?: boolean; 12 | blockquote?: number; 13 | } 14 | 15 | interface Node extends SimpleTag { 16 | tagName?: string; 17 | attribs?: { [key: string]: string }; 18 | children?: Node[]; 19 | type?: string; 20 | content?: string; 21 | enlarge?: number; 22 | href?: string; 23 | variant?: "ul"; 24 | resources?: Resource[]; 25 | } 26 | 27 | interface ImageNode extends Node { 28 | tagName: "img"; 29 | src: string; 30 | } 31 | 32 | interface ReferenceNode extends Node { 33 | tagName: "reference"; 34 | hash: string; 35 | } 36 | 37 | interface CodeNode extends Node { 38 | tagName: "code"; 39 | text: string; 40 | } 41 | 42 | interface Resource { 43 | encoded?: string; 44 | mime?: string; 45 | } 46 | 47 | interface PageContext { 48 | checkNode?: PageContext; 49 | type?: string; 50 | options?: any; 51 | children?: Array; 52 | title?: string; 53 | tags?: Node[]; 54 | resources?: { 55 | [key: string]: { 56 | data: string; 57 | type: "img"; 58 | mime: string; 59 | }; 60 | }; 61 | } 62 | 63 | const tags = { 64 | img: (context: PageContext, node: Node): void => { 65 | context.children.push({ 66 | type: "img", 67 | src: node.attribs.src, 68 | } as ImageNode); 69 | }, 70 | a: (context: PageContext, node: Node): void => { 71 | const children = node.children 72 | ? parseNodes(node.children, { 73 | options: context.options, 74 | }).children 75 | : null; 76 | let href = node.attribs ? node.attribs.href : "___SELF_WIKI_LINK___"; 77 | if (href !== "___SELF_WIKI_LINK___" && /^((?!https?:))/.test(href)) { 78 | href = "___SELF_WIKI_LINK___"; 79 | } 80 | context.children.push({ 81 | type: "text", 82 | href, 83 | children: children, 84 | }); 85 | }, 86 | p: (context: PageContext, node: Node): void => { 87 | // TODO: Triage as block element to divide div 88 | tags.div(context, node); 89 | }, 90 | note: (context: PageContext, node: Node): void => { 91 | if (context.options?.evernote) { 92 | let content; 93 | 94 | const pageContext: PageContext = { 95 | type: "page", 96 | options: context.options, 97 | children: [], 98 | }; 99 | node.children.forEach((child) => { 100 | if (child.tagName === "title") { 101 | pageContext.title = firstChildContent(child); 102 | } else if (child.tagName === "tag") { 103 | if (!pageContext.tags) { 104 | pageContext.tags = []; 105 | } 106 | pageContext.tags.push(firstChildContent(child)); 107 | } else if (child.tagName === "content") { 108 | content = firstChildContent(child); 109 | } else if (child.tagName === "resource") { 110 | if (!md5) { 111 | // Lazy-load md5 because it is not necessarily required 112 | md5 = require("nano-md5"); 113 | } 114 | const resource: Resource = {}; 115 | child.children.forEach((resourceChild) => { 116 | if (resourceChild.tagName === "data") { 117 | resource.encoded = firstChildContent(resourceChild); 118 | } else if (resourceChild.tagName === "mime") { 119 | resource.mime = firstChildContent(resourceChild); 120 | } 121 | }); 122 | if (/^image\/(png|jpeg|gif)$/.test(resource.mime)) { 123 | const raw = Buffer.from(resource.encoded, "base64"); 124 | const hash = md5.fromBytes(raw.toString("latin1")).toHex(); 125 | if (!pageContext.resources) { 126 | pageContext.resources = {}; 127 | } 128 | pageContext.resources[hash] = { 129 | type: "img", 130 | mime: resource.mime, 131 | data: resource.encoded, 132 | }; 133 | } 134 | } 135 | }); 136 | if (content) { 137 | const contentNodes = parseHTML(content); 138 | if (pageContext.tags) { 139 | pageContext.tags = pageContext.tags.sort(); 140 | } 141 | parseNode(pageContext, { 142 | children: contentNodes, 143 | }); 144 | delete pageContext.options; 145 | context.children.push(pageContext); 146 | } 147 | } 148 | }, 149 | "en-media": (context: PageContext, node: Node): void => { 150 | if (node.attribs && context.options.evernote) { 151 | context.children.push({ 152 | type: "reference", 153 | hash: node.attribs.hash, 154 | } as ReferenceNode); 155 | } 156 | }, 157 | br: singleNode.bind(null, "br"), 158 | td: (context: PageContext, node: Node): void => { 159 | const simple = parseSimple(null, context, node); 160 | simple.type = "td"; 161 | }, 162 | tbody: ignore, 163 | tr: (context: PageContext, node: Node): void => { 164 | const result = { 165 | type: "tr", 166 | children: [], 167 | options: context.options, 168 | }; 169 | if (node.children) { 170 | parseNodes( 171 | node.children.filter((node) => node.tagName === "td"), 172 | result, 173 | ); 174 | } 175 | delete result.options; 176 | context.children.push(result); 177 | }, 178 | table: (context: PageContext, node: Node): void => { 179 | const result = { 180 | type: "table", 181 | children: [], 182 | options: context.options, 183 | }; 184 | if (node.children) { 185 | parseNodes(node.children, result); 186 | } 187 | delete result.options; 188 | context.children.push(result); 189 | }, 190 | span: parseStyle, 191 | font: parseStyle, 192 | code: traditionalCodeBlock, 193 | pre: traditionalCodeBlock, 194 | h1: parseHeader.bind(null, 5), 195 | h2: parseHeader.bind(null, 4), 196 | h3: parseHeader.bind(null, 3), 197 | h4: parseHeader.bind(null, 2), 198 | h5: parseHeader.bind(null, 1), 199 | ol: list.bind(null, "ol"), 200 | ul: list.bind(null, "ul"), 201 | div: (context: PageContext, node: Node): void => { 202 | // are inline tags which are super weird. 203 | // This way .checkNode will be filled somehow. 204 | if ( 205 | context.options.evernote && 206 | node.children && 207 | node.children.length >= 1 && 208 | node.children[0].tagName === "en-todo" 209 | ) { 210 | // Remove the check node 211 | const checkNode = node.children.shift(); 212 | const result = { 213 | type: "check", 214 | checked: checkNode.attribs && /^true$/i.test(checkNode.attribs.checked), 215 | children: [], 216 | options: context.options, 217 | }; 218 | parseSimple(null, result, checkNode); 219 | delete result.options; 220 | context.checkNode = result; 221 | return; 222 | } 223 | 224 | // The fontfamily, namely the Monaco or Consolas font indicates 225 | // that we are in a code block 226 | if ( 227 | context.options.evernote && 228 | node.children && 229 | style(node, "-en-codeblock") === "true" 230 | ) { 231 | const data = []; 232 | node.children.forEach((child) => { 233 | if ( 234 | child.tagName === "div" && 235 | child.children.length === 1 && 236 | child.children[0].type === "Text" 237 | ) { 238 | data.push(firstChildContent(child)); 239 | } else { 240 | const content = collectConcatContents(child); 241 | if (content !== "") data.push(content); 242 | } 243 | }); 244 | context.children.push({ 245 | type: "code", 246 | text: data.join("\n"), 247 | } as CodeNode); 248 | return; 249 | } 250 | 251 | if (node.children) { 252 | const newNode = parseNodes(node.children, { 253 | options: context.options, 254 | }); 255 | if (newNode.children && newNode.children.length > 0) { 256 | context.children.push({ 257 | type: "div", 258 | children: newNode.children, 259 | }); 260 | } 261 | } 262 | }, 263 | hr: singleNode.bind(null, "hr"), 264 | blockquote: parseSimple.bind(null, "blockquote"), 265 | b: parseSimple.bind(null, "bold"), 266 | strong: parseSimple.bind(null, "bold"), 267 | i: parseSimple.bind(null, "italic"), 268 | em: parseSimple.bind(null, "italic"), 269 | u: parseSimple.bind(null, "underline"), 270 | s: parseSimple.bind(null, "strike"), 271 | } as const; 272 | 273 | function parseNode(context, node) { 274 | try { 275 | if (!node.tagName) { 276 | if (node.type === "Text") { 277 | context.children.push({ 278 | type: "text", 279 | text: node.content, 280 | }); 281 | } 282 | } 283 | const parser = tags[node.tagName]; 284 | if (parser) { 285 | parser(context, node); 286 | } else if (node.type === "Text") { 287 | parseSimple(null, context, node); 288 | } else { 289 | ignore(context, node); 290 | } 291 | } catch (e) { 292 | console.info("Node error is happend on"); 293 | console.info(JSON.stringify(node, null, 4)); 294 | console.error(e); 295 | throw new Error("ParseNodeError"); 296 | } 297 | } 298 | 299 | function parseNodes(nodes: Node[], context: PageContext): PageContext { 300 | if (!context.children) { 301 | context.children = []; 302 | } 303 | let checklist = null; 304 | const applyChecklist = () => { 305 | if (checklist) { 306 | context.children.splice(checklist.index, 0, { 307 | type: "list", 308 | variant: "ul", 309 | children: checklist.entries, 310 | }); 311 | checklist = null; 312 | } 313 | }; 314 | nodes.forEach((node) => { 315 | if (node.type === "Text" && node.content === "\n") { 316 | return; 317 | } 318 | if (node.tagName === "title") { 319 | // context.title = node.children[0].content 320 | } 321 | parseNode(context, node); 322 | if (context.checkNode) { 323 | if (!checklist) { 324 | checklist = { 325 | index: context.children.length, 326 | entries: [], 327 | }; 328 | } 329 | checklist.entries.push(context.checkNode); 330 | delete context.checkNode; 331 | } else if (node.tagName === "br" || node.tagName === "div") { 332 | applyChecklist(); 333 | } 334 | }); 335 | applyChecklist(); 336 | return context; 337 | } 338 | 339 | function reduceSameProperties(parent) { 340 | if (parent.children.length === 0) { 341 | return parent; 342 | } 343 | let groupParent; 344 | ["bold", "underline", "strike", "italic", "href"].forEach((prop) => { 345 | const value = parent.children[0][prop]; 346 | for (let i = 1; i < parent.children.length; i++) { 347 | if (parent.children[i][prop] !== value) { 348 | return; 349 | } 350 | } 351 | parent.children.forEach((token) => { 352 | delete token[prop]; 353 | }); 354 | if (value !== undefined) { 355 | if (!groupParent) { 356 | if (parent.type !== "text") { 357 | groupParent = { 358 | type: "text", 359 | children: parent.children, 360 | }; 361 | parent.children = [groupParent]; 362 | } else { 363 | groupParent = parent; 364 | } 365 | } 366 | groupParent[prop] = value; 367 | } 368 | }); 369 | return parent; 370 | } 371 | 372 | function parseSimple(variant: keyof SimpleTag, context: PageContext, node) { 373 | let children; 374 | if (node.children) { 375 | children = parseNodes(node.children, { 376 | options: context.options, 377 | }).children; 378 | } 379 | const result: Node = { 380 | type: "text", 381 | children: children, 382 | }; 383 | if (variant === "blockquote") { 384 | result.blockquote = 1; 385 | } else if (variant) { 386 | result[variant] = true; 387 | } 388 | if (!context.children) { 389 | context.children = []; 390 | } 391 | 392 | // Lists with broken inheritance (an ul inside a ul, instead of a li) 393 | // should be treated differently: the ul should become a child of the former 394 | // list entry. 395 | if ( 396 | context.type === "list" && 397 | (node.tagName === "ol" || node.tagName === "ul") 398 | ) { 399 | let pos = context.children.length - 1; 400 | let formerLi; 401 | // There can - and likely will - be spaces, and line breaks between the list 402 | // nodes so we need to search backwards for the best match 403 | do { 404 | formerLi = context.children[pos]; 405 | pos--; 406 | } while ((!formerLi || !formerLi.children) && pos > -1); 407 | // If there is none, well, let us create one... 408 | if (!formerLi) { 409 | formerLi = { 410 | type: "text", 411 | children: [], 412 | }; 413 | context.children.push(formerLi); 414 | } else if (!formerLi.children) { 415 | formerLi.children = []; 416 | } 417 | formerLi.children.push({ 418 | type: "br", 419 | }); 420 | formerLi.children.push({ 421 | type: "list", 422 | variant: node.tagName, 423 | children: result.children, 424 | }); 425 | } else { 426 | context.children.push(result); 427 | } 428 | return result; 429 | } 430 | 431 | function parseHeader(enlarge, context, node) { 432 | context.children.push({ 433 | type: "br", 434 | }); 435 | const simpleNode = parseSimple(null, context, node); 436 | simpleNode.bold = true; 437 | simpleNode.enlarge = enlarge; 438 | } 439 | 440 | function traditionalCodeBlock(context, node) { 441 | context.children.push({ 442 | type: "code", 443 | text: collectConcatContents(node), 444 | }); 445 | } 446 | 447 | function collectConcatContents(node) { 448 | const contents = []; 449 | if (node.content) { 450 | contents.push(node.content); 451 | } 452 | if (node.children) { 453 | node.children.forEach((child) => { 454 | contents.push(collectConcatContents(child)); 455 | }); 456 | } 457 | return trim( 458 | contents 459 | .filter((t) => { 460 | if (t === "") return false; 461 | return !/^\n\s+$/.test(t); 462 | }) 463 | .join(""), 464 | ); 465 | } 466 | 467 | function list(variant, context, node) { 468 | const result = { 469 | type: "list", 470 | variant: variant, 471 | children: [], 472 | options: context.options, 473 | }; 474 | if (!node.children) return; 475 | node.children.forEach((child) => { 476 | parseSimple(null, result, child); 477 | }); 478 | delete result.options; 479 | context.children.push(result); 480 | } 481 | 482 | function ignore(context, node) { 483 | if (node.children) { 484 | parseNodes(node.children, context); 485 | } 486 | } 487 | 488 | function parseStyle(context, node) { 489 | const fontSize = style(node, "font-size"); 490 | let parsed; 491 | let enlarge = 0; 492 | if (fontSize && (parsed = /^([0-9]+)\s*px\s*$/i.exec(fontSize))) { 493 | const num = Number.parseInt(parsed[1], 10); 494 | if (num > 68) { 495 | enlarge += 1; 496 | } 497 | if (num > 56) { 498 | enlarge += 1; 499 | } 500 | if (num > 42) { 501 | enlarge += 1; 502 | } 503 | if (num > 30) { 504 | enlarge += 1; 505 | } 506 | if (num > 21) { 507 | enlarge += 1; 508 | } 509 | } 510 | const bold = style(node, "font-weight") === "bold"; 511 | const italic = /(^|\s)italic(\s|$)/i.test(style(node, "font-style")); 512 | const underline = 513 | /(^|\s)underline(\s|$)/i.test(style(node, "text-decoration")) || 514 | (context.options.evernote && style(node, "-evernote-highlight") === "true"); 515 | const strikeThrough = /(^|\s)line-through(\s|$)/i.test( 516 | style(node, "text-decoration"), 517 | ); 518 | const addedNode = parseSimple(null, context, node); 519 | if (enlarge !== 0) { 520 | addedNode.enlarge = enlarge; 521 | addedNode.bold = true; 522 | } 523 | if (bold) { 524 | addedNode.bold = true; 525 | } 526 | if (underline) { 527 | addedNode.underline = true; 528 | } 529 | if (strikeThrough) { 530 | addedNode.strike = true; 531 | } 532 | if (italic) { 533 | addedNode.italic = true; 534 | } 535 | } 536 | 537 | function style(node, prop) { 538 | if (!node.style) { 539 | if (!node.attribs || !node.attribs.style) { 540 | return ""; 541 | } 542 | try { 543 | node.style = styleParser(node.attribs.style); 544 | } catch (e) { 545 | return ""; 546 | } 547 | } 548 | return node.style[prop] || ""; 549 | } 550 | 551 | function firstChildContent(node) { 552 | if (!node.children) { 553 | return; 554 | } 555 | if (node.children.length === 0) { 556 | return; 557 | } 558 | return node.children[0].content; 559 | } 560 | 561 | function singleNode(type, context) { 562 | const obj: { type: string; force?: true } = { type: type }; 563 | if (type === "br") obj.force = true; 564 | context.children.push(obj); 565 | } 566 | 567 | function reduceSimpleNodes(parent) { 568 | parent.children = parent.children.filter((token) => { 569 | if (token.type !== "text") { 570 | return true; 571 | } 572 | if (token.children) { 573 | return true; 574 | } 575 | if (token.text === undefined || token.text === null) { 576 | return false; 577 | } 578 | if (/^\s+$/i.test(token.text)) { 579 | return false; 580 | } 581 | token.text = trim(token.text); 582 | return true; 583 | }); 584 | let allText = true; 585 | parent.children.forEach((token) => { 586 | if (token.children) { 587 | token = reduceSimpleNodes(token); 588 | } 589 | if (token.type !== "text") { 590 | allText = false; 591 | } 592 | if ( 593 | token.type === "text" && 594 | token.children && 595 | token.children.length === 1 && 596 | (token.children[0].type === "text" || token.children[0].type === "img") 597 | ) { 598 | const targetToken = token.children[0]; 599 | if (token.href && targetToken.href) { 600 | return; 601 | } 602 | token.type = targetToken.type; 603 | if (targetToken.src) { 604 | token.src = targetToken.src; 605 | } 606 | if (targetToken.href) { 607 | token.href = targetToken.href; 608 | } 609 | if (targetToken.bold || token.bold) { 610 | token.bold = true; 611 | } 612 | if (targetToken.italic || token.italic) { 613 | token.italic = true; 614 | } 615 | if (targetToken.strike || token.strike) { 616 | token.strike = true; 617 | } 618 | if (targetToken.underline || token.underline) { 619 | token.underline = true; 620 | } 621 | if (targetToken.enlarge) { 622 | token.bold = true; 623 | token.enlarge = (token.enlarge || 0) + targetToken.enlarge; 624 | } 625 | if (targetToken.blockquote) { 626 | token.blockquote = (token.blockquote || 0) + targetToken.blockquote; 627 | } 628 | if (targetToken.children) { 629 | token.children = targetToken.children; 630 | } else { 631 | delete token.children; 632 | } 633 | if (targetToken.text) { 634 | token.text = targetToken.text; 635 | } else { 636 | delete token.text; 637 | } 638 | } 639 | }); 640 | if (allText && parent.children.length > 1) { 641 | parent = reduceSameProperties(parent); 642 | } 643 | parent.children = parent.children.filter((token) => { 644 | if ( 645 | !token.children && 646 | Object.prototype.hasOwnProperty.call(token, "children") 647 | ) { 648 | delete token.children; 649 | } 650 | return ( 651 | !token.children || token.children.length !== 0 || parent.type === "tr" 652 | ); 653 | }); 654 | return parent; 655 | } 656 | 657 | function parseHTML(input: string) { 658 | let current: Node = {}; 659 | const stack = []; 660 | const root = current; 661 | const parser = new Parser( 662 | { 663 | onopentag: (name, attribs) => { 664 | stack.push(current); 665 | const next: Node = { 666 | tagName: name, 667 | }; 668 | if (Object.keys(attribs).length > 0) { 669 | next.attribs = attribs; 670 | } 671 | if (!current.children) { 672 | current.children = []; 673 | } 674 | current.children.push(next); 675 | current = next; 676 | }, 677 | ontext: (text) => { 678 | if (!current.children) { 679 | current.children = []; 680 | } 681 | if ( 682 | current.children.length > 0 && 683 | current.children[current.children.length - 1].type === "Text" 684 | ) { 685 | current.children[current.children.length - 1].content += text; 686 | } else { 687 | current.children.push({ 688 | type: "Text", 689 | content: text, 690 | }); 691 | } 692 | }, 693 | onclosetag: (tagName) => { 694 | if (current.tagName === tagName) { 695 | current = stack.pop(); 696 | return; 697 | } 698 | for (let i = stack.length - 1; i >= 0; i--) { 699 | if (stack[i].tagName === tagName) { 700 | while (stack.length > i) { 701 | current = stack.pop(); 702 | } 703 | return; 704 | } 705 | } 706 | // ignore tags that are ever opened 707 | }, 708 | }, 709 | { 710 | recognizeCDATA: true, 711 | decodeEntities: true, 712 | }, 713 | ); 714 | parser.write(String(input)); 715 | parser.end(); 716 | return root.children || ([] as Node[]); 717 | } 718 | 719 | export const parse = (input: string, options = {}) => { 720 | const htmlNodes = parseHTML(input); 721 | let parseResult = parseNodes(htmlNodes, { 722 | title: null, 723 | options: options, 724 | children: [], 725 | }); 726 | delete parseResult.options; 727 | parseResult = reduceSimpleNodes(parseResult); 728 | if (parseResult.children.length === 0) { 729 | return []; 730 | } 731 | let allPages = true; 732 | if (parseResult.type === "page") { 733 | allPages = false; 734 | } else { 735 | for (let i = 0; i < parseResult.children.length; i++) { 736 | if (parseResult.children[i].type !== "page") { 737 | allPages = false; 738 | break; 739 | } 740 | } 741 | } 742 | if (!allPages) { 743 | return [parseResult] as unknown as Node[]; 744 | } 745 | return parseResult.children; 746 | }; 747 | --------------------------------------------------------------------------------