@bendyline/squisq-cli 2.3.2 → 2.4.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/README.md CHANGED
@@ -121,6 +121,29 @@ squisq validate ./my-folder --strict
121
121
 
122
122
  Diagnostics are reported at three severities — `error`, `warning`, and `info` (info is counted and shown separately). Exit codes depend on **errors** only: `0` clean, warnings-only, or info-only; `1` errors (or any warning with `--strict`); `2` input unreadable.
123
123
 
124
+ ### `squisq transform <input>`
125
+
126
+ Apply one-time markdown **source** transforms to a `.md` file, in the order given: `unwrap` (remove forced line wrapping so each paragraph is one line — hard breaks kept), `wrap` (hard-wrap paragraph prose at a column width on word boundaries — code, tables, and headings untouched), and `cleanup` (canonical house-style normalization: bullets, emphasis, headings, table padding, spacing; annotations and frontmatter preserved). Not to be confused with the `--transform <style>` slideshow-style flag on `convert`/`video` — this command rewrites the markdown text itself.
127
+
128
+ The transformed markdown goes to stdout by default (status messages go to stderr, so it pipes cleanly).
129
+
130
+ ```bash
131
+ squisq transform doc.md --ops unwrap
132
+ squisq transform doc.md --ops unwrap,cleanup > cleaned.md
133
+ squisq transform doc.md --ops wrap --width 100 --in-place
134
+ squisq transform doc.md --ops cleanup -o cleaned.md
135
+ ```
136
+
137
+ | Option | Description |
138
+ | -------------- | -------------------------------------------------------------------------- |
139
+ | `--ops <list>` | Comma-separated transforms, applied in order (`unwrap`, `wrap`, `cleanup`) |
140
+ | `--width <n>` | Column width for `wrap` (20–500, default 80) |
141
+ | `-o <file>` | Write the result to a file (refuses to overwrite without `--overwrite`) |
142
+ | `--in-place` | Rewrite the input file (conflicts with `-o`) |
143
+ | `--overwrite` | Allow `-o` to replace an existing file |
144
+
145
+ Transforms run in strict mode: each one reparses its output and structurally compares it against the input document; if equivalence cannot be proven, the command exits `1` without emitting anything. Exit codes: `0` success, `1` transform/output failure, `2` input unreadable.
146
+
124
147
  ## Input Formats
125
148
 
126
149
  All commands accept:
