towel-txt 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +66 -0
- package/LICENSE +21 -0
- package/README.md +334 -0
- package/dist/cli/args.d.ts +45 -0
- package/dist/cli/args.d.ts.map +1 -0
- package/dist/cli/args.js +208 -0
- package/dist/cli/assets.d.ts +15 -0
- package/dist/cli/assets.d.ts.map +1 -0
- package/dist/cli/assets.js +50 -0
- package/dist/cli/config.d.ts +29 -0
- package/dist/cli/config.d.ts.map +1 -0
- package/dist/cli/config.js +200 -0
- package/dist/cli/exit-codes.d.ts +8 -0
- package/dist/cli/exit-codes.d.ts.map +1 -0
- package/dist/cli/exit-codes.js +6 -0
- package/dist/cli/pdf.d.ts +9 -0
- package/dist/cli/pdf.d.ts.map +1 -0
- package/dist/cli/pdf.js +164 -0
- package/dist/cli/run.d.ts +16 -0
- package/dist/cli/run.d.ts.map +1 -0
- package/dist/cli/run.js +414 -0
- package/dist/cli/summary.d.ts +17 -0
- package/dist/cli/summary.d.ts.map +1 -0
- package/dist/cli/summary.js +20 -0
- package/dist/cli/watch.d.ts +8 -0
- package/dist/cli/watch.d.ts.map +1 -0
- package/dist/cli/watch.js +59 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +3 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +16 -0
- package/dist/meta.d.ts +3 -0
- package/dist/meta.d.ts.map +1 -0
- package/dist/meta.js +2 -0
- package/dist/parser/footnotes.d.ts +24 -0
- package/dist/parser/footnotes.d.ts.map +1 -0
- package/dist/parser/footnotes.js +159 -0
- package/dist/parser/headings.d.ts +8 -0
- package/dist/parser/headings.d.ts.map +1 -0
- package/dist/parser/headings.js +95 -0
- package/dist/parser/highlight.d.ts +2 -0
- package/dist/parser/highlight.d.ts.map +1 -0
- package/dist/parser/highlight.js +81 -0
- package/dist/parser/images.d.ts +8 -0
- package/dist/parser/images.d.ts.map +1 -0
- package/dist/parser/images.js +62 -0
- package/dist/parser/markdown.d.ts +12 -0
- package/dist/parser/markdown.d.ts.map +1 -0
- package/dist/parser/markdown.js +60 -0
- package/dist/parser/metadata.d.ts +18 -0
- package/dist/parser/metadata.d.ts.map +1 -0
- package/dist/parser/metadata.js +95 -0
- package/dist/parser/page-breaks.d.ts +3 -0
- package/dist/parser/page-breaks.d.ts.map +1 -0
- package/dist/parser/page-breaks.js +21 -0
- package/dist/render/document.d.ts +13 -0
- package/dist/render/document.d.ts.map +1 -0
- package/dist/render/document.js +103 -0
- package/dist/render/minify.d.ts +2 -0
- package/dist/render/minify.d.ts.map +1 -0
- package/dist/render/minify.js +21 -0
- package/dist/render/print-options.d.ts +6 -0
- package/dist/render/print-options.d.ts.map +1 -0
- package/dist/render/print-options.js +24 -0
- package/dist/render/toc.d.ts +8 -0
- package/dist/render/toc.d.ts.map +1 -0
- package/dist/render/toc.js +58 -0
- package/dist/theme/default.d.ts +2 -0
- package/dist/theme/default.d.ts.map +1 -0
- package/dist/theme/default.js +327 -0
- package/dist/theme/themes.d.ts +5 -0
- package/dist/theme/themes.d.ts.map +1 -0
- package/dist/theme/themes.js +97 -0
- package/dist/utils/ids.d.ts +3 -0
- package/dist/utils/ids.d.ts.map +1 -0
- package/dist/utils/ids.js +21 -0
- package/docs/cli-reference.md +222 -0
- package/docs/examples.md +63 -0
- package/docs/print-notes.md +11 -0
- package/docs/production-roadmap.md +159 -0
- package/docs/release.md +44 -0
- package/docs/security-and-limits.md +121 -0
- package/examples/brief.md +30 -0
- package/examples/image-workflow.md +26 -0
- package/examples/images/workflow.svg +43 -0
- package/examples/print.css +7 -0
- package/examples/report.css +22 -0
- package/examples/report.md +44 -0
- package/examples/sample.md +39 -0
- package/examples/technical-note.md +46 -0
- package/examples/towel-txt.config.yaml +7 -0
- package/package.json +85 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on Keep a Changelog, and this project uses Semantic
|
|
6
|
+
Versioning.
|
|
7
|
+
|
|
8
|
+
## Unreleased
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Release workflow serialization and timeout guardrails for safer publishing.
|
|
13
|
+
- Maintainer release preflight check for repository metadata, branch protection,
|
|
14
|
+
workflow health, npm package state, and publish credentials.
|
|
15
|
+
- Automated coverage for release preflight success and failure scenarios.
|
|
16
|
+
|
|
17
|
+
## 0.1.0 - 2026-05-30
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
- Initial open-source project scaffold.
|
|
22
|
+
- CLI file rendering from Markdown input to printable HTML output.
|
|
23
|
+
- Complete CLI reference for options, config fields, and common workflows.
|
|
24
|
+
- Custom CSS support for generated HTML documents.
|
|
25
|
+
- Dependabot update groups for npm dependencies and GitHub Actions.
|
|
26
|
+
- Documented the production readiness roadmap.
|
|
27
|
+
- Explicit page break markers and print break helper classes.
|
|
28
|
+
- Example Markdown document and generated printable HTML output.
|
|
29
|
+
- Example custom print CSS file.
|
|
30
|
+
- Front matter metadata parsing for document title, subtitle, author, date, and cover fields.
|
|
31
|
+
- GitHub Actions workflow dependencies updated to Node 24-compatible majors.
|
|
32
|
+
- Local relative image asset copying with copied, skipped, and missing diagnostics.
|
|
33
|
+
- Local image paths with Windows separators are handled consistently across platforms.
|
|
34
|
+
- Local image asset output directories with generated image path rewriting.
|
|
35
|
+
- Minor and patch Dependabot grouping with manual handling for major npm updates.
|
|
36
|
+
- Markdown heading extraction with stable heading IDs.
|
|
37
|
+
- Markdown body HTML rendering with safe raw HTML escaping.
|
|
38
|
+
- Named document themes: `default`, `compact`, and `report`.
|
|
39
|
+
- Optional cover pages from metadata, config, or CLI flags.
|
|
40
|
+
- Optional minified HTML output with `--minify` or config defaults.
|
|
41
|
+
- Optional table of contents suppression with `--no-toc`.
|
|
42
|
+
- Output overwrite protection with `--force`.
|
|
43
|
+
- Package metadata, exports, and publishable file list readiness checks.
|
|
44
|
+
- CodeQL code scanning for JavaScript and TypeScript security analysis.
|
|
45
|
+
- Package smoke testing for packed CLI installs.
|
|
46
|
+
- PDF export through a local Chrome, Edge, or Chromium browser.
|
|
47
|
+
- Production examples for reports, briefs, technical notes, images, and custom CSS.
|
|
48
|
+
- Print page size and margin options for generated HTML documents.
|
|
49
|
+
- Printable HTML document rendering with default screen and print styles.
|
|
50
|
+
- Project config files with `--config` and `--no-config`.
|
|
51
|
+
- Release checks for changelog, package metadata, and version consistency.
|
|
52
|
+
- Release workflow for verified package dry-runs and optional publishing.
|
|
53
|
+
- Release workflow publish prerequisite validation for npm credentials.
|
|
54
|
+
- Security audit and performance smoke gates in CI and release verification.
|
|
55
|
+
- Vitest upgraded to a patched 4.x release for GHSA-5xrq-8626-4rwp.
|
|
56
|
+
- Windows CI coverage for cross-platform CLI verification.
|
|
57
|
+
- Print-friendly footnotes with backlinks.
|
|
58
|
+
- Render summary JSON files for CI and script workflows.
|
|
59
|
+
- Security and limits documentation for rendering, image copying, CSS, and PDF export.
|
|
60
|
+
- Stable CLI exit codes for usage errors, render errors, and strict warning failures.
|
|
61
|
+
- Strict mode for failing renders when warnings are detected.
|
|
62
|
+
- Stdin Markdown input with `--stdin`.
|
|
63
|
+
- Stdout HTML output with `--stdout`.
|
|
64
|
+
- Syntax highlighting for JavaScript, TypeScript, JSON, and shell code fences.
|
|
65
|
+
- Table of contents tree building and HTML rendering.
|
|
66
|
+
- Watch mode with `--watch` for local Markdown and CSS rebuilds.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Towel.txt contributors
|
|
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.
|
package/README.md
ADDED
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
# Towel.txt
|
|
2
|
+
|
|
3
|
+
Towel.txt is a small command-line tool for turning Markdown into clean,
|
|
4
|
+
printable HTML and PDF documents.
|
|
5
|
+
|
|
6
|
+
It is designed for notes, reports, project writeups, briefs, and other
|
|
7
|
+
documents that start as plain Markdown but need a polished browser-printable
|
|
8
|
+
output.
|
|
9
|
+
|
|
10
|
+
## Status
|
|
11
|
+
|
|
12
|
+
Towel.txt can currently generate self-contained printable HTML and PDF files
|
|
13
|
+
from Markdown. PDF export uses a local Chrome, Edge, or Chromium browser.
|
|
14
|
+
The first public package release is prepared as `0.1.0`; publishing requires
|
|
15
|
+
the maintainer release workflow and npm credentials.
|
|
16
|
+
|
|
17
|
+
## Goals
|
|
18
|
+
|
|
19
|
+
- Convert Markdown files into readable HTML documents.
|
|
20
|
+
- Generate a table of contents from document headings.
|
|
21
|
+
- Include print-friendly CSS by default.
|
|
22
|
+
- Highlight common code fences with print-friendly colors.
|
|
23
|
+
- Render footnotes with backlinks for longer documents.
|
|
24
|
+
- Add an optional cover page from document metadata.
|
|
25
|
+
- Insert explicit print page breaks when a document needs fixed pagination.
|
|
26
|
+
- Keep output simple enough to inspect, customize, and share.
|
|
27
|
+
- Provide a focused CLI that works well in scripts and local workflows.
|
|
28
|
+
|
|
29
|
+
## Install From Source
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
git clone https://github.com/kaleab-kali/towel.txt.git
|
|
33
|
+
cd towel.txt
|
|
34
|
+
pnpm install
|
|
35
|
+
pnpm build
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Usage
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
towel-txt document.md -o document.html
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
For every command option, config field, conflict rule, and common workflow, see
|
|
45
|
+
[docs/cli-reference.md](docs/cli-reference.md).
|
|
46
|
+
|
|
47
|
+
Append a custom CSS file to the built-in print styles:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
towel-txt document.md -o document.html --css examples/print.css
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Place copied local image assets under a specific output folder:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
towel-txt document.md -o dist/document.html --asset-dir assets
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Generate compact HTML with layout whitespace removed:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
towel-txt document.md -o document.html --minify
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Write a JSON summary for CI or scripts:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
towel-txt document.md -o document.html --summary-json summary.json
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Fail the render when warnings are detected:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
towel-txt document.md -o document.html --strict
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Set print page size and margins:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
towel-txt document.md -o document.html --page-size "A4 landscape" --margin 18mm
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Generate a PDF:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
towel-txt document.md --format pdf -o document.pdf
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
The CLI also infers PDF output from a `.pdf` output path:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
towel-txt document.md -o document.pdf
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Use a specific browser executable for PDF export:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
towel-txt document.md --format pdf -o document.pdf --browser /path/to/chrome
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Select a built-in theme:
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
towel-txt document.md -o document.html --theme report
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Add a cover page for a rendered document:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
towel-txt document.md -o document.html --cover --subtitle "Quarterly planning notes"
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Insert a print page break by placing a marker on its own line:
|
|
114
|
+
|
|
115
|
+
```md
|
|
116
|
+
First section.
|
|
117
|
+
|
|
118
|
+
[[page-break]]
|
|
119
|
+
|
|
120
|
+
Second section.
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Rebuild output when the Markdown or custom CSS file changes:
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
towel-txt document.md -o document.html --watch
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Load defaults from a config file:
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
towel-txt document.md --config towel-txt.config.yaml
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Overwrite an existing output file intentionally:
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
towel-txt document.md -o document.html --force
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Disable the automatic table of contents:
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
towel-txt document.md -o document.html --no-toc
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Write generated HTML to stdout for piping:
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
towel-txt document.md --stdout
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Read Markdown from stdin:
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
cat document.md | towel-txt --stdin --stdout
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
During local development, run the CLI through pnpm:
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
pnpm dev examples/sample.md --output examples/sample.html
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
The CLI supports:
|
|
166
|
+
|
|
167
|
+
- Markdown input files.
|
|
168
|
+
- HTML output files.
|
|
169
|
+
- PDF output files through Chrome, Edge, or Chromium.
|
|
170
|
+
- Configurable copied asset output directories with `--asset-dir`.
|
|
171
|
+
- Syntax highlighting for JavaScript, TypeScript, JSON, and shell code fences.
|
|
172
|
+
- Footnotes using `[^label]` references and matching definitions.
|
|
173
|
+
- Optional cover pages from metadata, config, or `--cover`.
|
|
174
|
+
- Explicit page break markers: `[[page-break]]`, `\pagebreak`, and `\newpage`.
|
|
175
|
+
- Document title configuration.
|
|
176
|
+
- Front matter metadata for title, subtitle, author, date, and cover pages.
|
|
177
|
+
- Custom CSS appended to the default document styles.
|
|
178
|
+
- Heading anchors.
|
|
179
|
+
- Named themes: `default`, `compact`, and `report`.
|
|
180
|
+
- Output overwrite protection with an explicit `--force` option.
|
|
181
|
+
- Optional minified HTML output with `--minify`.
|
|
182
|
+
- Print page size and margin configuration.
|
|
183
|
+
- Relative local image asset copying with copied, skipped, and missing diagnostics.
|
|
184
|
+
- Table of contents generation.
|
|
185
|
+
- Optional table of contents suppression.
|
|
186
|
+
- Project config files for shared defaults.
|
|
187
|
+
- Render summary JSON files for CI and scripts.
|
|
188
|
+
- Watch mode for rebuilding file output during local editing.
|
|
189
|
+
- Stdout output for shell pipelines.
|
|
190
|
+
- Stdin input for shell pipelines.
|
|
191
|
+
- Strict mode for CI workflows that should fail on warnings.
|
|
192
|
+
- Stable CLI exit codes for scripts.
|
|
193
|
+
- Default screen and print styles.
|
|
194
|
+
|
|
195
|
+
## Metadata
|
|
196
|
+
|
|
197
|
+
Documents can include YAML front matter at the top of the Markdown file:
|
|
198
|
+
|
|
199
|
+
```md
|
|
200
|
+
---
|
|
201
|
+
title: Project Brief
|
|
202
|
+
subtitle: Quarterly planning notes
|
|
203
|
+
author: Kaleab
|
|
204
|
+
date: 2026-05-27
|
|
205
|
+
cover: true
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
# Project Brief
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
The `title` field controls the generated HTML document title unless `--title` is
|
|
212
|
+
provided on the CLI. Set `cover: true` to render a cover page before the table of
|
|
213
|
+
contents and document body. Use `--no-cover` to disable a metadata or config
|
|
214
|
+
cover page for one command.
|
|
215
|
+
|
|
216
|
+
## Footnotes
|
|
217
|
+
|
|
218
|
+
Use `[^label]` references in the document body and matching definitions later in
|
|
219
|
+
the Markdown file:
|
|
220
|
+
|
|
221
|
+
```md
|
|
222
|
+
Detailed context can live in a note.[^context]
|
|
223
|
+
|
|
224
|
+
[^context]: Footnotes support Markdown such as **strong text** and links.
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
Generated footnotes are rendered at the end of the document with backlinks to
|
|
228
|
+
their references.
|
|
229
|
+
|
|
230
|
+
## Page Breaks
|
|
231
|
+
|
|
232
|
+
Use `[[page-break]]`, `\pagebreak`, or `\newpage` on its own line to insert an
|
|
233
|
+
explicit print break. The generated CSS also includes `.break-before-page`,
|
|
234
|
+
`.break-after-page`, and `.avoid-page-break` helpers for custom styles.
|
|
235
|
+
|
|
236
|
+
## Configuration
|
|
237
|
+
|
|
238
|
+
Towel.txt automatically looks for `towel-txt.config.yaml`,
|
|
239
|
+
`towel-txt.config.yml`, or `towel-txt.config.json` in the current working
|
|
240
|
+
directory. Use `--config <path>` to load a specific config file, or
|
|
241
|
+
`--no-config` to disable discovery.
|
|
242
|
+
|
|
243
|
+
```yaml
|
|
244
|
+
output: dist/document.html
|
|
245
|
+
assetDir: assets
|
|
246
|
+
css: examples/print.css
|
|
247
|
+
format: html
|
|
248
|
+
theme: report
|
|
249
|
+
title: Project Brief
|
|
250
|
+
subtitle: Quarterly planning notes
|
|
251
|
+
cover: true
|
|
252
|
+
pageSize: A4
|
|
253
|
+
margin: 18mm
|
|
254
|
+
minify: false
|
|
255
|
+
strict: false
|
|
256
|
+
summaryJson: dist/summary.json
|
|
257
|
+
tableOfContents: true
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
CLI flags override config defaults. Use `--toc` when a config file disables the
|
|
261
|
+
table of contents and one command needs it enabled.
|
|
262
|
+
|
|
263
|
+
Supported config fields are `output`, `assetDir`, `css`, `format`, `theme`,
|
|
264
|
+
`title`, `subtitle`, `cover`, `pageSize`, `margin`, `minify`,
|
|
265
|
+
`strict`, `summaryJson`, `tableOfContents`, and `browser`.
|
|
266
|
+
|
|
267
|
+
## Strict Mode
|
|
268
|
+
|
|
269
|
+
Use `--strict` to fail the command when warnings are detected. Strict mode
|
|
270
|
+
currently fails on missing or skipped image assets and front matter metadata
|
|
271
|
+
that would otherwise be ignored, such as unsupported fields or invalid value
|
|
272
|
+
types. Use `--no-strict` to disable a config default for one command.
|
|
273
|
+
|
|
274
|
+
## JSON Summary
|
|
275
|
+
|
|
276
|
+
Use `--summary-json <path>` to write a machine-readable summary file after a
|
|
277
|
+
successful render. The summary includes the output format, absolute input and
|
|
278
|
+
output paths, bytes written, minify status, copied image results, and warning
|
|
279
|
+
messages.
|
|
280
|
+
|
|
281
|
+
## Example
|
|
282
|
+
|
|
283
|
+
See [examples/sample.md](examples/sample.md) and the generated
|
|
284
|
+
[examples/sample.html](examples/sample.html).
|
|
285
|
+
|
|
286
|
+
See [examples/print.css](examples/print.css) for a small custom CSS example.
|
|
287
|
+
See [examples/towel-txt.config.yaml](examples/towel-txt.config.yaml) for a
|
|
288
|
+
small config example.
|
|
289
|
+
See [docs/examples.md](docs/examples.md) for report, brief, technical note,
|
|
290
|
+
image, and custom CSS workflows.
|
|
291
|
+
See [docs/security-and-limits.md](docs/security-and-limits.md) for renderer
|
|
292
|
+
security behavior and operational limits.
|
|
293
|
+
|
|
294
|
+
The generated HTML is self-contained, so it can be opened directly in a browser
|
|
295
|
+
and printed from the browser print dialog.
|
|
296
|
+
|
|
297
|
+
When output is written to a different directory, safe relative Markdown image
|
|
298
|
+
paths such as `images/diagram.png` are copied beside the generated HTML output.
|
|
299
|
+
The CLI reports copied images, skipped remote or unsafe sources, and missing
|
|
300
|
+
local files. Use `--asset-dir <dir>` or config `assetDir` to place copied assets
|
|
301
|
+
under a dedicated output folder and rewrite the generated image paths.
|
|
302
|
+
|
|
303
|
+
PDF output uses browser print automation. Install Chrome, Edge, or Chromium, or
|
|
304
|
+
pass `--browser <path>` if the executable is not on the default search path.
|
|
305
|
+
|
|
306
|
+
## Development
|
|
307
|
+
|
|
308
|
+
This repository uses Node.js, TypeScript, and pnpm.
|
|
309
|
+
|
|
310
|
+
```bash
|
|
311
|
+
pnpm install
|
|
312
|
+
pnpm release:check
|
|
313
|
+
pnpm lint
|
|
314
|
+
pnpm security:audit
|
|
315
|
+
pnpm typecheck
|
|
316
|
+
pnpm test
|
|
317
|
+
pnpm build
|
|
318
|
+
pnpm perf:smoke
|
|
319
|
+
pnpm smoke:package
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
## Roadmap
|
|
323
|
+
|
|
324
|
+
See [docs/production-roadmap.md](docs/production-roadmap.md) for the production
|
|
325
|
+
readiness backlog.
|
|
326
|
+
|
|
327
|
+
## Contributing
|
|
328
|
+
|
|
329
|
+
Contributions should be small, focused, and easy to review. See
|
|
330
|
+
[CONTRIBUTING.md](CONTRIBUTING.md) for local setup and pull request standards.
|
|
331
|
+
|
|
332
|
+
## License
|
|
333
|
+
|
|
334
|
+
MIT
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { type ThemeName } from "../theme/themes.js";
|
|
2
|
+
import { type CliExitCode } from "./exit-codes.js";
|
|
3
|
+
export type OutputFormat = "html" | "pdf";
|
|
4
|
+
export type CliCommand = {
|
|
5
|
+
kind: "help";
|
|
6
|
+
} | {
|
|
7
|
+
browserPath?: string;
|
|
8
|
+
assetDirectory?: string;
|
|
9
|
+
configPath?: string;
|
|
10
|
+
cover: boolean;
|
|
11
|
+
coverSpecified: boolean;
|
|
12
|
+
cssPath?: string;
|
|
13
|
+
force: boolean;
|
|
14
|
+
format?: OutputFormat;
|
|
15
|
+
inputPath?: string;
|
|
16
|
+
kind: "render";
|
|
17
|
+
margin?: string;
|
|
18
|
+
minify: boolean;
|
|
19
|
+
minifySpecified: boolean;
|
|
20
|
+
noConfig: boolean;
|
|
21
|
+
outputPath?: string;
|
|
22
|
+
pageSize?: string;
|
|
23
|
+
stdout: boolean;
|
|
24
|
+
stdin: boolean;
|
|
25
|
+
strict: boolean;
|
|
26
|
+
strictSpecified: boolean;
|
|
27
|
+
subtitle?: string;
|
|
28
|
+
summaryJsonPath?: string;
|
|
29
|
+
tableOfContents: boolean;
|
|
30
|
+
tableOfContentsSpecified: boolean;
|
|
31
|
+
theme?: ThemeName;
|
|
32
|
+
title?: string;
|
|
33
|
+
watch: boolean;
|
|
34
|
+
} | {
|
|
35
|
+
kind: "version";
|
|
36
|
+
};
|
|
37
|
+
export declare class CliUsageError extends Error {
|
|
38
|
+
readonly exitCode: CliExitCode;
|
|
39
|
+
constructor(message: string, exitCode?: CliExitCode);
|
|
40
|
+
}
|
|
41
|
+
export declare class CliStrictModeError extends CliUsageError {
|
|
42
|
+
constructor(message: string);
|
|
43
|
+
}
|
|
44
|
+
export declare function parseCliArgs(argv: string[]): CliCommand;
|
|
45
|
+
//# sourceMappingURL=args.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"args.d.ts","sourceRoot":"","sources":["../../src/cli/args.ts"],"names":[],"mappings":"AAEA,OAAO,EAAe,KAAK,SAAS,EAAE,MAAM,oBAAoB,CAAC;AACjE,OAAO,EAAgB,KAAK,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAEjE,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,KAAK,CAAC;AAE1C,MAAM,MAAM,UAAU,GAClB;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IACE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,OAAO,CAAC;IACf,cAAc,EAAE,OAAO,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,QAAQ,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,OAAO,CAAC;IAChB,eAAe,EAAE,OAAO,CAAC;IACzB,QAAQ,EAAE,OAAO,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,OAAO,CAAC;IAChB,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,OAAO,CAAC;IAChB,eAAe,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,eAAe,EAAE,OAAO,CAAC;IACzB,wBAAwB,EAAE,OAAO,CAAC;IAClC,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,OAAO,CAAC;CAChB,GACD;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,CAAC;AAExB,qBAAa,aAAc,SAAQ,KAAK;IACtC,QAAQ,CAAC,QAAQ,EAAE,WAAW,CAAC;gBAEnB,OAAO,EAAE,MAAM,EAAE,QAAQ,GAAE,WAAqC;CAK7E;AAED,qBAAa,kBAAmB,SAAQ,aAAa;gBACvC,OAAO,EAAE,MAAM;CAI5B;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,UAAU,CA+JvD"}
|
package/dist/cli/args.js
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import { parseArgs } from "node:util";
|
|
2
|
+
import { isThemeName } from "../theme/themes.js";
|
|
3
|
+
import { cliExitCodes } from "./exit-codes.js";
|
|
4
|
+
export class CliUsageError extends Error {
|
|
5
|
+
exitCode;
|
|
6
|
+
constructor(message, exitCode = cliExitCodes.usageError) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.name = "CliUsageError";
|
|
9
|
+
this.exitCode = exitCode;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
export class CliStrictModeError extends CliUsageError {
|
|
13
|
+
constructor(message) {
|
|
14
|
+
super(message, cliExitCodes.strictWarnings);
|
|
15
|
+
this.name = "CliStrictModeError";
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export function parseCliArgs(argv) {
|
|
19
|
+
let parsed;
|
|
20
|
+
try {
|
|
21
|
+
parsed = parseArgs({
|
|
22
|
+
allowPositionals: true,
|
|
23
|
+
args: argv,
|
|
24
|
+
options: {
|
|
25
|
+
browser: {
|
|
26
|
+
type: "string"
|
|
27
|
+
},
|
|
28
|
+
"asset-dir": {
|
|
29
|
+
type: "string"
|
|
30
|
+
},
|
|
31
|
+
config: {
|
|
32
|
+
type: "string"
|
|
33
|
+
},
|
|
34
|
+
cover: {
|
|
35
|
+
type: "boolean"
|
|
36
|
+
},
|
|
37
|
+
help: {
|
|
38
|
+
short: "h",
|
|
39
|
+
type: "boolean"
|
|
40
|
+
},
|
|
41
|
+
css: {
|
|
42
|
+
type: "string"
|
|
43
|
+
},
|
|
44
|
+
force: {
|
|
45
|
+
type: "boolean"
|
|
46
|
+
},
|
|
47
|
+
format: {
|
|
48
|
+
type: "string"
|
|
49
|
+
},
|
|
50
|
+
output: {
|
|
51
|
+
short: "o",
|
|
52
|
+
type: "string"
|
|
53
|
+
},
|
|
54
|
+
margin: {
|
|
55
|
+
type: "string"
|
|
56
|
+
},
|
|
57
|
+
minify: {
|
|
58
|
+
type: "boolean"
|
|
59
|
+
},
|
|
60
|
+
"page-size": {
|
|
61
|
+
type: "string"
|
|
62
|
+
},
|
|
63
|
+
"no-toc": {
|
|
64
|
+
type: "boolean"
|
|
65
|
+
},
|
|
66
|
+
"no-config": {
|
|
67
|
+
type: "boolean"
|
|
68
|
+
},
|
|
69
|
+
"no-cover": {
|
|
70
|
+
type: "boolean"
|
|
71
|
+
},
|
|
72
|
+
"no-minify": {
|
|
73
|
+
type: "boolean"
|
|
74
|
+
},
|
|
75
|
+
"no-strict": {
|
|
76
|
+
type: "boolean"
|
|
77
|
+
},
|
|
78
|
+
stdout: {
|
|
79
|
+
type: "boolean"
|
|
80
|
+
},
|
|
81
|
+
stdin: {
|
|
82
|
+
type: "boolean"
|
|
83
|
+
},
|
|
84
|
+
strict: {
|
|
85
|
+
type: "boolean"
|
|
86
|
+
},
|
|
87
|
+
subtitle: {
|
|
88
|
+
type: "string"
|
|
89
|
+
},
|
|
90
|
+
"summary-json": {
|
|
91
|
+
type: "string"
|
|
92
|
+
},
|
|
93
|
+
title: {
|
|
94
|
+
type: "string"
|
|
95
|
+
},
|
|
96
|
+
theme: {
|
|
97
|
+
type: "string"
|
|
98
|
+
},
|
|
99
|
+
toc: {
|
|
100
|
+
type: "boolean"
|
|
101
|
+
},
|
|
102
|
+
version: {
|
|
103
|
+
type: "boolean"
|
|
104
|
+
},
|
|
105
|
+
watch: {
|
|
106
|
+
type: "boolean"
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
strict: true
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
catch (error) {
|
|
113
|
+
throw new CliUsageError(error instanceof Error ? error.message : "Invalid arguments.");
|
|
114
|
+
}
|
|
115
|
+
if (parsed.values.help) {
|
|
116
|
+
return { kind: "help" };
|
|
117
|
+
}
|
|
118
|
+
if (parsed.values.version) {
|
|
119
|
+
return { kind: "version" };
|
|
120
|
+
}
|
|
121
|
+
if (parsed.values.toc === true && parsed.values["no-toc"] === true) {
|
|
122
|
+
throw new CliUsageError("Do not pass --toc with --no-toc.");
|
|
123
|
+
}
|
|
124
|
+
if (parsed.values.cover === true && parsed.values["no-cover"] === true) {
|
|
125
|
+
throw new CliUsageError("Do not pass --cover with --no-cover.");
|
|
126
|
+
}
|
|
127
|
+
if (parsed.values.minify === true && parsed.values["no-minify"] === true) {
|
|
128
|
+
throw new CliUsageError("Do not pass --minify with --no-minify.");
|
|
129
|
+
}
|
|
130
|
+
if (parsed.values.strict === true && parsed.values["no-strict"] === true) {
|
|
131
|
+
throw new CliUsageError("Do not pass --strict with --no-strict.");
|
|
132
|
+
}
|
|
133
|
+
if (parsed.values.stdin === true && parsed.positionals.length > 0) {
|
|
134
|
+
throw new CliUsageError("Do not pass an input file when using --stdin.");
|
|
135
|
+
}
|
|
136
|
+
if (parsed.values.stdin !== true && parsed.positionals.length !== 1) {
|
|
137
|
+
throw new CliUsageError("Expected exactly one Markdown input file.");
|
|
138
|
+
}
|
|
139
|
+
return {
|
|
140
|
+
assetDirectory: getAssetDirectoryOption(parsed.values["asset-dir"], "--asset-dir"),
|
|
141
|
+
browserPath: getStringOption(parsed.values.browser),
|
|
142
|
+
configPath: getStringOption(parsed.values.config),
|
|
143
|
+
cover: parsed.values.cover === true && parsed.values["no-cover"] !== true,
|
|
144
|
+
coverSpecified: parsed.values.cover === true || parsed.values["no-cover"] === true,
|
|
145
|
+
cssPath: getStringOption(parsed.values.css),
|
|
146
|
+
force: parsed.values.force === true,
|
|
147
|
+
format: getOutputFormatOption(parsed.values.format),
|
|
148
|
+
...(parsed.positionals[0] ? { inputPath: parsed.positionals[0] } : {}),
|
|
149
|
+
kind: "render",
|
|
150
|
+
margin: getStringOption(parsed.values.margin),
|
|
151
|
+
minify: parsed.values.minify === true && parsed.values["no-minify"] !== true,
|
|
152
|
+
minifySpecified: parsed.values.minify === true || parsed.values["no-minify"] === true,
|
|
153
|
+
noConfig: parsed.values["no-config"] === true,
|
|
154
|
+
outputPath: getStringOption(parsed.values.output),
|
|
155
|
+
pageSize: getStringOption(parsed.values["page-size"]),
|
|
156
|
+
stdin: parsed.values.stdin === true,
|
|
157
|
+
stdout: parsed.values.stdout === true,
|
|
158
|
+
strict: parsed.values.strict === true && parsed.values["no-strict"] !== true,
|
|
159
|
+
strictSpecified: parsed.values.strict === true || parsed.values["no-strict"] === true,
|
|
160
|
+
subtitle: getStringOption(parsed.values.subtitle),
|
|
161
|
+
summaryJsonPath: getStringOption(parsed.values["summary-json"]),
|
|
162
|
+
tableOfContents: parsed.values["no-toc"] !== true,
|
|
163
|
+
tableOfContentsSpecified: parsed.values["no-toc"] === true || parsed.values.toc === true,
|
|
164
|
+
theme: getThemeOption(parsed.values.theme),
|
|
165
|
+
title: getStringOption(parsed.values.title),
|
|
166
|
+
watch: parsed.values.watch === true
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
function getThemeOption(value) {
|
|
170
|
+
if (value === undefined) {
|
|
171
|
+
return undefined;
|
|
172
|
+
}
|
|
173
|
+
if (isThemeName(value)) {
|
|
174
|
+
return value;
|
|
175
|
+
}
|
|
176
|
+
throw new CliUsageError('Expected --theme to be "default", "compact", or "report".');
|
|
177
|
+
}
|
|
178
|
+
function getStringOption(value) {
|
|
179
|
+
return typeof value === "string" ? value : undefined;
|
|
180
|
+
}
|
|
181
|
+
function getAssetDirectoryOption(value, optionName) {
|
|
182
|
+
const directory = getStringOption(value)?.trim();
|
|
183
|
+
if (!directory) {
|
|
184
|
+
return undefined;
|
|
185
|
+
}
|
|
186
|
+
if (directory.includes("?") ||
|
|
187
|
+
directory.includes("#") ||
|
|
188
|
+
/^[a-z][a-z0-9+.-]*:/iu.test(directory) ||
|
|
189
|
+
directory.startsWith("/") ||
|
|
190
|
+
directory.startsWith("\\")) {
|
|
191
|
+
throw new CliUsageError(`Expected ${optionName} to be a safe relative directory.`);
|
|
192
|
+
}
|
|
193
|
+
const normalizedDirectory = directory.replace(/\\/g, "/");
|
|
194
|
+
const parts = normalizedDirectory.split("/");
|
|
195
|
+
if (parts.includes("..") || parts.includes("")) {
|
|
196
|
+
throw new CliUsageError(`Expected ${optionName} to be a safe relative directory.`);
|
|
197
|
+
}
|
|
198
|
+
return normalizedDirectory;
|
|
199
|
+
}
|
|
200
|
+
function getOutputFormatOption(value) {
|
|
201
|
+
if (value === undefined) {
|
|
202
|
+
return undefined;
|
|
203
|
+
}
|
|
204
|
+
if (value === "html" || value === "pdf") {
|
|
205
|
+
return value;
|
|
206
|
+
}
|
|
207
|
+
throw new CliUsageError('Expected --format to be "html" or "pdf".');
|
|
208
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface CopyImageAssetsOptions {
|
|
2
|
+
assetDirectory?: string;
|
|
3
|
+
inputPath: string;
|
|
4
|
+
markdown: string;
|
|
5
|
+
outputPath: string;
|
|
6
|
+
}
|
|
7
|
+
export interface ImageAssetCopyResult {
|
|
8
|
+
error?: string;
|
|
9
|
+
reason?: string;
|
|
10
|
+
source: string;
|
|
11
|
+
status: "copied" | "missing" | "skipped";
|
|
12
|
+
targetSource?: string;
|
|
13
|
+
}
|
|
14
|
+
export declare function copyLocalImageAssets(options: CopyImageAssetsOptions): Promise<ImageAssetCopyResult[]>;
|
|
15
|
+
//# sourceMappingURL=assets.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assets.d.ts","sourceRoot":"","sources":["../../src/cli/assets.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,sBAAsB;IACrC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,oBAAoB;IACnC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,QAAQ,GAAG,SAAS,GAAG,SAAS,CAAC;IACzC,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,sBAAsB,GAC9B,OAAO,CAAC,oBAAoB,EAAE,CAAC,CA6CjC"}
|