A simple wrapper around the md-to-notion
tool that provides a cleaner API for uploading markdown files to Notion.
Run the installation script to add notion
to your PATH:
./install.sh
This will:
- Create a symlink in
~/.local/bin/notion
- Add
~/.local/bin
to your PATH (if not already there) - Allow you to use
notion
from anywhere
After installation, you can use:
notion <page-id> ./docs
notion <page-id> ./document.md
notion <page-id> ./docs --dry-run
notion --help
pnpm add @vrerv/md-to-notion
pnpm add -D typescript @types/node
# If installed as binary
notion <page-id> <path-to-md-file>
# Or using bun directly
bun run notion-upload.ts <page-id> <path-to-md-file>
Example:
notion 242a334e22908019a500f348e03a4a31 ./document.md
# If installed as binary
notion <page-id> <path-to-directory>
# Or using bun directly
bun run notion-upload.ts <page-id> <path-to-directory>
Example:
notion 242a334e22908019a500f348e03a4a31 ./docs
This will upload all .md
files in the directory (including subdirectories).
The CLI automatically creates folder hierarchy in Notion using relative paths only. For example, if you upload tests/fixtures/project/docs/README.md
:
- Creates a page called "tests" under your target page
- Creates a page called "fixtures" under the "tests" page
- Creates a page called "project" under the "fixtures" page
- Creates a page called "docs" under the "project" page
- Uploads the content to the "docs" page
Smart Page Reuse: The CLI checks if folder pages already exist and reuses them, preventing duplicate pages.
Relative Paths Only: The folder hierarchy is created based on the relative path from your current working directory, not the full absolute path.
This maintains your folder structure in Notion:
Your Target Page
├── tests/
│ └── fixtures/
│ └── project/
│ ├── docs/
│ │ ├── README.md
│ │ └── api.md
│ └── overview.md
Upload files matching a pattern:
# Upload all markdown files in current directory
notion <page-id> "./*.md"
# Upload all markdown files recursively
notion <page-id> "./**/*.md"
# Upload files matching a specific pattern
notion <page-id> "./mattermost-tasks*.md"
# Upload files in a specific directory
notion <page-id> "./docs/*.md"
Examples:
notion 242a334e22908019a500f348e03a4a31 "./*.md"
notion 242a334e22908019a500f348e03a4a31 "./mattermost-tasks*.md"
notion 242a334e22908019a500f348e03a4a31 "./**/*.md"
Important: Always quote glob patterns to prevent shell expansion:
- ✅
notion <page-id> "*.md"
- Correct (script handles glob) - ❌
notion <page-id> *.md
- Shell expands first (may only pass one file)
Test what would be uploaded without actually uploading:
notion <page-id> <path-to-md-files> --dry-run
Replace existing pages instead of creating new ones:
notion <page-id> <path-to-md-files> --replace
Show help information:
notion --help
- ✅ Single File Upload: Upload individual markdown files
- ✅ Directory Upload: Upload all markdown files in a directory
- ✅ Glob Pattern Upload: Upload files matching patterns like
*.md
,mattermost-tasks*.md
- ✅ Folder Hierarchy: Automatically creates folder structure in Notion
- ✅ Recursive Search: Finds markdown files in subdirectories
- ✅ Smart Filtering: Excludes
node_modules
,.git
, and other common directories - ✅ Dry Run Mode: Preview what would be uploaded without actually uploading
- ✅ Replace Mode: Replace existing pages instead of creating new ones
- ✅ Page ID Validation: Validates Notion page ID format
- ✅ Clean Output: Only shows upload status, not verbose md-to-notion output
- ✅ Error Handling: Clear error messages for failed uploads
- ✅ Summary: Shows total uploaded/failed count
- ✅ TypeScript Support: Full TypeScript support with proper types
- ✅ Binary Installation: Install as
notion
command for global use
Found 2 markdown file(s) to upload
Uploading: test-docs/page2.md
✅ test-docs/page2.md
Uploading: test-docs/page1.md
✅ test-docs/page1.md
📊 Summary: 2 uploaded, 0 failed
The wrapper uses the NOTION_TOKEN
environment variable from your shell. Make sure it's set in your .zshrc
:
export NOTION_TOKEN="your-notion-token-here"
Or export it for the current session:
export NOTION_TOKEN="your-notion-token-here"
# Run the upload script
pnpm run upload <page-id> <path>
# Run in development mode with file watching
pnpm run dev
# Type check the TypeScript code
pnpm run type-check
# Run tests (file discovery and validation)
pnpm run test
# Build the binary
pnpm run build
The project includes a test suite that validates:
- File discovery functionality
- Directory traversal
- Markdown file detection
- Specific file existence checks
Run tests with:
pnpm run test
- Node.js with Bun
@vrerv/md-to-notion
package installed- Valid Notion page ID (32 characters)
- Valid Notion token
If you get an "Invalid page ID format" error, make sure your page ID is:
- 32 characters long
- Contains only letters and numbers
- Example:
242a334e22908019a500f348e03a4a31
If no markdown files are found:
- Check that the path exists
- Ensure files have
.md
extension - Verify the directory contains markdown files
If you get a token error:
- Set the
NOTION_TOKEN
environment variable - Ensure the token has write permissions to the target page
- Check that the page ID is correct
If the notion
command is not found:
- Make sure you ran
./install.sh
- Restart your terminal or run
source ~/.zshrc
- Check that
~/.local/bin
is in your PATH:echo $PATH | grep local
To remove the binary:
./uninstall.sh