@aforemendude/prettier-plugin-wrap-comments 1.0.6 → 1.1.1

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.
Files changed (76) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +85 -8
  3. package/dist/comments/comment-body.d.ts +4 -0
  4. package/dist/comments/comment-body.js +36 -0
  5. package/dist/comments/comment-directives.d.ts +2 -0
  6. package/dist/comments/comment-directives.js +52 -0
  7. package/dist/comments/comment-eligibility.d.ts +5 -0
  8. package/dist/comments/comment-eligibility.js +23 -0
  9. package/dist/comments/comment-location.d.ts +9 -0
  10. package/dist/comments/comment-location.js +39 -0
  11. package/dist/comments/comment-ranges.d.ts +24 -0
  12. package/dist/comments/comment-ranges.js +36 -0
  13. package/dist/comments/embedded-expression-ranges.d.ts +12 -0
  14. package/dist/comments/embedded-expression-ranges.js +117 -0
  15. package/dist/comments/jsx-expression-layout.d.ts +10 -0
  16. package/dist/comments/jsx-expression-layout.js +114 -0
  17. package/dist/comments/line-comment-groups.d.ts +6 -0
  18. package/dist/comments/line-comment-groups.js +28 -0
  19. package/dist/comments/prettier-ignore.d.ts +9 -0
  20. package/dist/comments/prettier-ignore.js +239 -0
  21. package/dist/comments/printer-layout.d.ts +18 -0
  22. package/dist/comments/printer-layout.js +65 -0
  23. package/dist/comments/{block.d.ts → wrap-block-comment.d.ts} +5 -1
  24. package/dist/comments/{block.js → wrap-block-comment.js} +18 -15
  25. package/dist/comments/wrap-comments.d.ts +9 -0
  26. package/dist/comments/wrap-comments.js +169 -0
  27. package/dist/comments/wrap-line-comment-group.d.ts +4 -0
  28. package/dist/comments/wrap-line-comment-group.js +39 -0
  29. package/dist/comments/wrap-trailing-line-comment.d.ts +9 -0
  30. package/dist/comments/wrap-trailing-line-comment.js +78 -0
  31. package/dist/index.d.ts +2 -2
  32. package/dist/index.js +4 -4
  33. package/dist/plugin/create-parsers.d.ts +2 -0
  34. package/dist/plugin/create-parsers.js +60 -0
  35. package/dist/plugin/create-printers.d.ts +2 -0
  36. package/dist/plugin/create-printers.js +59 -0
  37. package/dist/plugin/get-printer-layout-source.d.ts +4 -0
  38. package/dist/plugin/get-printer-layout-source.js +35 -0
  39. package/dist/plugin/jsx-comment-rewrite-metadata.d.ts +8 -0
  40. package/dist/plugin/jsx-comment-rewrite-metadata.js +40 -0
  41. package/dist/plugin/parser-names.d.ts +2 -0
  42. package/dist/plugin/parser-names.js +1 -0
  43. package/dist/utils/ast.d.ts +12 -0
  44. package/dist/utils/ast.js +95 -0
  45. package/dist/utils/display-width.d.ts +2 -0
  46. package/dist/utils/display-width.js +19 -0
  47. package/dist/{shared/markdown.d.ts → utils/format-markdown.d.ts} +1 -1
  48. package/dist/utils/format-markdown.js +69 -0
  49. package/dist/utils/indentation.d.ts +4 -0
  50. package/dist/utils/indentation.js +21 -0
  51. package/dist/utils/replacements.d.ts +6 -0
  52. package/dist/utils/replacements.js +21 -0
  53. package/dist/utils/source-lines.d.ts +7 -0
  54. package/dist/utils/source-lines.js +43 -0
  55. package/dist/utils/type-guards.d.ts +2 -0
  56. package/dist/utils/type-guards.js +6 -0
  57. package/dist/utils/whitespace.d.ts +3 -0
  58. package/dist/utils/whitespace.js +26 -0
  59. package/dist/{shared/options.d.ts → utils/wrap-options.d.ts} +2 -1
  60. package/package.json +27 -11
  61. package/dist/comments/core.d.ts +0 -8
  62. package/dist/comments/core.js +0 -114
  63. package/dist/comments/line.d.ts +0 -6
  64. package/dist/comments/line.js +0 -108
  65. package/dist/comments/wrap.d.ts +0 -3
  66. package/dist/comments/wrap.js +0 -439
  67. package/dist/plugin/parsers.d.ts +0 -2
  68. package/dist/plugin/parsers.js +0 -35
  69. package/dist/plugin/printers.d.ts +0 -2
  70. package/dist/plugin/printers.js +0 -39
  71. package/dist/shared/markdown.js +0 -20
  72. package/dist/shared/text.d.ts +0 -15
  73. package/dist/shared/text.js +0 -99
  74. package/dist/shared/types.d.ts +0 -30
  75. package/dist/shared/types.js +0 -1
  76. /package/dist/{shared/options.js → utils/wrap-options.js} +0 -0
