@chromamark/cli 0.2.0 → 0.3.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/LICENSE.md CHANGED
@@ -1,15 +1,9 @@
1
- Copyright 2026 CJ Fravel
1
+ # MIT License
2
2
 
3
- Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
3
+ Copyright (c) 2026 CJ Fravel
4
4
 
5
- 1. The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6
6
 
7
- 2. **SaaS Provision**: If you use this software to provide a Software-as-a-Service (SaaS) offering, you must provide access to the source code of the modified version of the Software that you are using in your service, under the terms of this license. The access must be provided within a reasonable time, but no later than 30 days, and the access method should be publicly documented.
7
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8
8
 
9
- THE SOFTWARE IS PROVIDED AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
10
-
11
- ---
12
-
13
- ## Third-party components
14
-
15
- The published browser bundles in `packages/renderer/dist/` and the bundled VS Code extension embed [markdown-it](https://github.com/markdown-it/markdown-it), which is licensed under the [MIT License](https://github.com/markdown-it/markdown-it/blob/master/LICENSE).
9
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md CHANGED
@@ -2,8 +2,8 @@
2
2
 
3
3
  Compile [ChromaMark](https://github.com/cjfravel-dev/ChromaMark) (`.cm`) files
4
4
  into **self-contained HTML** — the theme is inlined, so there's no CDN or build
5
- step to deploy — render them straight to a **color terminal**, or **lint** them
6
- in CI.
5
+ step to deploy — transpile them to **GitHub-native GFM**, render them straight
6
+ to a **color terminal**, or **lint** them in CI.
7
7
 
8
8
  ## Install
9
9
 
@@ -27,6 +27,10 @@ chromamark render report.cm # render to the terminal (ANSI)
27
27
  cat report.cm | chromamark render # render piped stdin
28
28
  chromamark render report.cm --no-color # plain, icon-annotated text
29
29
 
30
+ chromamark github report.cm # GitHub-native GFM on stdout
31
+ chromamark github report.cm -o report.md # write GitHub-native GFM
32
+ cat report.cm | chromamark github # render piped stdin
33
+
30
34
  chromamark lint report.cm # check for common mistakes (CI)
31
35
  chromamark lint report.cm --disable CM001 # suppress a rule
32
36
  ```
@@ -35,11 +39,18 @@ Options: `-o/--output`, `--stdout`, `--title <text>`, `--watch`,
35
39
  `--color <auto|always|never>`, `--no-color`, `--disable <rules>`, `-h/--help`,
36
40
  `-v/--version`.
37
41
 
42
+ Commands accept one input source. Extra positional arguments, including a second
43
+ `-` stdin marker, are rejected instead of being silently ignored.
44
+
38
45
  The `render` command turns tones into terminal colors, pills into bracketed
39
46
  icon chips (`[✓ PASS]`), blocks into a colored left bar, and meters into a
40
47
  unicode bar. Color is automatic on a TTY and can be forced with `--color`; it
41
48
  also honors the [`NO_COLOR`](https://no-color.org) convention.
42
49
 
50
+ The `github` command maps callouts to GitHub Alerts, details and tables to native
51
+ GitHub structures, and pills to tone-aware `<kbd>` badges. See the complete
52
+ [mapping contract](../../docs/github-export.md).
53
+
43
54
  The `lint` command flags mistakes that otherwise degrade silently — a construct
44
55
  wrapped in backticks (`CM001`), an unknown tone (`CM002`), an unknown block kind
45
56
  (`CM003`), a bad meter value (`CM004`), or an unclosed container (`CM005`) — and
@@ -48,15 +59,20 @@ exits non-zero when any are found. Suppress rules with `--disable CM001,CM003`.
48
59
  ## Programmatic
49
60
 
50
61
  ```js
51
- import { compile, render, renderAnsi, lint } from '@chromamark/cli';
62
+ import { compile, render, renderAnsi, renderGitHub, lint } from '@chromamark/cli';
52
63
 
53
64
  const page = compile('::: success\nAll good [!pass]\n:::', { title: 'Report' });
54
65
  // → complete <!DOCTYPE html> document with the theme inlined
55
66
  const fragment = render('[!pass]'); // just the HTML fragment
56
67
  const ansi = renderAnsi('[!pass]', { color: 'always' }); // terminal-styled text
68
+ const github = renderGitHub('[!pass]'); // ✅ <kbd>PASS</kbd>
57
69
  const problems = lint('Use `[!pass]` here'); // [{ line, column, rule, ... }]
58
70
  ```
59
71
 
72
+ TypeScript declarations ship with the package.
73
+
60
74
  ## License
61
75
 
62
- Modified MIT License with a SaaS source-availability provision see LICENSE.md.
76
+ This software package is licensed under the MIT License; see LICENSE.md. The
77
+ [ChromaMark specification](https://github.com/cjfravel-dev/ChromaMark/blob/main/LICENSE-SPEC.md)
78
+ is licensed separately under CC BY-SA 4.0.
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@chromamark/cli",
3
- "version": "0.2.0",
4
- "description": "ChromaMark command-line tool — compile .cm to self-contained HTML, render to a color terminal, or lint.",
5
- "license": "SEE LICENSE IN LICENSE.md",
3
+ "version": "0.3.0",
4
+ "description": "ChromaMark command-line tool — compile .cm to HTML or GitHub GFM, render to a color terminal, or lint.",
5
+ "license": "MIT",
6
6
  "author": "cjfravel-dev",
7
7
  "homepage": "https://github.com/cjfravel-dev/ChromaMark#readme",
8
8
  "repository": {
@@ -24,8 +24,12 @@
24
24
  "chromamark": "./bin/chromamark.js"
25
25
  },
26
26
  "main": "./src/index.js",
27
+ "types": "./src/index.d.ts",
27
28
  "exports": {
28
- ".": "./src/index.js"
29
+ ".": {
30
+ "types": "./src/index.d.ts",
31
+ "default": "./src/index.js"
32
+ }
29
33
  },
30
34
  "files": [
31
35
  "bin/",
@@ -39,9 +43,10 @@
39
43
  "markdown",
40
44
  "cli",
41
45
  "static-site",
42
- "html"
46
+ "html",
47
+ "github"
43
48
  ],
44
49
  "dependencies": {
45
- "@chromamark/renderer": "^0.3.0"
50
+ "@chromamark/renderer": "^0.4.0"
46
51
  }
47
52
  }
package/src/cli.js CHANGED
@@ -8,21 +8,25 @@ import {
8
8
  } from 'node:fs';
9
9
  import { join, basename, extname, dirname, relative } from 'node:path';
10
10
  import { fileURLToPath } from 'node:url';
11
- import { compile, renderAnsi, lint } from './index.js';
11
+ import {
12
+ compile, renderAnsi, renderGitHub, lint,
13
+ } from './index.js';
12
14
 
13
- const HELP = `chromamark — compile ChromaMark (.cm) to HTML, render it, or lint it
15
+ const HELP = `chromamark — compile ChromaMark (.cm) to HTML, GitHub GFM, ANSI, or lint it
14
16
 
15
17
  Usage:
16
18
  chromamark [build] <input.cm> [-o <output.html>] compile one file to HTML
17
19
  chromamark [build] <dir> -o <outdir> compile every .cm in a tree
18
20
  chromamark render <input.cm> render to ANSI on stdout
21
+ chromamark github <input.cm> [-o <output.md>] render to GitHub-native GFM
19
22
  chromamark lint <input.cm> check for common mistakes
20
23
  cat file.cm | chromamark render render stdin to ANSI
24
+ cat file.cm | chromamark github render stdin to GitHub GFM
21
25
  chromamark <input.cm> --stdout write HTML to stdout
22
26
  cat file.cm | chromamark - read HTML from stdin
23
27
 
24
28
  Options:
25
- -o, --output <path> output file or directory (build)
29
+ -o, --output <path> output file or directory (build/github)
26
30
  --stdout write HTML to stdout instead of a file
27
31
  --title <text> page title (default: derived from the file name)
28
32
  --color <when> colorize render output: auto (default), always, never
@@ -33,7 +37,7 @@ Options:
33
37
  -v, --version print the version
34
38
  `;
35
39
 
36
- const COMMANDS = new Set(['build', 'render', 'lint']);
40
+ const COMMANDS = new Set(['build', 'render', 'github', 'lint']);
37
41
 
38
42
  /**
39
43
  * Read all of stdin synchronously. Unlike readFileSync(0), this tolerates a
@@ -66,7 +70,8 @@ function version() {
66
70
 
67
71
  function parseArgs(argv) {
68
72
  const opts = {
69
- command: 'build', input: null, output: null, stdout: false, title: null, watch: false, color: 'auto', disable: [],
73
+ command: 'build', input: null, output: null, stdout: false, title: null, watch: false,
74
+ color: 'auto', colorSpecified: false, disable: [],
70
75
  };
71
76
  let rest = argv;
72
77
  if (argv.length && COMMANDS.has(argv[0])) {
@@ -87,18 +92,24 @@ function parseArgs(argv) {
87
92
  else if (a === '-v' || a === '--version') opts.version = true;
88
93
  else if (a === '--stdout') opts.stdout = true;
89
94
  else if (a === '--watch') opts.watch = true;
90
- else if (a === '--no-color') opts.color = 'never';
95
+ else if (a === '--no-color') {
96
+ opts.color = 'never';
97
+ opts.colorSpecified = true;
98
+ }
91
99
  else if (a === '--color') {
92
100
  const v = takeValue(a);
93
101
  if (!['auto', 'always', 'never'].includes(v)) throw new Error('--color must be auto, always, or never');
94
102
  opts.color = v;
103
+ opts.colorSpecified = true;
95
104
  } else if (a === '--disable') {
96
105
  opts.disable = opts.disable.concat(takeValue(a).split(',').map((s) => s.trim()).filter(Boolean));
97
106
  } else if (a === '-o' || a === '--output') opts.output = takeValue(a);
98
107
  else if (a === '--title') opts.title = takeValue(a);
99
- else if (a === '-') opts.input = '-';
108
+ else if (a === '-' && opts.input == null) opts.input = '-';
109
+ else if (a === '-') throw new Error('unexpected argument: -');
100
110
  else if (a.startsWith('-')) throw new Error(`unknown option: ${a}`);
101
111
  else if (opts.input == null) opts.input = a;
112
+ else throw new Error(`unexpected argument: ${a}`);
102
113
  }
103
114
  return opts;
104
115
  }
@@ -136,6 +147,44 @@ function runRender(opts) {
136
147
  return 0;
137
148
  }
138
149
 
150
+ /** Render ChromaMark to GitHub-native GFM, from a file, `-`, or piped stdin. */
151
+ function runGitHub(opts) {
152
+ const unsupported = [];
153
+ if (opts.watch) unsupported.push('--watch');
154
+ if (opts.title !== null) unsupported.push('--title');
155
+ if (opts.colorSpecified) unsupported.push('--color');
156
+ if (opts.disable.length) unsupported.push('--disable');
157
+ if (unsupported.length) {
158
+ process.stderr.write(`error: ${unsupported.join(', ')} not supported by the github command\n`);
159
+ return 1;
160
+ }
161
+
162
+ let input;
163
+ try {
164
+ input = readInput(opts);
165
+ } catch (err) {
166
+ process.stderr.write(`error: ${err.message}\n`);
167
+ return 1;
168
+ }
169
+ if (!input || input.empty) {
170
+ process.stderr.write(`error: github needs an input file or piped stdin\n\n${HELP}`);
171
+ return 1;
172
+ }
173
+ const output = renderGitHub(input.src);
174
+ try {
175
+ if (opts.output && !opts.stdout) {
176
+ mkdirSync(dirname(opts.output), { recursive: true });
177
+ writeFileSync(opts.output, output);
178
+ } else {
179
+ process.stdout.write(output);
180
+ }
181
+ } catch (err) {
182
+ process.stderr.write(`error: ${err.message}\n`);
183
+ return 1;
184
+ }
185
+ return 0;
186
+ }
187
+
139
188
  /** Lint ChromaMark and print diagnostics; exit non-zero when any are found. */
140
189
  function runLint(opts) {
141
190
  let input;
@@ -211,6 +260,9 @@ export function run(argv = process.argv.slice(2)) {
211
260
  if (opts.command === 'render') {
212
261
  return runRender(opts);
213
262
  }
263
+ if (opts.command === 'github') {
264
+ return runGitHub(opts);
265
+ }
214
266
  if (opts.command === 'lint') {
215
267
  return runLint(opts);
216
268
  }
@@ -234,23 +286,38 @@ export function run(argv = process.argv.slice(2)) {
234
286
 
235
287
  try {
236
288
  info = statSync(opts.input);
237
- rebuild();
238
289
  } catch (err) {
239
290
  process.stderr.write(`error: ${err.message}\n`);
240
291
  return 1;
241
292
  }
242
293
 
243
294
  if (opts.watch && !opts.stdout) {
244
- process.stderr.write('watching for changes… (Ctrl+C to stop)\n');
245
- const onChange = () => {
295
+ const onChange = (_eventType, filename) => {
296
+ if (info.isDirectory() && filename && extname(String(filename)) !== '.cm') return;
246
297
  try { rebuild(); } catch (err) { process.stderr.write(`error: ${err.message}\n`); }
247
298
  };
299
+ let watcher;
248
300
  try {
249
- watch(opts.input, { recursive: info.isDirectory() }, onChange);
250
- } catch {
251
- watch(opts.input, onChange); // non-recursive fallback (older Node / platforms)
301
+ try {
302
+ watcher = watch(opts.input, { recursive: info.isDirectory() }, onChange);
303
+ } catch {
304
+ watcher = watch(opts.input, onChange); // non-recursive fallback (older Node / platforms)
305
+ }
306
+ process.stderr.write('watching for changes… (Ctrl+C to stop)\n');
307
+ rebuild();
308
+ } catch (err) {
309
+ if (watcher) watcher.close();
310
+ process.stderr.write(`error: ${err.message}\n`);
311
+ return 1;
252
312
  }
253
313
  return 0;
254
314
  }
315
+
316
+ try {
317
+ rebuild();
318
+ } catch (err) {
319
+ process.stderr.write(`error: ${err.message}\n`);
320
+ return 1;
321
+ }
255
322
  return 0;
256
323
  }
package/src/index.d.ts ADDED
@@ -0,0 +1,28 @@
1
+ import type {
2
+ LintDiagnostic,
3
+ LintOptions,
4
+ RendererOptions,
5
+ RenderAnsiOptions,
6
+ RenderGitHubOptions,
7
+ } from '@chromamark/renderer';
8
+
9
+ export interface CompileOptions {
10
+ title?: string;
11
+ theme?: string;
12
+ rendererOptions?: RendererOptions;
13
+ }
14
+
15
+ export function render(source: unknown, options?: RendererOptions): string;
16
+ export function renderAnsi(source: unknown, options?: RenderAnsiOptions): string;
17
+ export function renderGitHub(source: unknown, options?: RenderGitHubOptions): string;
18
+ export function lint(source: unknown, options?: LintOptions): LintDiagnostic[];
19
+ export function theme(): string;
20
+ export function compile(source: unknown, options?: CompileOptions): string;
21
+
22
+ export type {
23
+ LintDiagnostic,
24
+ LintOptions,
25
+ RendererOptions,
26
+ RenderAnsiOptions,
27
+ RenderGitHubOptions,
28
+ };
package/src/index.js CHANGED
@@ -5,7 +5,9 @@
5
5
 
6
6
  import { readFileSync } from 'node:fs';
7
7
  import { createRequire } from 'node:module';
8
- import { render as renderFragment, renderAnsi, lint } from '@chromamark/renderer';
8
+ import {
9
+ render as renderFragment, renderAnsi, renderGitHub, lint,
10
+ } from '@chromamark/renderer';
9
11
 
10
12
  const require = createRequire(import.meta.url);
11
13
 
@@ -15,6 +17,9 @@ export const render = renderFragment;
15
17
  /** Render ChromaMark to ANSI-styled text for a terminal. */
16
18
  export { renderAnsi };
17
19
 
20
+ /** Render ChromaMark to GitHub-native GFM. */
21
+ export { renderGitHub };
22
+
18
23
  /** Lint ChromaMark source, returning an array of diagnostics. */
19
24
  export { lint };
20
25