@d3lm/lint-preset 1.5.0 → 1.5.2
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 +5 -3
- package/dist/bin/comments.js +105 -51
- package/dist/bin/comments.js.map +1 -1
- package/dist/comments.cjs +94 -28
- package/dist/comments.cjs.map +1 -1
- package/dist/comments.d.cts +5 -3
- package/dist/comments.d.ts +5 -3
- package/dist/comments.js +94 -28
- package/dist/comments.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -245,7 +245,7 @@ comments agent # hand suspicious comments to an agent, then
|
|
|
245
245
|
comments check --cwd ../other-app # run as if started in another project
|
|
246
246
|
```
|
|
247
247
|
|
|
248
|
-
Without targets, only comments in uncommitted changes and untracked files are checked. `--
|
|
248
|
+
Without targets, only comments in uncommitted changes and untracked files are checked. `--base main` checks everything since the merge base with `main`, and `--repo` checks every file. `--whole-files` checks every comment in the changed files instead of only the comments on changed lines. Targets can be files, directories, `file:line`, or `file:start-end`. A comment that contains `comments-ignore` is skipped. A file with a syntax error is skipped as a whole and named in a warning, because the parser stops at the error and never sees the comments after it. JSX is read in `.js` files too.
|
|
249
249
|
|
|
250
250
|
With `--json`, each paragraph of a comment comes with its issues and a `suggestion` that holds the exact rewrapped lines. Code blocks, `@example`, tables, drawings, directives, and other [text kept as written](#text-kept-as-written) have no suggestion.
|
|
251
251
|
|
|
@@ -285,7 +285,7 @@ A comment with an issue in a part that is kept is handed to the agent, which can
|
|
|
285
285
|
|
|
286
286
|
### Configuration
|
|
287
287
|
|
|
288
|
-
`width` is the preferred line length; `maxWidth` is the hard maximum, including indentation and the comment prefix. The CLI defaults to a maximum of 120 columns (or `width`, if larger). A balanced parenthetical can extend a line past `width` when this keeps it together within `maxWidth`. In longer paragraphs, the wrapper balances all lines together and may split a parenthetical when that produces a better layout. If the parenthetical cannot fit on a line of its own, it wraps normally. For example, `width: 80, maxWidth: 120` keeps `(JSDoc types, \`{@link ...}\`)` together in the example below:
|
|
288
|
+
`width` is the preferred line length; `maxWidth` is the hard maximum, including indentation and the comment prefix. The CLI defaults to a maximum of 120 columns (or `width`, if larger). A balanced parenthetical can extend a line past `width` when this keeps it together within `maxWidth`. In longer paragraphs, the wrapper balances all lines together and may split a parenthetical when that produces a better layout. A line never sticks out more than 15 columns past the line before it, like a full second line after a first line that the orphan rule keeps short, when up to two more lines avoid that. If the parenthetical cannot fit on a line of its own, it wraps normally. For example, `width: 80, maxWidth: 120` keeps `(JSDoc types, \`{@link ...}\`)` together in the example below:
|
|
289
289
|
|
|
290
290
|
```ts
|
|
291
291
|
/**
|
|
@@ -294,7 +294,7 @@ A comment with an issue in a part that is kept is handed to the agent, which can
|
|
|
294
294
|
*/
|
|
295
295
|
```
|
|
296
296
|
|
|
297
|
-
The separate `singleLineWidth` allowance is capped by `maxWidth`. Code and documented layout regions retain their exemptions. An indivisible token in measured prose that exceeds `maxWidth` is reported as overflow; the wrapper preserves it, so resolving that case requires changing the maximum or the comment manually.
|
|
297
|
+
The separate `singleLineWidth` allowance is capped by `maxWidth`. Code and documented layout regions retain their exemptions. An indivisible token in measured prose that exceeds `maxWidth` is reported as overflow; the wrapper preserves it, so resolving that case requires changing the maximum or the comment manually. `tolerance` lets prose lines go up to that many columns past `width` when that saves a line in a paragraph of three or more lines, and it never goes past `maxWidth`. It defaults to 0. When `maxWidth` is omitted, `tolerance` also sets a legacy maximum of `width + tolerance`. Direct `analyzeSource` callers can omit `maxWidth` to retain the legacy width/tolerance behavior.
|
|
298
298
|
|
|
299
299
|
Like prettier, `comments` looks for a config file in the current directory and its parents, up to the repository root. The config can be a `comments.config.{ts,mts,js,mjs,cjs}`, `.commentsrc`, or `.commentsrc.json` file, or a `comments` key in `package.json`. All options are optional.
|
|
300
300
|
|
|
@@ -306,6 +306,8 @@ export default defineConfig({
|
|
|
306
306
|
// defaults to prettier's printWidth, or 80
|
|
307
307
|
width: 100,
|
|
308
308
|
maxWidth: 120,
|
|
309
|
+
// prose lines may go this many columns past `width` when that saves a line
|
|
310
|
+
tolerance: 0,
|
|
309
311
|
// a comment that is one paragraph stays on one line up to this length, and wraps to `width` beyond it
|
|
310
312
|
singleLineWidth: 120,
|
|
311
313
|
avoidSentenceOrphans: true,
|
package/dist/bin/comments.js
CHANGED
|
@@ -24,7 +24,7 @@ When rules pull in different directions, the earlier one wins.
|
|
|
24
24
|
|
|
25
25
|
## Layout
|
|
26
26
|
|
|
27
|
-
- After you finish, the tool rewraps prose paragraphs, list items, and JSDoc tag descriptions. The width is a preferred target. A parenthetical may extend a line up to the configured maximum so it stays together. In longer paragraphs, balanced lines take priority over keeping every parenthetical on one line. The tool also avoids short last lines where possible. Leave these line breaks to the tool, except for the sentence-orphan case below.
|
|
27
|
+
- After you finish, the tool rewraps prose paragraphs, list items, and JSDoc tag descriptions. The width is a preferred target. A parenthetical may extend a line up to the configured maximum so it stays together, and lines may go up to the configured tolerance past the width when that saves a line. In longer paragraphs, balanced lines take priority over keeping every parenthetical on one line. The tool also avoids short last lines where possible. Leave these line breaks to the tool, except for the sentence-orphan case below.
|
|
28
28
|
- Don't end a line with the first word of a new sentence, clause, or parenthetical, like \`This\` after a period, \`the\` after a comma, or \`(the\`. Move it to the next line instead.
|
|
29
29
|
- In a paragraph of two lines, the first line ends with a sentence whenever the rest fits on the second line and is no longer than the first. Don't end it with the start of the next sentence, like \`A wrap\` in \`...lay out differently. A wrap\`. Move those words to the second line instead.
|
|
30
30
|
- A comment whose whole text is one paragraph stays on a single line when that line fits a separate, larger single-line width, even past the width. Only a longer comment wraps to the width.
|
|
@@ -578,6 +578,8 @@ function hasWidow(lines) {
|
|
|
578
578
|
}
|
|
579
579
|
var FULLNESS_WEIGHT = 0.07;
|
|
580
580
|
var PARENTHETICAL_SPLIT_WEIGHT = 2;
|
|
581
|
+
var MAX_STEP = 15;
|
|
582
|
+
var EXTRA_LINES = 2;
|
|
581
583
|
function fewestLines(starts, target) {
|
|
582
584
|
const total = starts.length - 1;
|
|
583
585
|
const fewest = new Int32Array(total + 1);
|
|
@@ -710,7 +712,56 @@ function balanceTokens(tokens, firstLimit, restLimit, count, options, preferredL
|
|
|
710
712
|
const bounds = [0, ...best.breaks, total];
|
|
711
713
|
return bounds.slice(0, -1).map((start, index) => tokens.slice(start, bounds[index + 1]));
|
|
712
714
|
}
|
|
715
|
+
function hasStep(lines, offset = 0) {
|
|
716
|
+
return lines.some((line, index) => {
|
|
717
|
+
if (index === 0) {
|
|
718
|
+
return false;
|
|
719
|
+
}
|
|
720
|
+
const before = lineLength(lines[index - 1]) + (index === 1 ? offset : 0);
|
|
721
|
+
return lineLength(line) - before > MAX_STEP;
|
|
722
|
+
});
|
|
723
|
+
}
|
|
724
|
+
function splitCostsOf(tokens, limit) {
|
|
725
|
+
const splitCosts = /* @__PURE__ */ new Map();
|
|
726
|
+
for (const group of parentheticalGroups(tokens, limit)) {
|
|
727
|
+
const length = lineLength(tokens.slice(group.start, group.end));
|
|
728
|
+
for (let at = group.start + 1; at < group.end; at += 1) {
|
|
729
|
+
splitCosts.set(at, length * PARENTHETICAL_SPLIT_WEIGHT);
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
return splitCosts;
|
|
733
|
+
}
|
|
734
|
+
function hasParentheticals(tokens, restLimit, options) {
|
|
735
|
+
if (options.parentheticalOverflow === void 0) {
|
|
736
|
+
return false;
|
|
737
|
+
}
|
|
738
|
+
return bindParentheticals(tokens, restLimit + options.parentheticalOverflow).some((token) => isParenthetical(token));
|
|
739
|
+
}
|
|
713
740
|
function layoutTokens(tokens, firstLimit, restLimit, options) {
|
|
741
|
+
const lines = layoutLines(tokens, firstLimit, restLimit, options);
|
|
742
|
+
const offset = restLimit - firstLimit;
|
|
743
|
+
if (options.keepFirstLine || options.keepSteps || !hasStep(lines, offset)) {
|
|
744
|
+
return lines;
|
|
745
|
+
}
|
|
746
|
+
const overflow = hasParentheticals(tokens, restLimit, options) ? options.parentheticalOverflow ?? 0 : 0;
|
|
747
|
+
const splitCosts = overflow > 0 ? splitCostsOf(tokens, restLimit + overflow) : /* @__PURE__ */ new Map();
|
|
748
|
+
for (let count = lines.length + 1; count <= lines.length + EXTRA_LINES; count += 1) {
|
|
749
|
+
const even = balanceTokens(
|
|
750
|
+
tokens,
|
|
751
|
+
firstLimit + overflow,
|
|
752
|
+
restLimit + overflow,
|
|
753
|
+
count,
|
|
754
|
+
options,
|
|
755
|
+
restLimit,
|
|
756
|
+
splitCosts
|
|
757
|
+
);
|
|
758
|
+
if (even && !hasStep(even, offset)) {
|
|
759
|
+
return even;
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
return lines;
|
|
763
|
+
}
|
|
764
|
+
function layoutLines(tokens, firstLimit, restLimit, options) {
|
|
714
765
|
if (options.parentheticalOverflow !== void 0) {
|
|
715
766
|
const grouped = bindParentheticals(tokens, restLimit + options.parentheticalOverflow);
|
|
716
767
|
if (grouped.some((token) => isParenthetical(token))) {
|
|
@@ -744,14 +795,7 @@ function layoutTokens(tokens, firstLimit, restLimit, options) {
|
|
|
744
795
|
return expanded;
|
|
745
796
|
});
|
|
746
797
|
}
|
|
747
|
-
const splitCosts =
|
|
748
|
-
const groups = parentheticalGroups(tokens, restLimit + options.parentheticalOverflow);
|
|
749
|
-
for (const group of groups) {
|
|
750
|
-
const length = lineLength(tokens.slice(group.start, group.end));
|
|
751
|
-
for (let at = group.start + 1; at < group.end; at += 1) {
|
|
752
|
-
splitCosts.set(at, length * PARENTHETICAL_SPLIT_WEIGHT);
|
|
753
|
-
}
|
|
754
|
-
}
|
|
798
|
+
const splitCosts = splitCostsOf(tokens, restLimit + options.parentheticalOverflow);
|
|
755
799
|
const preferred = wrapTokens(tokens, firstLimit, restLimit, { ...options, parentheticalOverflow: void 0 });
|
|
756
800
|
for (let count = minimumLines; count <= Math.max(preferred.length + 1, preserved.length); count += 1) {
|
|
757
801
|
const balanced2 = balanceTokens(
|
|
@@ -781,6 +825,19 @@ function layoutTokens(tokens, firstLimit, restLimit, options) {
|
|
|
781
825
|
return lines;
|
|
782
826
|
}
|
|
783
827
|
}
|
|
828
|
+
if (options.tolerance > 0 && greedy.length > 2) {
|
|
829
|
+
const fewer = balanceTokens(
|
|
830
|
+
tokens,
|
|
831
|
+
firstLimit + options.tolerance,
|
|
832
|
+
restLimit + options.tolerance,
|
|
833
|
+
greedy.length - 1,
|
|
834
|
+
options,
|
|
835
|
+
restLimit
|
|
836
|
+
);
|
|
837
|
+
if (fewer && !hasStep(fewer, restLimit - firstLimit)) {
|
|
838
|
+
return fewer;
|
|
839
|
+
}
|
|
840
|
+
}
|
|
784
841
|
const balanced = balanceTokens(tokens, firstLimit, restLimit, greedy.length, options);
|
|
785
842
|
if (balanced) {
|
|
786
843
|
return balanced;
|
|
@@ -1270,11 +1327,11 @@ function analyzeParagraph(paragraph, options) {
|
|
|
1270
1327
|
parentheticalOverflow: options.maxWidth === void 0 ? void 0 : options.maxWidth - options.width,
|
|
1271
1328
|
keepFirstLine: paragraph.kind === "tag"
|
|
1272
1329
|
};
|
|
1273
|
-
const layOut = (from) => {
|
|
1274
|
-
const even2 = layoutTokens(from, firstLimit, restLimit,
|
|
1275
|
-
return { even: even2, wrapped: breakAfterSentence(even2, firstLimit, restLimit,
|
|
1330
|
+
const layOut = (from, settings) => {
|
|
1331
|
+
const even2 = layoutTokens(from, firstLimit, restLimit, settings);
|
|
1332
|
+
return { even: even2, wrapped: breakAfterSentence(even2, firstLimit, restLimit, settings) };
|
|
1276
1333
|
};
|
|
1277
|
-
|
|
1334
|
+
const units = bindLineStarts(paragraph.kind === "tag" ? bindTagHead(tokens) : tokens, startsLine);
|
|
1278
1335
|
const hardLimit = restLimit + (options.maxWidth === void 0 ? options.tolerance : options.maxWidth - options.width);
|
|
1279
1336
|
const written = currentLines.map((words) => ` ${words.join(" ")} `);
|
|
1280
1337
|
const staysInLimit = (unit) => {
|
|
@@ -1283,29 +1340,38 @@ function analyzeParagraph(paragraph, options) {
|
|
|
1283
1340
|
if (!units.every((unit) => staysInLimit(unit))) {
|
|
1284
1341
|
return keep({ tier: "manual", reason: "unbreakable text across lines" });
|
|
1285
1342
|
}
|
|
1286
|
-
let { even, wrapped } = layOut(units);
|
|
1287
1343
|
const bodyOf = (lines) => {
|
|
1288
1344
|
return lines.map((words, position) => {
|
|
1289
1345
|
const text = `${position === 0 ? paragraph.marker : hang}${words.join(" ")}`;
|
|
1290
1346
|
return { ...first, text, verbatim: false, hardBreak: HARD_BREAK.test(text) };
|
|
1291
1347
|
});
|
|
1292
1348
|
};
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
const
|
|
1304
|
-
|
|
1349
|
+
const settle = (settings) => {
|
|
1350
|
+
let current = units;
|
|
1351
|
+
let { even: even2, wrapped: wrapped2 } = layOut(current, settings);
|
|
1352
|
+
for (; ; ) {
|
|
1353
|
+
const body = bodyOf(wrapped2);
|
|
1354
|
+
const rewrap = structureOf(body);
|
|
1355
|
+
if (!rewrap) {
|
|
1356
|
+
return { even: even2, wrapped: wrapped2 };
|
|
1357
|
+
}
|
|
1358
|
+
const tipping = body.findIndex((_, position) => structureOf(body.slice(0, position + 1)) !== void 0);
|
|
1359
|
+
const start = wrapped2.slice(0, Math.max(tipping, 1)).flat().length;
|
|
1360
|
+
const pair = `${current[start - 1]} ${current[start]}`;
|
|
1361
|
+
if (body.length < 2 || !staysInLimit(pair)) {
|
|
1362
|
+
const reading = rewrap.tier === "code-like" ? "code" : rewrap.reason;
|
|
1363
|
+
return { tier: "manual", reason: `rewrap would read as ${reading}` };
|
|
1364
|
+
}
|
|
1365
|
+
current = current.toSpliced(start - 1, 2, pair);
|
|
1366
|
+
({ even: even2, wrapped: wrapped2 } = layOut(current, settings));
|
|
1305
1367
|
}
|
|
1306
|
-
|
|
1307
|
-
|
|
1368
|
+
};
|
|
1369
|
+
const settled = settle(layoutOptions);
|
|
1370
|
+
const layout = "wrapped" in settled ? settled : settle({ ...layoutOptions, keepSteps: true });
|
|
1371
|
+
if (!("wrapped" in layout)) {
|
|
1372
|
+
return keep(layout);
|
|
1308
1373
|
}
|
|
1374
|
+
const { even, wrapped } = layout;
|
|
1309
1375
|
const matchesLayout = sameLayout(wrapped, currentLines);
|
|
1310
1376
|
const lineIssues = [];
|
|
1311
1377
|
const overflow = options.maxWidth === void 0 ? [] : overflowIssues(paragraph.lines, options);
|
|
@@ -2114,23 +2180,12 @@ function snapshotChanges(root) {
|
|
|
2114
2180
|
var DIFF_FLAGS = ["diff", "-U0", "--no-color", "--no-ext-diff", "--no-renames", "--src-prefix=a/", "--dst-prefix=b/"];
|
|
2115
2181
|
function getChangedLines(root, scope = {}) {
|
|
2116
2182
|
if (!hasHead(root)) {
|
|
2117
|
-
|
|
2118
|
-
return new Map(files.map((file) => [file, "all"]));
|
|
2119
|
-
}
|
|
2120
|
-
let args;
|
|
2121
|
-
if (scope.staged) {
|
|
2122
|
-
args = [...DIFF_FLAGS, "--cached", "HEAD"];
|
|
2123
|
-
} else if (scope.base) {
|
|
2124
|
-
const mergeBase = gitOrThrow(root, ["merge-base", scope.base, "HEAD"]).trim();
|
|
2125
|
-
args = [...DIFF_FLAGS, mergeBase];
|
|
2126
|
-
} else {
|
|
2127
|
-
args = [...DIFF_FLAGS, "HEAD"];
|
|
2183
|
+
return new Map(listFiles2(root).map((file) => [file, "all"]));
|
|
2128
2184
|
}
|
|
2129
|
-
const
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
}
|
|
2185
|
+
const target = scope.base ? gitOrThrow(root, ["merge-base", scope.base, "HEAD"]).trim() : "HEAD";
|
|
2186
|
+
const changed = parseUnifiedDiff(gitOrThrow(root, [...DIFF_FLAGS, target]));
|
|
2187
|
+
for (const file of listUntrackedFiles(root)) {
|
|
2188
|
+
changed.set(file, "all");
|
|
2134
2189
|
}
|
|
2135
2190
|
return changed;
|
|
2136
2191
|
}
|
|
@@ -2245,7 +2300,7 @@ var Project = class {
|
|
|
2245
2300
|
throw new Error("comments: not a git repository, so pass files or directories explicitly");
|
|
2246
2301
|
}
|
|
2247
2302
|
for (const [file, lines] of getChangedLines(this.root, scope)) {
|
|
2248
|
-
add(path4.join(this.root, file), lines);
|
|
2303
|
+
add(path4.join(this.root, file), scope.wholeFiles ? "all" : lines);
|
|
2249
2304
|
}
|
|
2250
2305
|
return files;
|
|
2251
2306
|
}
|
|
@@ -2756,8 +2811,8 @@ Targets are files, directories, file:line, or file:start-end. Without targets, o
|
|
|
2756
2811
|
overlapping uncommitted changes (and untracked files) are considered.
|
|
2757
2812
|
|
|
2758
2813
|
Options:
|
|
2759
|
-
--staged Only consider staged changes
|
|
2760
2814
|
--base <ref> Consider changes since the merge base with <ref>
|
|
2815
|
+
--whole-files Consider every comment in the changed files, not only comments on changed lines
|
|
2761
2816
|
--repo Consider every file in the repository
|
|
2762
2817
|
--suspicious Only comments with layout issues (overflow, early line ends, last lines of one or two words)
|
|
2763
2818
|
or hints (possible orphans, em-dashes, banners, ...). For agent and hook (the default), only
|
|
@@ -2777,7 +2832,7 @@ Config overrides (take precedence over the config file):
|
|
|
2777
2832
|
--width <n> Preferred comment line length
|
|
2778
2833
|
--max-width <n> Hard limit for prose (default: 120, never below width)
|
|
2779
2834
|
--single-line-width <n> Longest line a one-paragraph comment may stay on (default: 120)
|
|
2780
|
-
--tolerance <n>
|
|
2835
|
+
--tolerance <n> Columns prose may go past width to save a line, within max-width (default: 0)
|
|
2781
2836
|
--[no-]orphans Keep a sentence's first word off the end of a line, and end the first of two lines
|
|
2782
2837
|
with a sentence when the second is no longer than the first (default: on)
|
|
2783
2838
|
--[no-]style Look for possible semicolons, em-dashes, dashes, label colons, emphasis, and banners
|
|
@@ -2803,8 +2858,8 @@ var OPTIONS = {
|
|
|
2803
2858
|
"max-files": { type: "string" },
|
|
2804
2859
|
"max-agents": { type: "string" },
|
|
2805
2860
|
instructions: { type: "string" },
|
|
2806
|
-
staged: { type: "boolean" },
|
|
2807
2861
|
base: { type: "string" },
|
|
2862
|
+
"whole-files": { type: "boolean" },
|
|
2808
2863
|
repo: { type: "boolean" },
|
|
2809
2864
|
suspicious: { type: "boolean" },
|
|
2810
2865
|
all: { type: "boolean" },
|
|
@@ -2891,7 +2946,7 @@ async function createProject(cwd, values) {
|
|
|
2891
2946
|
return { project, configPath: loaded.filepath };
|
|
2892
2947
|
}
|
|
2893
2948
|
function scopeFrom(positionals, values) {
|
|
2894
|
-
return { targets: positionals,
|
|
2949
|
+
return { targets: positionals, base: values.base, wholeFiles: values["whole-files"], repo: values.repo };
|
|
2895
2950
|
}
|
|
2896
2951
|
function selectFrom(values, project) {
|
|
2897
2952
|
if (values.all) {
|
|
@@ -2952,9 +3007,8 @@ function commentsOf(analyses) {
|
|
|
2952
3007
|
function hintEmptyScope(analyses, scope) {
|
|
2953
3008
|
const isDefaultScope = (scope.targets ?? []).length === 0 && !scope.repo && !scope.base;
|
|
2954
3009
|
if (analyses.length === 0 && isDefaultScope) {
|
|
2955
|
-
const changes = scope.staged ? "staged changes" : "uncommitted changes";
|
|
2956
3010
|
log2(
|
|
2957
|
-
|
|
3011
|
+
"comments: no uncommitted changes in JavaScript or TypeScript files. Pass --repo, --base <ref>, or files to widen the scope."
|
|
2958
3012
|
);
|
|
2959
3013
|
}
|
|
2960
3014
|
}
|