@@ -0,0 +1,95 @@
1
+ import { isRecord, numberOrUndefined } from './type-guards.js';
2
+ const AST_TRAVERSAL_SKIP_KEYS = new Set([
3
+ 'comments',
4
+ 'errors',
5
+ 'innerComments',
6
+ 'leadingComments',
7
+ 'loc',
8
+ 'parent',
9
+ 'range',
10
+ 'tokens',
11
+ 'trailingComments',
12
+ ]);
13
+ export function visitAstNodes(ast, visitor) {
14
+ const seen = new Set();
15
+ visit(ast);
16
+ function visit(value) {
17
+ if (!isRecord(value) || seen.has(value)) {
18
+ return;
19
+ }
20
+ seen.add(value);
21
+ if (typeof value['type'] === 'string') {
22
+ visitor(value);
23
+ }
24
+ for (const [key, child] of Object.entries(value)) {
25
+ if (AST_TRAVERSAL_SKIP_KEYS.has(key)) {
26
+ continue;
27
+ }
28
+ if (Array.isArray(child)) {
29
+ for (const item of child) {
30
+ visit(item);
31
+ }
32
+ }
33
+ else {
34
+ visit(child);
35
+ }
36
+ }
37
+ }
38
+ }
39
+ export function getAstNodeRange(node) {
40
+ const start = numberOrUndefined(node['start']) ?? getRangeNumber(node['range'], 0);
41
+ const end = numberOrUndefined(node['end']) ?? getRangeNumber(node['range'], 1);
42
+ if (start === undefined || end === undefined || start >= end) {
43
+ return undefined;
44
+ }
45
+ return { end, start };
46
+ }
47
+ export function collectAstNodeRangesByStart(ast) {
48
+ const rangesByStart = new Map();
49
+ visitAstNodes(ast, (node) => {
50
+ const range = getAstNodeRange(node);
51
+ if (range === undefined) {
52
+ return;
53
+ }
54
+ const existingRange = rangesByStart.get(range.start);
55
+ if (existingRange === undefined || range.end > existingRange.end) {
56
+ rangesByStart.set(range.start, range);
57
+ }
58
+ });
59
+ return rangesByStart;
60
+ }
61
+ export function matchOrderedRangesToSmallestContainers(targets, containers) {
62
+ const matches = [];
63
+ const openContainers = [];
64
+ let containerIndex = 0;
65
+ for (const target of targets) {
66
+ let container = containers[containerIndex];
67
+ while (container !== undefined && container.start < target.start) {
68
+ let previousContainer = openContainers[openContainers.length - 1];
69
+ while (previousContainer !== undefined && previousContainer.range.end <= container.start) {
70
+ openContainers.pop();
71
+ previousContainer = openContainers[openContainers.length - 1];
72
+ }
73
+ if (previousContainer === undefined ||
74
+ previousContainer.range.start !== container.start ||
75
+ previousContainer.range.end !== container.end) {
76
+ openContainers.push({ index: containerIndex, range: container });
77
+ }
78
+ containerIndex += 1;
79
+ container = containers[containerIndex];
80
+ }
81
+ let containingRange = openContainers[openContainers.length - 1];
82
+ while (containingRange !== undefined && containingRange.range.end <= target.end) {
83
+ openContainers.pop();
84
+ containingRange = openContainers[openContainers.length - 1];
85
+ }
86
+ matches.push(containingRange);
87
+ }
88
+ return matches;
89
+ }
90
+ function getRangeNumber(range, index) {
91
+ if (!Array.isArray(range)) {
92
+ return undefined;
93
+ }
94
+ return numberOrUndefined(range[index]);
95
+ }
@@ -0,0 +1,2 @@
1
+ export declare function getColumnAt(text: string, index: number, tabWidth: number): number;
2
+ export declare function getColumns(text: string, tabWidth: number, startColumn?: number): number;
@@ -0,0 +1,19 @@
1
+ import { util } from 'prettier';
2
+ import { getLineStart } from './source-lines.js';
3
+ export function getColumnAt(text, index, tabWidth) {
4
+ return getColumns(text.slice(getLineStart(text, index), index), tabWidth);
5
+ }
6
+ export function getColumns(text, tabWidth, startColumn = 0) {
7
+ let column = startColumn;
8
+ let segmentStart = 0;
9
+ for (let index = 0; index < text.length; index += 1) {
10
+ if (text[index] === '\t') {
11
+ column += util.getStringWidth(text.slice(segmentStart, index));
12
+ if (tabWidth > 0) {
13
+ column += tabWidth - (column % tabWidth);
14
+ }
15
+ segmentStart = index + 1;
16
+ }
17
+ }
18
+ return column + util.getStringWidth(text.slice(segmentStart)) - startColumn;
19
+ }
@@ -1,2 +1,2 @@
1
- import type { WrapOptions } from './types.js';
1
+ import type { WrapOptions } from './wrap-options.js';
2
2
  export declare function formatMarkdownLines(markdown: string, printWidth: number, options: WrapOptions): Promise<string[]>;
