Sync Markdown files with configurable routing and multi-user support
npm install @joshcanhelp/mdsyncSync markdown files from personal note directories into a shared repository with multi-user support. This was created to manage devlogs, engineering notes, and ADRs in an Obsidian repo but still make them available to the rest of the team. It works by looking for files in specific routes, filtered by tags or other properties, then copying the found notes over, with optional tranfomrations. I use it here:
- Devlogs in this repo
- Devlogs and ADRs for this project
``bash`
npm install -g @joshcanhelp/mdsync
`bash`
npm install --save-dev @joshcanhelp/mdsync
`bash`
npm install
npm run build
npm run mdsync [COMMAND]
Two configuration files control behavior:
Create markdown-sync.config.cjs in the repo root. This file defines shared settings and should be committed.
See markdown-sync.config.example.cjs for a complete example.
Required fields:
- outputDir - Where to sync files (relative to repo root)
Optional fields:
- routes - Routing rules (first match wins)exclude
- - Patterns to exclude from syncingrequireTags
- - Files must have ALL these tagsrequireProps
- - Files must have matching property valuestransformations
- - Content transformation settings
Create .markdown-sync.user.cjs in the repo root or your home directory. This file contains personal settings and should NOT be committed.
See .markdown-sync.user.example.cjs for a complete example.
Required:
- sourceDir - Path to your markdown files
Optional fields:
- userId - Override auto-detected user IDroutes
- - Define routes if no repo config existsexclude
- - Patterns to excluderequireTags
- - Required frontmatter tagsrequireProps
- - Required frontmatter propertiestransformations
- - Content transformation settings
User ID is determined in this order:
1. Git config user.email (username portion before @)MARKDOWN_SYNC_USER
2. Environment variable userId
3. field in user config
4. Error if none found
User ID is injected before the file extension:
``
Source: vault/daily-notes/2024-01-09.md
Output: notes/logs/2024-01-09.username.md
Collision detection prevents multiple users from creating files with the same base name in the same location.
Routes are evaluated in order. First match wins.
Each route can specify:
- sourcePath - Glob pattern to match source file pathstag
- - Frontmatter tag to match (without # prefix)outputPath
- - Subdirectory for matched files
If a route has both sourcePath and tag, the file must match BOTH conditions (AND logic).
Examples:
`javascript
// Match only files in Logs/ with artifact/devlog tag
{
sourcePath: "Logs/*/.md",
tag: "artifact/devlog",
outputPath: "devlog"
}
// Match any file with working tag
{
tag: "working",
outputPath: "projects"
}
// Match any file in Archive/
{
sourcePath: "Archive/*/.md",
outputPath: "archive"
}
`
Files can be filtered by required fields:
Files must have ALL specified tags:
`js`
requireTags: ["public", "published"]
Files must have matching frontmatter properties. Matching uses substring matching:
`js`
requireProps: {
status: ["published", "review"], // must contain one of these
title: "*", // must exist with any value
references: "[[Technology/Pub" // must contain this substring
}
Content transformations are applied during sync to modify files before writing them to the output directory.
Wikilinks ([[internal-link]]) are transformed based on frontmatter URL properties:
``
[[note.md]] → note.md
[[note.md|Display Text]] → Display Text
Configure behavior in markdown-sync.config.example.cjs:
- urlProperty - Frontmatter property containing the URL (default: "link_to")wikilinkBehavior
- - How to handle wikilinks:"resolve"
- (default) - Convert to markdown links, keep unresolved as wikilinks"remove"
- - Remove wikilinks that can't be resolved"preserve"
- - Keep all wikilinks unchangedlinkOverrides
- - Manual URL mappings for specific files
Control which frontmatter properties appear in synced files:
- contentProperties - Injected into file content (removed from frontmatter)passthroughProperties
- - Kept in frontmatter unchanged
- Properties not listed in either are omitted from output
See markdown-sync.config.example.cjs for configuration examples.
Sync command shows unresolved wikilinks:
`bash
mdsync syncOutput shows: Wikilinks: 3 unresolved
mdsync sync --verbose
Commands
$3
`bash
mdsync config
`$3
Show which files would be synced:
`bash
mdsync scan
`$3
Show what would change without making modifications:
`bash
mdsync status
`$3
Perform the actual sync:
`bash
mdsync sync
mdsync sync --verbose
`$3
Remove all synced files for the current user:
`bash
mdsync clean
``