yoctomarkdown 0.0.1 → 0.0.3

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.
@@ -0,0 +1,31 @@
1
+ ---
2
+ description: Create a new tagged npm release (patch/minor/major)
3
+ ---
4
+
5
+ Create a new npm release for this repo. The bump type is: $1 (default to "patch" if not provided).
6
+
7
+ Steps to follow exactly:
8
+
9
+ 1. Normalize bump type to one of `patch|minor|major`; if empty, use `patch`.
10
+ 2. Verify git state before doing anything:
11
+ - current branch must be `main`
12
+ - working tree must be clean
13
+ - run `git fetch origin --tags` and ensure `HEAD` equals `origin/main`
14
+ If any check fails, stop and report.
15
+ 3. Run the full repo check suit — stop and report failures.
16
+ 4. Read `package.json` and capture both `name` and current `version`.
17
+ 5. Compute the next semver version by bumping the selected part.
18
+ 6. Guardrails before mutating files:
19
+ - ensure git tag `v<new-version>` does not exist locally or on origin
20
+ - ensure npm version does not already exist: `npm view <package-name>@<new-version> version`
21
+ If either exists, stop and report.
22
+ 7. Update the `version` field in `package.json` to `<new-version>`.
23
+ 8. Run `bun run build`.
24
+ 9. Commit all staged and unstaged changes with message: `chore: release v<new-version>`.
25
+ 10. Create annotated tag: `git tag -a v<new-version> -m "<new-version>\n<small-changelog>"`.
26
+ 11. Push explicitly to main and the exact tag:
27
+ - `git push origin main`
28
+ - `git push origin v<new-version>`
29
+ 12. Post-push verification: run `npm view <package-name>@<new-version> version`.
30
+ - If it already exists, report that publish already happened and do **not** ask user to publish again.
31
+ - If it does not exist, run `npm publish`.
package/README.md CHANGED
@@ -13,6 +13,8 @@ A small, fast, Bun-native Markdown highlighter for terminal output.
13
13
 
14
14
  ## Installation
15
15
 
16
+ [npm package](https://www.npmjs.com/package/yoctomarkdown)
17
+
16
18
  ```bash
17
19
  bun add yoctomarkdown
18
20
  ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yoctomarkdown",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
4
  "module": "src/index.ts",
5
5
  "type": "module",
6
6
  "devDependencies": {
@@ -18,7 +18,7 @@
18
18
  "yoctocolors": "^2.1.2"
19
19
  },
20
20
  "bin": {
21
- "yoctomarkdown": "./src/cli.ts"
21
+ "yoctomarkdown": "src/cli.ts"
22
22
  },
23
23
  "scripts": {
24
24
  "typecheck": "tsc --noEmit",
@@ -30,5 +30,13 @@
30
30
  "format": "prettier --write .",
31
31
  "format:check": "prettier --check .",
32
32
  "test": "bun test"
33
+ },
34
+ "types": "./src/index.ts",
35
+ "exports": {
36
+ ".": {
37
+ "types": "./src/index.ts",
38
+ "import": "./src/index.ts",
39
+ "default": "./src/index.ts"
40
+ }
33
41
  }
34
42
  }
package/src/index.ts CHANGED
@@ -83,17 +83,17 @@ export function createHighlighter(options?: Options): Highlighter {
83
83
  return result;
84
84
  }
85
85
 
86
- function parseLine(line: string, _isLastLine: boolean): string {
86
+ function parseLine(line: string, isPartial: boolean): string {
87
87
  if (inCodeBlock) {
88
88
  if (line.trim().startsWith("```")) {
89
- inCodeBlock = false;
89
+ if (!isPartial) inCodeBlock = false;
90
90
  return theme.codeBlock(line);
91
91
  }
92
92
  return theme.codeBlock(line);
93
93
  }
94
94
 
95
95
  if (line.trim().startsWith("```")) {
96
- inCodeBlock = true;
96
+ if (!isPartial) inCodeBlock = true;
97
97
  return theme.codeBlock(line);
98
98
  }
99
99
 
@@ -127,6 +127,9 @@ export function createHighlighter(options?: Options): Highlighter {
127
127
  return parseInline(line);
128
128
  }
129
129
 