@@ -0,0 +1,69 @@
1
+ import { format } from 'prettier';
2
+ import { isBlankLine, normalizeLineTerminators } from './source-lines.js';
3
+ import { getTabWidth } from './wrap-options.js';
4
+ const MAX_MARKDOWN_FORMAT_PASSES = 3;
5
+ export async function formatMarkdownLines(markdown, printWidth, options) {
6
+ const normalized = trimBlankEdges(normalizeLineTerminators(markdown));
7
+ let formatted;
8
+ try {
9
+ formatted = await formatMarkdown(normalized, printWidth, options);
10
+ }
11
+ catch {
12
+ return normalized.split('\n');
13
+ }
14
+ if (startsWithThematicRule(normalized)) {
15
+ formatted = await stabilizePotentialFrontMatter(formatted, printWidth, options);
16
+ }
17
+ return formatted.replace(/\n$/, '').split('\n');
18
+ }
19
+ async function formatMarkdown(markdown, printWidth, options) {
20
+ return format(markdown, {
21
+ endOfLine: 'lf',
22
+ parser: 'markdown',
23
+ printWidth,
24
+ proseWrap: 'always',
25
+ tabWidth: getTabWidth(options),
26
+ useTabs: options.useTabs,
27
+ });
28
+ }
29
+ async function stabilizePotentialFrontMatter(formatted, printWidth, options) {
30
+ const seen = new Set([formatted]);
31
+ let current = formatted;
32
+ for (let pass = 1; pass < MAX_MARKDOWN_FORMAT_PASSES && hasPotentialFrontMatterAmbiguity(current); pass += 1) {
33
+ let next;
34
+ try {
35
+ next = await formatMarkdown(current, printWidth, options);
36
+ }
37
+ catch {
38
+ return current;
39
+ }
40
+ if (next === current || seen.has(next)) {
41
+ return current;
42
+ }
43
+ seen.add(next);
44
+ current = next;
45
+ }
46
+ return current;
47
+ }
48
+ function startsWithThematicRule(markdown) {
49
+ const [firstLine = ''] = markdown.split('\n', 1);
50
+ return /^[ \t]{0,3}([*_-])(?:[ \t]*\1){2,}[ \t]*$/u.test(firstLine);
51
+ }
52
+ function hasPotentialFrontMatterAmbiguity(markdown) {
53
+ const lines = markdown.replace(/\n$/u, '').split('\n');
54
+ if (lines[0] !== '---') {
55
+ return false;
56
+ }
57
+ const closingDelimiterIndex = lines.indexOf('---', 1);
58
+ return closingDelimiterIndex > 1 && (isBlankLine(lines[1]) || isBlankLine(lines[closingDelimiterIndex - 1]));
59
+ }
60
+ function trimBlankEdges(markdown) {
61
+ const lines = markdown.split('\n');
62
+ while (isBlankLine(lines[0])) {
63
+ lines.shift();
64
+ }
65
+ while (isBlankLine(lines.at(-1))) {
66
+ lines.pop();
67
+ }
68
+ return lines.join('\n');
69
+ }
@@ -0,0 +1,4 @@
1
+ import type { WrapOptions } from './wrap-options.js';
2
+ export declare function getContinuationIndent(text: string, commentStart: number, markerColumn: number, options: WrapOptions): string;
3
+ export declare function getLeadingIndent(text: string): string;
4
+ export declare function makeIndent(column: number, options: WrapOptions): string;
@@ -0,0 +1,21 @@
1
+ import { getLinePrefix } from './source-lines.js';
2
+ import { getTabWidth } from './wrap-options.js';
3
+ export function getContinuationIndent(text, commentStart, markerColumn, options) {
4
+ const linePrefix = getLinePrefix(text, commentStart);
5
+ if (/^[ \t]*$/u.test(linePrefix)) {
6
+ return linePrefix;
7
+ }
8
+ return makeIndent(markerColumn, options);
9
+ }
10
+ export function getLeadingIndent(text) {
11
+ return /^[ \t]*/u.exec(text)?.[0] ?? '';
12
+ }
13
+ export function makeIndent(column, options) {
14
+ const tabWidth = getTabWidth(options);
15
+ if (options.useTabs === true && tabWidth > 0) {
16
+ const tabs = Math.floor(column / tabWidth);
17
+ const spaces = column % tabWidth;
18
+ return `${'\t'.repeat(tabs)}${' '.repeat(spaces)}`;
19
+ }
20
+ return ' '.repeat(column);
21
+ }
@@ -0,0 +1,6 @@
1
+ export type Replacement = {
2
+ end: number;
3
+ start: number;
4
+ text: string;
5
+ };
6
+ export declare function applyReplacements(text: string, replacements: Replacement[]): string;
@@ -0,0 +1,21 @@
1
+ export function applyReplacements(text, replacements) {
2
+ let result = text;
3
+ for (const replacement of getNonOverlappingReplacements(replacements).sort((left, right) => right.start - left.start)) {
4
+ result = result.slice(0, replacement.start) + replacement.text + result.slice(replacement.end);
5
+ }
6
+ return result;
7
+ }
8
+ function getNonOverlappingReplacements(replacements) {
9
+ return [...replacements]
10
+ .sort((left, right) => left.start - right.start || right.end - left.end)
11
+ .reduce((accepted, replacement) => {
12
+ const previous = accepted.at(-1);
13
+ if (previous === undefined || !rangesOverlap(previous, replacement)) {
14
+ accepted.push(replacement);
15
+ }
16
+ return accepted;
17
+ }, []);
18
+ }
19
+ function rangesOverlap(left, right) {
20
+ return left.start < right.end && right.start < left.end;
21
+ }
@@ -0,0 +1,7 @@
1
+ import type { WrapOptions } from './wrap-options.js';
2
+ export declare function normalizeLineTerminators(text: string): string;
3
+ export declare function getPreferredNewline(text: string, options: WrapOptions): string;
4
+ export declare function getLinePrefix(text: string, index: number): string;
5
+ export declare function getLineStart(text: string, index: number): number;
6
+ export declare function getLineEnd(text: string, index: number): number;
7
+ export declare function isBlankLine(line: string | undefined): boolean;
@@ -0,0 +1,43 @@
1
+ export function normalizeLineTerminators(text) {
2
+ return text.replace(/\r\n|[\r\u2028\u2029]/gu, '\n');
3
+ }
4
+ export function getPreferredNewline(text, options) {
5
+ if (options.endOfLine === 'crlf') {
6
+ return '\r\n';
7
+ }
8
+ if (options.endOfLine === 'cr') {
9
+ return '\r';
10
+ }
11
+ if (options.endOfLine === 'auto') {
12
+ const match = /\r\n|\n|\r/u.exec(text);
13
+ return match?.[0] ?? '\n';
14
+ }
15
+ return '\n';
16
+ }
17
+ export function getLinePrefix(text, index) {
18
+ return text.slice(getLineStart(text, index), index);
19
+ }
20
+ export function getLineStart(text, index) {
21
+ for (let cursor = Math.min(index, text.length) - 1; cursor >= 0; cursor -= 1) {
22
+ const character = text[cursor];
23
+ if (character === '\n' || character === '\u2028' || character === '\u2029') {
24
+ return cursor + 1;
25
+ }
26
+ if (character === '\r' && text[cursor + 1] !== '\n') {
27
+ return cursor + 1;
28
+ }
29
+ }
30
+ return 0;
31
+ }
32
+ export function getLineEnd(text, index) {
33
+ for (let cursor = Math.max(index, 0); cursor < text.length; cursor += 1) {
34
+ const character = text[cursor];
35
+ if (character === '\r' || character === '\n' || character === '\u2028' || character === '\u2029') {
36
+ return character === '\n' && text[cursor - 1] === '\r' ? cursor - 1 : cursor;
37
+ }
38
+ }
39
+ return text.length;
40
+ }
41
+ export function isBlankLine(line) {
42
+ return line !== undefined && line.trim() === '';
43
+ }
@@ -0,0 +1,2 @@
1
+ export declare function isRecord(value: unknown): value is Record<string, unknown>;
2
+ export declare function numberOrUndefined(value: unknown): number | undefined;
@@ -0,0 +1,6 @@
1
+ export function isRecord(value) {
2
+ return typeof value === 'object' && value !== null;
3
+ }
4
+ export function numberOrUndefined(value) {
5
+ return typeof value === 'number' ? value : undefined;
6
+ }
@@ -0,0 +1,3 @@
1
+ export declare function isEcmaScriptHorizontalWhitespace(character: string): boolean;
2
+ export declare function skipWhitespace(text: string, index: number): number;
3
+ export declare function trimWhitespaceEnd(text: string, start: number, end: number): number;
@@ -0,0 +1,26 @@
1
+ const ECMASCRIPT_HORIZONTAL_WHITESPACE_PATTERN = /^[\t\u000b\u000c \u00a0\u1680\u2000-\u200a\u202f\u205f\u3000\ufeff]$/u;
2
+ export function isEcmaScriptHorizontalWhitespace(character) {
3
+ return ECMASCRIPT_HORIZONTAL_WHITESPACE_PATTERN.test(character);
4
+ }
5
+ export function skipWhitespace(text, index) {
6
+ let cursor = index;
7
+ while (cursor < text.length) {
8
+ const character = text[cursor];
9
+ if (character === undefined || !/\s/u.test(character)) {
10
+ break;
11
+ }
12
+ cursor += 1;
13
+ }
14
+ return cursor;
15
+ }
16
+ export function trimWhitespaceEnd(text, start, end) {
17
+ let cursor = end;
18
+ while (cursor > start) {
19
+ const character = text[cursor - 1];
20
+ if (character === undefined || !/\s/u.test(character)) {
21
+ break;
22
+ }
23
+ cursor -= 1;
24
+ }
25
+ return cursor;
26
+ }
@@ -1,4 +1,5 @@
1
- import type { WrapOptions } from './types.js';
1
+ import type { ParserOptions } from 'prettier';
2
+ export type WrapOptions = Pick<ParserOptions, 'endOfLine' | 'printWidth' | 'tabWidth' | 'useTabs'>;
2
3
  export declare function getAvailableContentWidth(options: WrapOptions, contentStartColumn: number): number;