@@ -11,7 +11,7 @@ import {
11
11
  readInput,
12
12
  renderDocToGif,
13
13
  renderDocToMp4
14
- } from "./chunk-ZEN42AFN.js";
14
+ } from "./chunk-WR2BZJ77.js";
15
15
  import {
16
16
  GIF_EXPORT_DEFAULTS,
17
17
  framesToGifNative,
package/dist/api.js CHANGED
@@ -298,6 +298,7 @@ var GIF_DEFAULTS = {
298
298
  function mp4Format() {
299
299
  return {
300
300
  id: "mp4",
301
+ templateAnnotationHandling: "rendered",
301
302
  label: "MP4 Video",
302
303
  mimeType: "video/mp4",
303
304
  extensions: [".mp4"],
@@ -339,6 +340,7 @@ function mp4Format() {
339
340
  function gifFormat() {
340
341
  return {
341
342
  id: "gif",
343
+ templateAnnotationHandling: "rendered",
342
344
  label: "Animated GIF",
343
345
  mimeType: "image/gif",
344
346
  extensions: [".gif"],
@@ -295,6 +295,7 @@ var GIF_DEFAULTS = {
295
295
  function mp4Format() {
296
296
  return {
297
297
  id: "mp4",
298
+ templateAnnotationHandling: "rendered",
298
299
  label: "MP4 Video",
299
300
  mimeType: "video/mp4",
300
301
  extensions: [".mp4"],
@@ -308,7 +309,7 @@ function mp4Format() {
308
309
  const animationsEnabled = typeof mp4Opts.animationsEnabled === "boolean" ? mp4Opts.animationsEnabled : MP4_DEFAULTS.animationsEnabled;
309
310
  const outputPath = join(tmpdir(), `squisq-mp4-${randomBytes(8).toString("hex")}.mp4`);
310
311
  try {
311
- const { renderDocToMp4: renderDocToMp42 } = await import("./api-A7FKMZY6.js");
312
+ const { renderDocToMp4: renderDocToMp42 } = await import("./api-HBPJQ7MA.js");
312
313
  await renderDocToMp42(input.doc, input.container, {
313
314
  outputPath,
314
315
  fps,
@@ -336,6 +337,7 @@ function mp4Format() {
336
337
  function gifFormat() {
337
338
  return {
338
339
  id: "gif",
340
+ templateAnnotationHandling: "rendered",
339
341
  label: "Animated GIF",
340
342
  mimeType: "image/gif",
341
343
  extensions: [".gif"],
@@ -348,7 +350,7 @@ function gifFormat() {
348
350
  const defaultHeight = portrait ? GIF_DEFAULTS.width : GIF_DEFAULTS.height;
349
351
  const outputPath = join(tmpdir(), `squisq-gif-${randomBytes(8).toString("hex")}.gif`);
350
352
  try {
351
- const { renderDocToGif: renderDocToGif2 } = await import("./api-A7FKMZY6.js");
353
+ const { renderDocToGif: renderDocToGif2 } = await import("./api-HBPJQ7MA.js");
352
354
  const result = await renderDocToGif2(input.doc, input.container, {
353
355
  outputPath,
354
356
  fps: typeof gifOpts.fps === "number" ? gifOpts.fps : GIF_DEFAULTS.fps,
package/dist/index.js CHANGED
@@ -7,12 +7,12 @@ import {
7
7
  readInput,
8
8
  renderDocToGif,
9
9
  renderDocToMp4
10
- } from "./chunk-ZEN42AFN.js";
10
+ } from "./chunk-WR2BZJ77.js";
11
11
  import "./chunk-GGVASRPG.js";
12
12
 
13
13
  // src/index.ts
14
14
  import { createRequire } from "module";
15
- import { Command } from "commander";
15
+ import { Command as Command2 } from "commander";
16
16
 
17
17
  // src/commands/convert.ts
18
18
  import { mkdir } from "fs/promises";
@@ -627,6 +627,91 @@ async function runDoctor(runtime = defaultDoctorRuntime) {
627
627
  return allPresent;
628
628
  }
629
629
 
630
+ // src/commands/transform.ts
631
+ import { mkdir as mkdir3, readFile } from "fs/promises";
632
+ import { dirname as dirname4, resolve as resolve4 } from "path";
633
+ import { Option as Option2 } from "commander";
634
+ import {
635
+ DEFAULT_WRAP_WIDTH,
636
+ MARKDOWN_SOURCE_TRANSFORMS,
637
+ applyMarkdownSourceTransform
638
+ } from "@bendyline/squisq/markdown";
639
+ var VALID_OPS = MARKDOWN_SOURCE_TRANSFORMS.map((t) => t.id);
640
+ function registerTransformCommand(program2) {
641
+ program2.command("transform").description("Apply one-time markdown source transforms (unwrap, wrap, cleanup) to a document").argument("<input>", "Path to a markdown (.md) file").requiredOption(
642
+ "--ops <list>",
643
+ `Comma-separated transforms, applied in order. Valid: ${VALID_OPS.join(", ")}`
644
+ ).option(
645
+ "--width <n>",
646
+ "Column width for the wrap transform (20\u2013500)",
647
+ String(DEFAULT_WRAP_WIDTH)
648
+ ).addOption(
649
+ new Option2("-o, --output <file>", "Write the result to a file (default: stdout)").conflicts(
650
+ "inPlace"
651
+ )
652
+ ).addOption(
653
+ new Option2("--in-place", "Rewrite the input file with the result").conflicts("output")
654
+ ).option("--overwrite", "Replace an existing --output file (default: refuse and exit non-zero)").action(async (inputPath, opts) => {
655
+ try {
656
+ process.exitCode = await runTransform(inputPath, opts);
657
+ } catch (err) {
658
+ const message = err instanceof Error ? err.message : String(err);
659
+ console.error(`Error: ${message}`);
660
+ process.exitCode = 1;
661
+ }
662
+ });
663
+ }
664
+ async function runTransform(inputPath, opts) {
665
+ const resolvedInput = resolve4(inputPath);
666
+ let source;
667
+ try {
668
+ source = await readFile(resolvedInput, { encoding: "utf-8" });
669
+ } catch (err) {
670
+ const message = err instanceof Error ? err.message : String(err);
671
+ console.error(`Error: could not read input: ${message}`);
672
+ return 2;
673
+ }
674
+ const ids = opts.ops.split(",").map((id) => id.trim()).filter((id) => id.length > 0);
675
+ if (ids.length === 0) {
676
+ console.error(`Error: --ops needs at least one transform (valid: ${VALID_OPS.join(", ")})`);
677
+ return 1;
678
+ }
679
+ for (const id of ids) {
680
+ if (!VALID_OPS.includes(id)) {
681
+ console.error(`Error: unknown transform "${id}" (valid: ${VALID_OPS.join(", ")})`);
682
+ return 1;
683
+ }
684
+ }
685
+ const width = Number.parseInt(opts.width, 10);
686
+ if (Number.isNaN(width)) {
687
+ console.error(`Error: --width must be a number (got "${opts.width}")`);
688
+ return 1;
689
+ }
690
+ let result = source;
691
+ const applied = [];
692
+ for (const id of ids) {
693
+ const step = applyMarkdownSourceTransform(id, result, { width, strict: true });
694
+ result = step.output;
695
+ applied.push(step.changed ? id : `${id} (no changes)`);
696
+ }
697
+ const summary = applied.join(", ");
698
+ const bytes = Buffer.from(result, "utf-8");
699
+ if (opts.inPlace) {
700
+ await writeFileGuarded(resolvedInput, bytes, true);
701
+ console.error(`\u2713 ${summary} \u2192 ${resolvedInput}`);
702
+ } else if (opts.output) {
703
+ const resolvedOutput = resolve4(opts.output);
704
+ await assertOutputsWritable([resolvedOutput], opts.overwrite);
705
+ await mkdir3(dirname4(resolvedOutput), { recursive: true });
706
+ await writeFileGuarded(resolvedOutput, bytes, opts.overwrite);
707
+ console.error(`\u2713 ${summary} \u2192 ${resolvedOutput}`);
708
+ } else {
709
+ process.stdout.write(result);
710
+ console.error(`\u2713 ${summary}`);
711
+ }
712
+ return 0;
713
+ }
714
+
630
715
  // src/index.ts
631
716
  var require2 = createRequire(import.meta.url);
632
717
  var { version } = require2("../package.json");
@@ -635,10 +720,11 @@ if (process.stderr.isTTY) {
635
720
  `\x1B[36m{[\x1B[0m \x1B[1msquiggly square\x1B[0m \x1B[2m\u2014\x1B[0m \x1B[1msquisq\x1B[0m \x1B[2m\u2014\x1B[0m \x1B[33mv${version}\x1B[0m \x1B[36m]}\x1B[0m`
636
721
  );
637
722
  }
638
- var program = new Command();
723
+ var program = new Command2();
639
724
  program.name("squisq").description("Squisq CLI \u2014 convert and process markdown-based documents").version(version);
640
725
  registerConvertCommand(program);
641
726
  registerVideoCommand(program);
642
727
  registerValidateCommand(program);
643
728
  registerDoctorCommand(program);
729
+ registerTransformCommand(program);
644
730
  program.parse();