130
+ let hasPartial = false;
131
+ let partialLength = 0;
132
+
130
133
  return {
131
134
  write(chunk: string | Uint8Array): string {
132
135
  const text =
@@ -136,15 +139,50 @@ export function createHighlighter(options?: Options): Highlighter {
136
139
  const lines = buffer.split("\n");
137
140
  buffer = lines.pop() ?? "";
138
141
 
139
- return (
140
- lines.map((line) => parseLine(line, false)).join("\n") +
141
- (lines.length > 0 ? "\n" : "")
142
- );
142
+ let out = "";
143
+ if (hasPartial) {
144
+ const columns =
145
+ typeof process !== "undefined" && process.stdout?.columns
146
+ ? process.stdout.columns
147
+ : 80;
148
+ const linesToClear = Math.ceil(partialLength / columns) || 1;
149
+ out += "\x1b[2K\r";
150
+ if (linesToClear > 1) {
151
+ out += "\x1b[1A\x1b[2K".repeat(linesToClear - 1);
152
+ }
153
+ }
154
+
155
+ if (lines.length > 0) {
156
+ out += `${lines.map((line) => parseLine(line, false)).join("\n")}\n`;
157
+ }
158
+
159
+ if (buffer.length > 0) {
160
+ out += parseLine(buffer, true);
161
+ hasPartial = true;
162
+ partialLength = buffer.length;
163
+ } else {
164
+ hasPartial = false;
165
+ partialLength = 0;
166
+ }
167
+
168
+ return out;
143
169
  },
144
170
  end(): string {
145
171
  if (buffer.length > 0) {
146
- const out = parseLine(buffer, true);
172
+ const out = parseLine(buffer, false);
147
173
  buffer = "";
174
+ if (hasPartial) {
175
+ const columns =
176
+ typeof process !== "undefined" && process.stdout?.columns
177
+ ? process.stdout.columns
178
+ : 80;
179
+ const linesToClear = Math.ceil(partialLength / columns) || 1;
180
+ let clearSeq = "\x1b[2K\r";
181
+ if (linesToClear > 1) {
182
+ clearSeq += "\x1b[1A\x1b[2K".repeat(linesToClear - 1);
183
+ }
184
+ return clearSeq + out;
185
+ }
148
186
  return out;
149
187
  }
150
188
  return "";
@@ -32,6 +32,12 @@ describe("Programmatic Usage: highlightSync", () => {
32
32
  });
33
33
  });
34
34
 
35
+ function expectStreamMatch(res: string, markdown: string) {
36
+ const expected = highlightSync(markdown, { theme: "none" });
37
+ // biome-ignore lint/suspicious/noControlCharactersInRegex: clear line ansi escape
38
+ expect(res.replace(/[^\n]*\x1b\[2K\r/g, "")).toBe(expected);
39
+ }
40
+
35
41
  describe("Programmatic Usage: createHighlighter (Streaming)", () => {
36
42
  test("should handle chunks without breaking output", () => {
37
43
  const highlighter = createHighlighter({ theme: "none" });
@@ -55,8 +61,7 @@ describe("Programmatic Usage: createHighlighter (Streaming)", () => {
55
61
  }
56
62
  res += highlighter.end();
57
63
 
58
- const expected = highlightSync(markdown, { theme: "none" });
59
- expect(res).toBe(expected);
64
+ expectStreamMatch(res, markdown);
60
65
  });
61
66
 
62
67
  test("should handle random chunk sizes", () => {
@@ -74,7 +79,36 @@ describe("Programmatic Usage: createHighlighter (Streaming)", () => {
74
79
  }
75
80
  res += highlighter.end();
76
81
 
77
- const expected = highlightSync(markdown, { theme: "none" });
78
- expect(res).toBe(expected);
82
+ expectStreamMatch(res, markdown);
83
+ });
84
+
85
+ test("should clear multiple lines when wrapped", () => {
86
+ // Mock columns
87
+ const originalColumns = process.stdout.columns;
88
+ Object.defineProperty(process.stdout, "columns", {
89
+ value: 10,
90
+ configurable: true,
91
+ });
92
+
93
+ const highlighter = createHighlighter({ theme: "none" });
94
+ // 25 chars, columns = 10, so it takes 3 visual lines
95
+ const chunk1 = "1234567890123456789012345";
96
+ const chunk2 = "67890\n";
97
+
98
+ let res = highlighter.write(chunk1);
99
+ res += highlighter.write(chunk2);
100
+ res += highlighter.end();
101
+
102
+ // The first chunk is written, then cleared. Since it takes 3 lines,
103
+ // clear sequence should have \x1b[1A\x1b[2K repeated twice.
104
+ expect(res).toContain("\x1b[1A\x1b[2K\x1b[1A\x1b[2K");
105
+
106
+ // Restore
107
+ if (originalColumns !== undefined) {
108
+ Object.defineProperty(process.stdout, "columns", {
109
+ value: originalColumns,
110
+ configurable: true,
111
+ });
112
+ }
79
113
  });
80
114
  });