├── .gitignore ├── test.js ├── readme.md ├── package.json └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | const assert = require(`assert`) 2 | const buildFilename = require(`./build-filename`) 3 | 4 | assert.equal(buildFilename('23/01/2020'), `23-01-2020.md`) -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | Back up your Bear Notes as markdown files. 2 | 3 | # Install 4 | 5 | ```sh 6 | npm i -g backup-bear-notes 7 | ``` 8 | 9 | # Usage 10 | 11 | ```sh 12 | backup-bear-notes ./my-backup-location 13 | ``` 14 | 15 | # License 16 | 17 | [WTFPL](https://wtfpl2.com) 18 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "backup-bear-notes", 3 | "version": "1.0.4", 4 | "description": "Back up your Bear Notes content to a folder", 5 | "bin": "index.js", 6 | "scripts": { 7 | "test": "node test.js" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/TehShrike/backup-bear-notes.git" 12 | }, 13 | "keywords": [ 14 | "bear", 15 | "notes", 16 | "app", 17 | "backup" 18 | ], 19 | "author": "TehShrike", 20 | "license": "WTFPL", 21 | "dependencies": { 22 | "make-dir": "1.3.0", 23 | "mri": "^1.1.4", 24 | "pify": "4.0.1", 25 | "sqlite": "3.0.0", 26 | "untildify": "3.0.3" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const untildify = require(`untildify`) 4 | const buildFilename = require(`./build-filename`) 5 | const mri = require('mri') 6 | 7 | const { 'use-tags-as-directories': useTagsAsDirectories } = mri(process.argv.slice(2)) 8 | 9 | const BEAR_DB = untildify( 10 | `~/Library/Group Containers/9K33E3U3T4.net.shinyfrog.bear/Application Data/database.sqlite` 11 | ) 12 | 13 | const [ ,, outputDirectory ] = process.argv 14 | 15 | if (!outputDirectory) { 16 | process.stderr.write(`You must provide an output directory\n`) 17 | process.exit(1) 18 | } 19 | 20 | main( 21 | untildify(outputDirectory) 22 | ).then(writeFileResults => { 23 | console.log(`Backed up ${ writeFileResults.length } notes.`) 24 | }).catch(err => { 25 | process.nextTick(() => { 26 | throw err 27 | }) 28 | }) 29 | 30 | async function main(outputDirectory) { 31 | const path = require(`path`) 32 | const sqlite = require(`sqlite`) 33 | const makeDir = require(`make-dir`) 34 | const pify = require(`pify`) 35 | const fs = pify(require(`fs`)) 36 | 37 | await makeDir(outputDirectory) 38 | 39 | const db = await sqlite.open(BEAR_DB) 40 | 41 | const rows = await db.all(` 42 | SELECT 43 | ZSFNOTE.ZTITLE AS title, 44 | ZSFNOTE.ZTEXT AS text, 45 | ZSFNOTETAG.ZTITLE AS tag, 46 | ZSFNOTE.ZTRASHED AS trashed 47 | FROM 48 | ZSFNOTE 49 | LEFT JOIN Z_7TAGS ON ZSFNOTE.Z_PK = Z_7TAGS.Z_7NOTES 50 | LEFT JOIN ZSFNOTETAG ON Z_7TAGS.Z_14TAGS = ZSFNOTETAG.Z_PK 51 | ORDER BY LENGTH(tag)`) 52 | 53 | if (useTagsAsDirectories) { 54 | const tags = Array.from(new Set(rows.map(row => row.tag))) 55 | 56 | await Promise.all(tags.map(tag => { 57 | const tagDirectory = tag ? tag : 'untagged' 58 | 59 | return makeDir(path.join(outputDirectory, tagDirectory)) 60 | })) 61 | } 62 | 63 | return Promise.all( 64 | rows.map(({ title, text, tag, trashed }) => { 65 | const filename = buildFilename(title) 66 | const destinationDirectory = !useTagsAsDirectories ? 67 | outputDirectory : path.join(outputDirectory, tag || `untagged`) 68 | 69 | if (trashed) { 70 | return fs.unlink(path.join(destinationDirectory, filename)) 71 | } 72 | 73 | return fs.writeFile(path.join(destinationDirectory, filename), text, { encoding: `utf8` }) 74 | }) 75 | ) 76 | } --------------------------------------------------------------------------------