@davidsneighbour/draft-pipeline
v0.3.0
Published
Reusable markdown-to-pdf and optional reMarkable upload toolkit
Readme
@davidsneighbour/draft-pipeline
A standalone package extracted from the bookofhugo.dev toolchain for:
- Markdown to print-style PDF generation.
- With Tailwind CSS integration for PDF styling.
- Optional upload of built PDFs to a reMarkable tablet and/or any SSH/rsyncable destination.
How it works
- Step 1: Build Tailwind CSS from
TAILWIND_INPUT_CSStoOUTPUT_CSS_FILE. - Step 2: Convert Markdown files from
MARKDOWN_INPUT_DIRinto PDFs inOUTPUT_DIR(either n-to-1 or n-to-n generation). - Step 3: Run enabled upload integrations.
Markdown to PDF notes
- The input is a directory tree containing Markdown files (
.md,.markdown). - It's front matter is read with
gray-matter. - Files with
b/pdf/ignore: truein the front matter are skipped. - Markdown content is rendered via
marked. - PDF generation runs through headless Playwright Chromium.
- Header, footer, and document HTML can be configured using templates.
- Output PDF names are flattened/sanitized from source path segments and can be in parts configured via front matter.
Upload integrations
The upload stage offers integrations to upload (synchronize) the created PDF files to various locations.
- Generic SSH/Rsync integration - (
SSH_UPLOAD_ENABLED=true|false) - reMarkable integration - (
REMARKABLE_UPLOAD_ENABLED=true|false)
Each integration must be explicitly enabled (true).
reMarkable integration internals
This part of the project exists because I, @davidsneighbour use a reMarkable 2. reMarkable does not ingest plain PDF uploads directly into the library. It requires companion .metadata and .content JSON files, a UUID for each file, and a folder that the file is copied to. This integration creates those files on the fly, copies all files into the configured xochitl data directory, and finally restarts xochitl so the documents appear in the UI. This is relatively slow per file and, depending on the amount of files already on the tablet, the restart takes very long. It's not optimal, but it works. I suggest to create two configurations, one to create the PDF files and one that creates and uploads those files when you want to update the tablet. Or you disconnect your tablet while uploading. The script will fail gracefully when no remarkable is connected.
This README.md assumes you have your reMarkable tablet set up for SSH access and I call it remarkable in my SSH configuration instead of a dynamically assigned IP address. Read the reMarkable support article for the USB to SSH access for details on how to set this up. I might write up a guide one day.
Generic SSH integration
Uploads built PDFs to any SSH target and directory.
scpmode uploads one PDF at a time.rsyncmode uses include/exclude patterns so only*.pdffiles are transferred.- Both modes are configurable through
SSH_UPLOAD_METHOD=scp|rsync.
Installation
npm install @davidsneighbour/draft-pipelineCommands
draft-pipeline css
To be written... creates the css file.
draft-pipeline pdf
To be written... creates the PDF files.
draft-pipeline upload
To be written... uploads to configured targets.
draft-piepline build
To be written... a successive pipeline of css > pdf > upload.
Configuration
The pipeline configuration can be changed using .env-variables, a config file, or directly via CLI parameters when you call the tool. To create the env file run the following command:
npx draft-pipeline setup-env --pipeline-env sample.envLeaving --pipeline-env out will save the example to sample.env
All configuration is resolved in this order:
- Sensible defaults
- Environment variables (
.pipeline.envby default) - JSON config file (
.pipeline.config.jsonby default) - CLI flags
Later sources override earlier ones.
Print the final resolved configuration and where each value came from:
draft-pipeline build --print-config
draft-pipeline build --print-config=jsonCLI-only options
--pipeline-env <path>: env file path (default:.pipeline.env, will be loaded automatically)--pipeline-config <path>: JSON config file path (default:.pipeline.config.json)--print-config[=table|json]: show resolved config and source mapping, then exit
Parameter reference (CLI / env / config)
| Purpose | CLI | Env | Config key | Default |
| ------------------------------------- | --------------------- | ------------------------------- | ---------------------------- | --------------------------------- |
| Markdown input dir | — | MARKDOWN_INPUT_DIR | markdownInputDir | ./book |
| Output dir | — | OUTPUT_DIR | outputDir | ./dist |
| Output CSS file | — | OUTPUT_CSS_FILE | outputCssFile | ./dist/output.css |
| Tailwind input CSS | — | TAILWIND_INPUT_CSS | tailwindInputCss | ./styles/pdf.css |
| Header template | --header-template | HEADER_TEMPLATE_PATH | headerTemplatePath | ./templates/header.html |
| Footer template | --footer-template | FOOTER_TEMPLATE_PATH | footerTemplatePath | ./templates/footer.html |
| Document template | --document-template | DOCUMENT_TEMPLATE_PATH | documentTemplatePath | ./templates/document.html |
| Book layout CSS | --book-layout-css | BOOK_LAYOUT_CSS_PATH | bookLayoutCssPath | ./styles/pdf-book-layout.css |
| Print ready mode | --printready | PDF_PRINT_READY | pdfPrintReady | false |
| PDF bleed | --bleed | PDF_BLEED | pdfBleed | 3mm |
| Enable reMarkable upload | — | REMARKABLE_UPLOAD_ENABLED | remarkableUploadEnabled | false |
| reMarkable host | — | REMARKABLE_HOST | remarkableHost | remarkable |
| reMarkable xochitl dir | — | REMARKABLE_XOCHITL_DIR | remarkableXochitlDir | .local/share/remarkable/xochitl |
| reMarkable parent folder UUID | — | REMARKABLE_PARENT_FOLDER_UUID | remarkableParentFolderUuid | empty |
| reMarkable parent folder display name | — | REMARKABLE_PARENT_FOLDER_NAME | remarkableParentFolderName | Book of Hugo |
| Enable SSH upload | — | SSH_UPLOAD_ENABLED | sshUploadEnabled | false |
| SSH target (user@host) | — | SSH_TARGET | sshTarget | empty |
| SSH target dir | — | SSH_TARGET_DIR | sshTargetDir | empty |
| SSH upload method | — | SSH_UPLOAD_METHOD | sshUploadMethod | scp |
| SSH port | — | SSH_PORT | sshPort | unset |
Sensible defaults
Defaults are chosen to make local development work out-of-the-box with this repository layout:
- source markdown from
./book - write build artifacts to
./dist - use repository-provided templates and CSS
- disable all uploads by default for safe local runs
- use
scpfor SSH uploads unless explicitly changed - keep print-ready mode disabled unless explicitly enabled
Graceful errors
The package intentionally fails with direct explanations when:
- input/template/css files are not readable,
- no markdown files are found,
- an upload integration is enabled but its host is unreachable,
- reMarkable upload is enabled but folder UUID is missing,
- generic SSH upload is enabled but target settings are missing,
- no PDFs are available for upload,
- Tailwind build command fails.