3
4
  export declare function getPrintWidth(options: WrapOptions): number;
4
5
  export declare function getTabWidth(options: WrapOptions): number;
package/package.json CHANGED
@@ -5,11 +5,21 @@
5
5
  },
6
6
  "description": "A Prettier plugin that wraps non-JSDoc JavaScript and TypeScript comments as Markdown.",
7
7
  "devDependencies": {
8
- "prettier": "3.8.3",
9
- "typescript": "6.0.3"
8
+ "@types/node": "20.19.43",
9
+ "prettier": "3.9.6",
10
+ "prettier-plugin-wrap-comments-stable": "npm:@aforemendude/prettier-plugin-wrap-comments@1.0.6",
11
+ "typescript": "7.0.2",
12
+ "vite": "8.2.0",
13
+ "vitest": "4.1.10"
14
+ },
15
+ "devEngines": {
16
+ "runtime": {
17
+ "name": "node",
18
+ "version": ">=22.12.0"
19
+ }
10
20
  },
11
21
  "engines": {
12
- "node": ">=18"
22
+ "node": ">=20.0.0"
13
23
  },
14
24
  "exports": {
15
25
  ".": {
@@ -32,7 +42,7 @@
32
42
  "main": "./dist/index.js",
33
43
  "name": "@aforemendude/prettier-plugin-wrap-comments",
34
44
  "peerDependencies": {
35
- "prettier": ">=3.0.0"
45
+ "prettier": ">=3.0.0 <4.0.0"
36
46
  },
37
47
  "publishConfig": {
38
48
  "access": "public"
@@ -42,14 +52,20 @@
42
52
  "url": "git+https://github.com/aforemendude/prettier-plugin-wrap-comments.git"
43
53
  },
44
54
  "scripts": {
45
- "build": "rm -rf dist && tsc -p tsconfig.json",
46
- "format": "prettier --write .",
47
- "format:check": "prettier --check .",
48
- "prepack": "npm run verify",
49
- "test": "npm run build && node --test test/*.test.mjs",
50
- "verify": "npm install && npm run format:check && npm run test"
55
+ "benchmark": "vitest bench --run",
56
+ "build": "npm run clean && tsc -p tsconfig.build.json",
57
+ "clean": "node scripts/clean.mjs",
58
+ "format": "prettier --write --cache --cache-strategy metadata .",
59
+ "format:check": "prettier --check --cache --cache-strategy metadata .",
60
+ "format:nocache": "prettier --write .",
61
+ "prepack": "npm install && git diff --exit-code -- package-lock.json && npm run verify",
62
+ "test": "vitest run",
63
+ "test:integration": "vitest run test/integration",
64
+ "test:unit": "vitest run test/unit",
65
+ "typecheck": "tsc -p tsconfig.json",
66
+ "verify": "prettier --check . && npm run typecheck && npm run build && npm run test"
51
67
  },
52
68
  "type": "module",
53
69
  "types": "./dist/index.d.ts",
54
- "version": "1.0.6"
70
+ "version": "1.1.1"
55
71
  }
@@ -1,8 +0,0 @@
1
- import type { CommentRange, RawComment } from '../shared/types.js';
2
- export declare function collectComments(ast: unknown): RawComment[];
3
- export declare function toCommentRange(comment: RawComment, text: string): CommentRange | undefined;
4
- export declare function normalizeLineCommentBody(rawBody: string): string;
5
- export declare function normalizeBlockCommentBody(rawComment: string): string;
6
- export declare function isDirectiveComment(body: string): boolean;
7
- export declare function isPrettierIgnoreComment(body: string): boolean;
8
- export declare function hasPreserveCommentMarker(rawComment: string): boolean;
@@ -1,114 +0,0 @@
1
- export function collectComments(ast) {
2
- const candidate = ast;
3
- if (Array.isArray(candidate.comments)) {
4
- return candidate.comments;
5
- }
6
- if (Array.isArray(candidate.program?.comments)) {
7
- return candidate.program.comments;
8
- }
9
- return [];
10
- }
11
- export function toCommentRange(comment, text) {
12
- const range = Array.isArray(comment.range) ? comment.range : undefined;
13
- const start = numberOrUndefined(comment.start) ?? numberOrUndefined(range?.[0]);
14
- const end = numberOrUndefined(comment.end) ?? numberOrUndefined(range?.[1]);
15
- if (start === undefined || end === undefined || start >= end) {
16
- return undefined;
17
- }
18
- const rawStart = text.slice(start, start + 3);
19
- if (rawStart.startsWith('//')) {
20
- return { end, kind: 'line', start };
21
- }
22
- if (rawStart.startsWith('/*')) {
23
- return { end, kind: 'block', start };
24
- }
25
- return undefined;
26
- }
27
- export function normalizeLineCommentBody(rawBody) {
28
- if (rawBody.trim() === '') {
29
- return '';
30
- }
31
- return rawBody.replace(/^[ \t]?/, '').replace(/[ \t]+$/u, '');
32
- }
33
- export function normalizeBlockCommentBody(rawComment) {
34
- const body = rawComment.slice(2, -2).replace(/\r\n?/g, '\n');
35
- const lines = body.split('\n');
36
- if (lines.length === 1) {
37
- return lines[0]?.trim() ?? '';
38
- }
39
- while (isBlankLine(lines[0])) {
40
- lines.shift();
41
- }
42
- while (isBlankLine(lines.at(-1))) {
43
- lines.pop();
44
- }
45
- return lines
46
- .map((line) => {
47
- const withoutIndent = line.replace(/^[ \t]*/u, '');
48
- if (!withoutIndent.startsWith('*')) {
49
- return withoutIndent.replace(/[ \t]+$/u, '');
50
- }
51
- return withoutIndent
52
- .slice(1)
53
- .replace(/^[ \t]?/u, '')
54
- .replace(/[ \t]+$/u, '');
55
- })
56
- .join('\n');
57
- }
58
- const PRAGMA_DIRECTIVE_COMMENT_PATTERNS = [
59
- /^@(?:license|preserve)\b/u,
60
- /^@(?:jsxFrag|jsxImportSource|jsxRuntime|jsx)\b/u,
61
- /^@(?:ts-check|ts-expect-error|ts-ignore|ts-nocheck)\b/u,
62
- /^[@#]__(?:NO_SIDE_EFFECTS|PURE)__\b/u,
63
- ];
64
- const SOURCE_MAP_DIRECTIVE_COMMENT_PATTERNS = [
65
- /^[#@][ \t]*sourceMappingURL=/u,
66
- /^[#@][ \t]*sourceURL=/u,
67
- /^sourceMappingURL=/u,
68
- /^sourceURL=/u,
69
- ];
70
- const TOOL_DIRECTIVE_COMMENT_PATTERNS = [
71
- /^biome-ignore\b/u,
72
- /^c8\b/u,
73
- /^deno-lint-ignore\b/u,
74
- /^eslint\b/u,
75
- /^eslint-/u,
76
- /^exported\b/u,
77
- /^globals?\b/u,
78
- /^istanbul\b/u,
79
- /^jshint\b/u,
80
- /^nyc\b/u,
81
- /^oxlint\b/u,
82
- /^prettier-ignore\b/u,
83
- /^prettier-ignore-start\b/u,
84
- /^prettier-ignore-end\b/u,
85
- /^stylelint\b/u,
86
- /^tslint\b/u,
87
- /^v8\b/u,
88
- /^vite-ignore\b/u,
89
- ];
90
- const BUNDLER_DIRECTIVE_COMMENT_PATTERNS = [
91
- /^webpack(?:ChunkName|Exclude|Ignore|Include|Mode|Prefetch|Preload)\b/u,
92
- ];
93
- const DIRECTIVE_COMMENT_PATTERNS = [
94
- ...PRAGMA_DIRECTIVE_COMMENT_PATTERNS,
95
- ...SOURCE_MAP_DIRECTIVE_COMMENT_PATTERNS,
96
- ...TOOL_DIRECTIVE_COMMENT_PATTERNS,
97
- ...BUNDLER_DIRECTIVE_COMMENT_PATTERNS,
98
- ];
99
- export function isDirectiveComment(body) {
100
- const normalizedBody = body.trimStart();
101
- return DIRECTIVE_COMMENT_PATTERNS.some((pattern) => pattern.test(normalizedBody));
102
- }
103
- export function isPrettierIgnoreComment(body) {
104
- return body.trim() === 'prettier-ignore';
105
- }
106
- export function hasPreserveCommentMarker(rawComment) {
107
- return rawComment.startsWith('/*!') || rawComment.startsWith('//!');
108
- }
109
- function isBlankLine(line) {
110
- return line !== undefined && line.trim() === '';
111
- }
112
- function numberOrUndefined(value) {
113
- return typeof value === 'number' ? value : undefined;
114
- }
@@ -1,6 +0,0 @@
1
- import type { CommentRange, Replacement, WrapOptions } from '../shared/types.js';
2
- export declare function wrapLineCommentGroup(text: string, comments: CommentRange[], options: WrapOptions): Promise<Replacement | undefined>;
3
- export declare function wrapTrailingLineComment(text: string, comment: CommentRange, options: WrapOptions): Promise<Replacement[] | undefined>;
4
- export declare function shouldSkipLineComment(text: string, comment: CommentRange): boolean;
5
- export declare function isStandaloneLineComment(text: string, comment: CommentRange): boolean;
6
- export declare function areAdjacentLineComments(text: string, previous: CommentRange, next: CommentRange): boolean;
@@ -1,108 +0,0 @@
1
- import { hasPreserveCommentMarker, isDirectiveComment, normalizeLineCommentBody } from './core.js';
2
- import { formatMarkdownLines } from '../shared/markdown.js';
3
- import { getAvailableContentWidth, getPrintWidth, getTabWidth } from '../shared/options.js';
4
- import { getColumnAt, getColumns, getContinuationIndent, getLineEnd, getLinePrefix, getLineStart, getPreferredNewline, makeIndent, } from '../shared/text.js';
5
- export async function wrapLineCommentGroup(text, comments, options) {
6
- const firstComment = comments[0];
7
- if (firstComment === undefined) {
8
- return undefined;
9
- }
10
- const lastComment = comments.at(-1) ?? firstComment;
11
- const bodyLines = comments.map((comment) => normalizeLineCommentBody(text.slice(comment.start + 2, comment.end)));
12
- if (bodyLines.every((line) => line.trim() === '')) {
13
- return undefined;
14
- }
15
- const tabWidth = getTabWidth(options);
16
- const markerColumn = getColumnAt(text, firstComment.start, tabWidth);
17
- const availableWidth = getAvailableContentWidth(options, markerColumn + 3);
18
- const formattedLines = await formatMarkdownLines(bodyLines.join('\n'), availableWidth, options);
19
- const newline = getPreferredNewline(text, options);
20
- const continuationIndent = getContinuationIndent(text, firstComment.start, markerColumn, options);
21
- const replacementText = formattedLines
22
- .map((line, index) => {
23
- const commentText = line.length === 0 ? '//' : `// ${line}`;
24
- return index === 0 ? commentText : `${newline}${continuationIndent}${commentText}`;
25
- })
26
- .join('');
27
- const start = firstComment.start;
28
- const end = lastComment.end;
29
- if (replacementText === text.slice(start, end)) {
30
- return undefined;
31
- }
32
- return {
33
- end,
34
- start,
35
- text: replacementText,
36
- };
37
- }
38
- export async function wrapTrailingLineComment(text, comment, options) {
39
- if (isTrailingLineCommentWithinPrintWidth(text, comment, options)) {
40
- return undefined;
41
- }
42
- const lineStart = getLineStart(text, comment.start);
43
- const lineEnd = getLineEnd(text, comment.end);
44
- const linePrefix = text.slice(lineStart, comment.start);
45
- const codeText = linePrefix.replace(/[ \t]+$/u, '');
46
- if (codeText.trim() === '') {
47
- return undefined;
48
- }
49
- const body = normalizeLineCommentBody(text.slice(comment.start + 2, comment.end));
50
- if (body.trim() === '') {
51
- return undefined;
52
- }
53
- const tabWidth = getTabWidth(options);
54
- const indent = getTrailingCommentIndent(codeText, linePrefix, options);
55
- const availableWidth = getAvailableContentWidth(options, getColumns(indent, tabWidth) + 3);
56
- const formattedLines = await formatMarkdownLines(body, availableWidth, options);
57
- const newline = getPreferredNewline(text, options);
58
- const leadingCommentText = formattedLines
59
- .map((line) => `${indent}${line.length === 0 ? '//' : `// ${line}`}`)
60
- .join(newline);
61
- const codeEnd = lineStart + codeText.length;
62
- return [
63
- {
64
- end: lineStart,
65
- start: lineStart,
66
- text: `${leadingCommentText}${newline}`,
67
- },
68
- {
69
- end: lineEnd,
70
- start: codeEnd,
71
- text: '',
72
- },
73
- ];
74
- }
75
- export function shouldSkipLineComment(text, comment) {
76
- const raw = text.slice(comment.start, comment.end);
77
- if (raw.startsWith('///') || hasPreserveCommentMarker(raw)) {
78
- return true;
79
- }
80
- return isDirectiveComment(normalizeLineCommentBody(raw.slice(2)));
81
- }
82
- export function isStandaloneLineComment(text, comment) {
83
- return /^[ \t]*$/u.test(getLinePrefix(text, comment.start));
84
- }
85
- export function areAdjacentLineComments(text, previous, next) {
86
- return /^(?:\r\n|\n|\r)[ \t]*$/u.test(text.slice(previous.end, next.start));
87
- }
88
- function isTrailingLineCommentWithinPrintWidth(text, comment, options) {
89
- const tabWidth = getTabWidth(options);
90
- const lineStart = getLineStart(text, comment.start);
91
- const lineEnd = getLineEnd(text, comment.end);
92
- const lineText = text.slice(lineStart, lineEnd).replace(/[ \t]+$/u, '');
93
- return getColumns(lineText, tabWidth) <= getPrintWidth(options);
94
- }
95
- function getLineIndent(linePrefix) {
96
- return /^[ \t]*/u.exec(linePrefix)?.[0] ?? '';
97
- }
98
- function getTrailingCommentIndent(codeText, linePrefix, options) {
99
- const indent = getLineIndent(linePrefix);
100
- if (!isClosingDelimiterLine(codeText)) {
101
- return indent;
102
- }
103
- const tabWidth = getTabWidth(options);
104
- return makeIndent(getColumns(indent, tabWidth) + tabWidth, options);
105
- }
106
- function isClosingDelimiterLine(codeText) {
107
- return /^[ \t]*[\])}]+[\])};,]*[ \t]*$/u.test(codeText);
108
- }
@@ -1,3 +0,0 @@
1
- import type { WrapOptions } from '../shared/types.js';
2
- export declare function wrapComments<T>(text: string, ast: T, options: WrapOptions): Promise<string>;
3
- export declare function neutralizePrettierIgnoreForIgnoredComments<T>(text: string, ast: T): T;