@markuplint/cli-utils 5.0.0-alpha.1 → 5.0.0-alpha.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.
- package/CHANGELOG.md +14 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/lib/message-to-string.d.ts +4 -3
- package/lib/message-to-string.js +11 -6
- package/lib/unified-diff.d.ts +10 -0
- package/lib/unified-diff.js +144 -0
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,20 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
# [5.0.0-alpha.3](https://github.com/markuplint/markuplint/compare/v5.0.0-alpha.2...v5.0.0-alpha.3) (2026-02-26)
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
- **cli-utils:** return empty string from unifiedDiff for identical inputs ([a738284](https://github.com/markuplint/markuplint/commit/a738284e439724da17d3444ac176ccc3d9bd2008))
|
|
11
|
+
|
|
12
|
+
### Features
|
|
13
|
+
|
|
14
|
+
- **cli-utils:** add specConformance to messageToString ([fda8660](https://github.com/markuplint/markuplint/commit/fda866061dbd8a989ca3c393487ea95be89b5a67))
|
|
15
|
+
|
|
16
|
+
# [5.0.0-alpha.2](https://github.com/markuplint/markuplint/compare/v5.0.0-alpha.1...v5.0.0-alpha.2) (2026-02-23)
|
|
17
|
+
|
|
18
|
+
**Note:** Version bump only for package @markuplint/cli-utils
|
|
19
|
+
|
|
6
20
|
# [5.0.0-alpha.1](https://github.com/markuplint/markuplint/compare/v5.0.0-alpha.0...v5.0.0-alpha.1) (2026-02-22)
|
|
7
21
|
|
|
8
22
|
**Note:** Version bump only for package @markuplint/cli-utils
|
package/lib/index.d.ts
CHANGED
package/lib/index.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Combines a lint violation message with an optional
|
|
3
|
-
*
|
|
2
|
+
* Combines a lint violation message with an optional specConformance tag
|
|
3
|
+
* and reason string.
|
|
4
4
|
*
|
|
5
5
|
* @param message - The primary violation message
|
|
6
|
+
* @param specConformance - An optional conformance level (e.g. "normative")
|
|
6
7
|
* @param reason - An optional supplementary reason or detail to append
|
|
7
8
|
* @returns The combined message string
|
|
8
9
|
*/
|
|
9
|
-
export declare function messageToString(message: string, reason?: string): string;
|
|
10
|
+
export declare function messageToString(message: string, specConformance?: string, reason?: string): string;
|
package/lib/message-to-string.js
CHANGED
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Combines a lint violation message with an optional
|
|
3
|
-
*
|
|
2
|
+
* Combines a lint violation message with an optional specConformance tag
|
|
3
|
+
* and reason string.
|
|
4
4
|
*
|
|
5
5
|
* @param message - The primary violation message
|
|
6
|
+
* @param specConformance - An optional conformance level (e.g. "normative")
|
|
6
7
|
* @param reason - An optional supplementary reason or detail to append
|
|
7
8
|
* @returns The combined message string
|
|
8
9
|
*/
|
|
9
|
-
export function messageToString(message, reason) {
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
export function messageToString(message, specConformance, reason) {
|
|
11
|
+
let result = message;
|
|
12
|
+
if (specConformance) {
|
|
13
|
+
result += ` [${specConformance}]`;
|
|
12
14
|
}
|
|
13
|
-
|
|
15
|
+
if (reason) {
|
|
16
|
+
result += ` / ${reason}`;
|
|
17
|
+
}
|
|
18
|
+
return result;
|
|
14
19
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generates a unified-style diff string between two texts.
|
|
3
|
+
* Uses ANSI colors via picocolors when color output is supported.
|
|
4
|
+
*
|
|
5
|
+
* @param filePath - File path for the diff header
|
|
6
|
+
* @param original - Original text
|
|
7
|
+
* @param fixed - Fixed text
|
|
8
|
+
* @returns Formatted unified diff string
|
|
9
|
+
*/
|
|
10
|
+
export declare function unifiedDiff(filePath: string, original: string, fixed: string): string;
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import c from 'picocolors';
|
|
2
|
+
/**
|
|
3
|
+
* Generates a unified-style diff string between two texts.
|
|
4
|
+
* Uses ANSI colors via picocolors when color output is supported.
|
|
5
|
+
*
|
|
6
|
+
* @param filePath - File path for the diff header
|
|
7
|
+
* @param original - Original text
|
|
8
|
+
* @param fixed - Fixed text
|
|
9
|
+
* @returns Formatted unified diff string
|
|
10
|
+
*/
|
|
11
|
+
export function unifiedDiff(filePath, original, fixed) {
|
|
12
|
+
const originalLines = original.split('\n');
|
|
13
|
+
const fixedLines = fixed.split('\n');
|
|
14
|
+
const edits = computeLineEdits(originalLines, fixedLines);
|
|
15
|
+
const CONTEXT = 3;
|
|
16
|
+
const hunks = groupIntoHunks(edits, CONTEXT);
|
|
17
|
+
if (hunks.length === 0) {
|
|
18
|
+
return '';
|
|
19
|
+
}
|
|
20
|
+
const lines = [c.red(`--- a/${filePath}`), c.green(`+++ b/${filePath}`)];
|
|
21
|
+
for (const hunk of hunks) {
|
|
22
|
+
let origCount = 0;
|
|
23
|
+
let fixCount = 0;
|
|
24
|
+
const hunkLines = [];
|
|
25
|
+
for (const edit of hunk.edits) {
|
|
26
|
+
switch (edit.type) {
|
|
27
|
+
case 'equal': {
|
|
28
|
+
hunkLines.push(` ${edit.line}`);
|
|
29
|
+
origCount++;
|
|
30
|
+
fixCount++;
|
|
31
|
+
break;
|
|
32
|
+
}
|
|
33
|
+
case 'delete': {
|
|
34
|
+
hunkLines.push(c.red(`-${edit.line}`));
|
|
35
|
+
origCount++;
|
|
36
|
+
break;
|
|
37
|
+
}
|
|
38
|
+
case 'insert': {
|
|
39
|
+
hunkLines.push(c.green(`+${edit.line}`));
|
|
40
|
+
fixCount++;
|
|
41
|
+
break;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
lines.push(c.cyan(`@@ -${hunk.origStart + 1},${origCount} +${hunk.fixStart + 1},${fixCount} @@`), ...hunkLines);
|
|
46
|
+
}
|
|
47
|
+
return lines.join('\n');
|
|
48
|
+
}
|
|
49
|
+
// O(n*m) LCS — acceptable for typical HTML files; consider Myers' algorithm if performance issues arise.
|
|
50
|
+
function computeLineEdits(a, b) {
|
|
51
|
+
const n = a.length;
|
|
52
|
+
const m = b.length;
|
|
53
|
+
// Build LCS table — dp[i][j] = LCS length of a[0..i-1] and b[0..j-1]
|
|
54
|
+
const dp = [];
|
|
55
|
+
for (let i = 0; i <= n; i++) {
|
|
56
|
+
const row = [];
|
|
57
|
+
for (let j = 0; j <= m; j++) {
|
|
58
|
+
row.push(0);
|
|
59
|
+
}
|
|
60
|
+
dp.push(row);
|
|
61
|
+
}
|
|
62
|
+
for (let i = 1; i <= n; i++) {
|
|
63
|
+
for (let j = 1; j <= m; j++) {
|
|
64
|
+
if (a[i - 1] === b[j - 1]) {
|
|
65
|
+
dp[i][j] = dp[i - 1][j - 1] + 1;
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
// Backtrack to produce edit script
|
|
73
|
+
const edits = [];
|
|
74
|
+
let i = n;
|
|
75
|
+
let j = m;
|
|
76
|
+
while (i > 0 || j > 0) {
|
|
77
|
+
if (i > 0 && j > 0 && a[i - 1] === b[j - 1]) {
|
|
78
|
+
edits.push({ type: 'equal', line: a[i - 1] });
|
|
79
|
+
i--;
|
|
80
|
+
j--;
|
|
81
|
+
}
|
|
82
|
+
else if (j > 0 && (i === 0 || dp[i][j - 1] >= dp[i - 1][j])) {
|
|
83
|
+
edits.push({ type: 'insert', line: b[j - 1] });
|
|
84
|
+
j--;
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
edits.push({ type: 'delete', line: a[i - 1] });
|
|
88
|
+
i--;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
edits.reverse();
|
|
92
|
+
return edits;
|
|
93
|
+
}
|
|
94
|
+
function groupIntoHunks(edits, context) {
|
|
95
|
+
const changedIndices = [];
|
|
96
|
+
for (const [i, edit] of edits.entries()) {
|
|
97
|
+
if (edit.type !== 'equal') {
|
|
98
|
+
changedIndices.push(i);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
if (changedIndices.length === 0) {
|
|
102
|
+
return [];
|
|
103
|
+
}
|
|
104
|
+
const firstIdx = changedIndices[0];
|
|
105
|
+
const hunks = [];
|
|
106
|
+
let rangeStart = Math.max(0, firstIdx - context);
|
|
107
|
+
let rangeEnd = Math.min(edits.length - 1, firstIdx + context);
|
|
108
|
+
for (let k = 1; k < changedIndices.length; k++) {
|
|
109
|
+
const idx = changedIndices[k];
|
|
110
|
+
const newStart = Math.max(0, idx - context);
|
|
111
|
+
const newEnd = Math.min(edits.length - 1, idx + context);
|
|
112
|
+
if (newStart <= rangeEnd + 1) {
|
|
113
|
+
rangeEnd = newEnd;
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
hunks.push(buildHunk(edits, rangeStart, rangeEnd));
|
|
117
|
+
rangeStart = newStart;
|
|
118
|
+
rangeEnd = newEnd;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
hunks.push(buildHunk(edits, rangeStart, rangeEnd));
|
|
122
|
+
return hunks;
|
|
123
|
+
}
|
|
124
|
+
function buildHunk(edits, start, end) {
|
|
125
|
+
let origLine = 0;
|
|
126
|
+
let fixLine = 0;
|
|
127
|
+
for (let i = 0; i < start; i++) {
|
|
128
|
+
const edit = edits[i];
|
|
129
|
+
if (!edit) {
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
if (edit.type === 'equal' || edit.type === 'delete') {
|
|
133
|
+
origLine++;
|
|
134
|
+
}
|
|
135
|
+
if (edit.type === 'equal' || edit.type === 'insert') {
|
|
136
|
+
fixLine++;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return {
|
|
140
|
+
origStart: origLine,
|
|
141
|
+
fixStart: fixLine,
|
|
142
|
+
edits: edits.slice(start, end + 1),
|
|
143
|
+
};
|
|
144
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@markuplint/cli-utils",
|
|
3
|
-
"version": "5.0.0-alpha.
|
|
3
|
+
"version": "5.0.0-alpha.3",
|
|
4
4
|
"description": "Utilities for CLI of Markuplint",
|
|
5
5
|
"repository": "git@github.com:markuplint/markuplint.git",
|
|
6
6
|
"author": "Yusuke Hirao <yusukehirao@me.com>",
|
|
@@ -31,5 +31,5 @@
|
|
|
31
31
|
"picocolors": "1.1.1",
|
|
32
32
|
"strip-ansi": "7.1.2"
|
|
33
33
|
},
|
|
34
|
-
"gitHead": "
|
|
34
|
+
"gitHead": "2fbdf26daa3d021ac628ccc2f59f0eeae6ddd53d"
|
|
35
35
|
}
|