@aforemendude/prettier-plugin-wrap-comments 1.1.0 → 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.
- package/README.md +62 -3
- package/dist/comments/comment-body.js +2 -2
- package/dist/comments/comment-directives.js +4 -0
- package/dist/comments/comment-eligibility.d.ts +1 -0
- package/dist/comments/comment-eligibility.js +4 -1
- package/dist/comments/comment-location.js +24 -6
- package/dist/comments/embedded-expression-ranges.d.ts +2 -3
- package/dist/comments/embedded-expression-ranges.js +39 -27
- package/dist/comments/jsx-expression-layout.d.ts +2 -2
- package/dist/comments/jsx-expression-layout.js +16 -19
- package/dist/comments/prettier-ignore.d.ts +2 -1
- package/dist/comments/prettier-ignore.js +110 -16
- package/dist/comments/printer-layout.d.ts +1 -0
- package/dist/comments/printer-layout.js +1 -0
- package/dist/comments/wrap-block-comment.d.ts +1 -0
- package/dist/comments/wrap-block-comment.js +3 -1
- package/dist/comments/wrap-comments.js +75 -7
- package/dist/comments/wrap-trailing-line-comment.d.ts +1 -1
- package/dist/plugin/create-parsers.js +4 -1
- package/dist/plugin/create-printers.js +23 -2
- package/dist/plugin/get-printer-layout-source.js +10 -2
- package/dist/utils/ast.d.ts +6 -0
- package/dist/utils/ast.js +43 -0
- package/dist/utils/display-width.d.ts +1 -1
- package/dist/utils/display-width.js +3 -3
- package/dist/utils/format-markdown.js +50 -11
- package/dist/utils/source-lines.d.ts +1 -0
- package/dist/utils/source-lines.js +3 -0
- package/dist/utils/whitespace.d.ts +1 -0
- package/dist/utils/whitespace.js +4 -0
- package/package.json +13 -5
package/README.md
CHANGED
|
@@ -5,7 +5,8 @@ real column to calculate the available content width, so nested comments wrap mo
|
|
|
5
5
|
|
|
6
6
|
## Requirements
|
|
7
7
|
|
|
8
|
-
- Node.js 20 or newer
|
|
8
|
+
- Node.js 20 or newer to use the plugin
|
|
9
|
+
- Node.js 22.12 or newer to develop the plugin
|
|
9
10
|
- Prettier 3 (`>=3.0.0 <4.0.0`)
|
|
10
11
|
|
|
11
12
|
## Install
|
|
@@ -30,6 +31,31 @@ Then run Prettier normally:
|
|
|
30
31
|
npx prettier --write .
|
|
31
32
|
```
|
|
32
33
|
|
|
34
|
+
### Cache Repeated CLI Runs
|
|
35
|
+
|
|
36
|
+
Prettier's CLI cache skips files that have not changed since a successful formatting pass. Enable it in package scripts
|
|
37
|
+
so contributors use it consistently. This repository uses the faster `metadata` strategy for local formatting:
|
|
38
|
+
|
|
39
|
+
```json
|
|
40
|
+
{
|
|
41
|
+
"scripts": {
|
|
42
|
+
"format": "prettier --write --cache --cache-strategy metadata .",
|
|
43
|
+
"format:check": "prettier --check --cache --cache-strategy metadata .",
|
|
44
|
+
"format:nocache": "prettier --write ."
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
The first cached run still processes every file; later runs skip files whose relevant metadata and other cache keys have
|
|
50
|
+
not changed. Omit `--cache-strategy metadata` to use the default `content` strategy when workflows such as Git
|
|
51
|
+
operations frequently change timestamps without changing file contents.
|
|
52
|
+
|
|
53
|
+
By default, Prettier stores the cache under `node_modules/.cache/prettier/`, which is normally excluded from version
|
|
54
|
+
control with `node_modules`. Prettier does not include plugin versions or implementations in its cache keys, so run the
|
|
55
|
+
uncached command once after updating this plugin or another Prettier plugin. Running Prettier without `--cache`, as the
|
|
56
|
+
`format:nocache` script does, also removes the default cache. See [Prettier's CLI cache documentation][prettier-cache]
|
|
57
|
+
for cache keys, strategies, and custom cache locations.
|
|
58
|
+
|
|
33
59
|
## Behavior
|
|
34
60
|
|
|
35
61
|
The plugin wraps comments for Prettier's `babel`, `babel-ts`, and `typescript` parsers. It runs during parser
|
|
@@ -148,10 +174,13 @@ The plugin leaves these comments unchanged:
|
|
|
148
174
|
- JSDoc comments that start with `/**`
|
|
149
175
|
- bang-preserved comments that start with `/*!` or `//!`
|
|
150
176
|
- TypeScript-style triple-slash line comments that start with `///`
|
|
177
|
+
- Flow type annotations and includes that start with `/*:`, `/*::`, or `/*flow-include`, including Flow's supported
|
|
178
|
+
spaces or tabs before the marker
|
|
151
179
|
- empty comment bodies
|
|
152
180
|
- `prettier-ignore` markers themselves
|
|
153
|
-
- other directive comments such as `@license`, `@preserve`, JSX and
|
|
154
|
-
`#__PURE__`, `@__PURE__`,
|
|
181
|
+
- other directive comments such as `@license`, `@preserve`, JSX, TypeScript, and Flow pragmas, Flow error suppressions,
|
|
182
|
+
`flowlint` comments, source map directives, `#__PURE__`, `@__PURE__`, exact Node test coverage controls, other
|
|
183
|
+
lint/coverage/formatter directives, `vite-ignore`, and webpack magic comments
|
|
155
184
|
|
|
156
185
|
## Supported Parsers
|
|
157
186
|
|
|
@@ -159,6 +188,28 @@ The plugin leaves these comments unchanged:
|
|
|
159
188
|
- `babel-ts`
|
|
160
189
|
- `typescript`
|
|
161
190
|
|
|
191
|
+
## Performance
|
|
192
|
+
|
|
193
|
+
`npm run benchmark` compares uncached, in-memory `prettier.format()` calls with and without the plugin across five
|
|
194
|
+
generated files. Lower times are better. The plugin/plain column divides the plugin mean by the plain Prettier mean.
|
|
195
|
+
|
|
196
|
+
These results are from a representative run on August 8, 2026, using Linux 6.8, an Intel Core i7-10750H, Node.js
|
|
197
|
+
24.18.0, Prettier 3.9.6, and Vitest 4.1.10. The suite was configured with `time: 500`, `iterations: 10`,
|
|
198
|
+
`warmupTime: 100`, and `warmupIterations: 2` for each case.
|
|
199
|
+
|
|
200
|
+
| Generated workload | Characters | Plain Prettier mean | Plugin mean | Plugin/plain |
|
|
201
|
+
| ------------------------------------------ | ---------: | ------------------: | -----------------: | -----------: |
|
|
202
|
+
| Comment-free JavaScript | 29,995 | 54.78 ms (±10.09%) | 45.59 ms (±5.82%) | 0.83× |
|
|
203
|
+
| Code-heavy JavaScript with sparse comments | 30,494 | 44.05 ms (±3.22%) | 135.66 ms (±3.81%) | 3.08× |
|
|
204
|
+
| Comment-heavy TypeScript | 64,432 | 56.75 ms (±11.82%) | 327.61 ms (±2.67%) | 5.77× |
|
|
205
|
+
| Preformatted comment-heavy TypeScript | 54,467 | 54.54 ms (±2.47%) | 279.65 ms (±2.50%) | 5.13× |
|
|
206
|
+
| JSX-comment-heavy TSX | 40,575 | 57.52 ms (±6.87%) | 263.67 ms (±3.83%) | 4.58× |
|
|
207
|
+
|
|
208
|
+
The comment-free case takes the plugin's early exit after scanning for comment delimiters. Its apparent speedup should
|
|
209
|
+
be treated as benchmark variation, not as an expected optimization over plain Prettier. In this run, sparse comments
|
|
210
|
+
took about 3.08× the plain formatting time, while comment-heavy inputs took 4.58–5.77×. These measurements isolate
|
|
211
|
+
formatter work on changed files; enabling the CLI cache above skips that work for unchanged files.
|
|
212
|
+
|
|
162
213
|
## Development
|
|
163
214
|
|
|
164
215
|
Source files are organized by responsibility. `src/plugin/` contains parser and printer integration,
|
|
@@ -168,6 +219,7 @@ concerns and source file names wherever practical.
|
|
|
168
219
|
|
|
169
220
|
```sh
|
|
170
221
|
npm install
|
|
222
|
+
npm run benchmark
|
|
171
223
|
npm run format:check
|
|
172
224
|
npm run typecheck
|
|
173
225
|
npm run test
|
|
@@ -177,3 +229,10 @@ npm run build
|
|
|
177
229
|
`npm run test` runs the TypeScript unit and fixture-based integration suites with Vitest. Use `npm run test:unit` or
|
|
178
230
|
`npm run test:integration` to run one suite. `npm run build` removes and recreates `dist` using a cross-platform Node
|
|
179
231
|
cleanup script, and `npm run verify` runs formatting, type checking, the build, and both test suites.
|
|
232
|
+
|
|
233
|
+
`npm run benchmark` compares plain Prettier with Prettier using the plugin. Its JavaScript, TypeScript, and TSX inputs
|
|
234
|
+
are generated in memory by the files under `test/benchmark`, so large benchmark fixtures are not stored in the
|
|
235
|
+
repository. Benchmarks use Vitest's separate benchmark mode and do not run as part of `npm run test` or
|
|
236
|
+
`npm run verify`.
|
|
237
|
+
|
|
238
|
+
[prettier-cache]: https://prettier.io/docs/cli#--cache
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isBlankLine } from '../utils/source-lines.js';
|
|
1
|
+
import { isBlankLine, normalizeLineTerminators } from '../utils/source-lines.js';
|
|
2
2
|
export function getCommentBody(text, comment) {
|
|
3
3
|
const raw = text.slice(comment.start, comment.end);
|
|
4
4
|
return comment.kind === 'line' ? normalizeLineCommentBody(raw.slice(2)) : normalizeBlockCommentBody(raw);
|
|
@@ -10,7 +10,7 @@ export function normalizeLineCommentBody(rawBody) {
|
|
|
10
10
|
return rawBody.replace(/^[ \t]?/, '').replace(/[ \t]+$/u, '');
|
|
11
11
|
}
|
|
12
12
|
export function normalizeBlockCommentBody(rawComment) {
|
|
13
|
-
const body = rawComment.slice(2, -2)
|
|
13
|
+
const body = normalizeLineTerminators(rawComment.slice(2, -2));
|
|
14
14
|
const lines = body.split('\n');
|
|
15
15
|
if (lines.length === 1) {
|
|
16
16
|
return lines[0]?.trim() ?? '';
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const PRAGMA_DIRECTIVE_COMMENT_PATTERNS = [
|
|
2
|
+
/^@(?:flow|noflow)\b/u,
|
|
2
3
|
/^@(?:license|preserve)\b/u,
|
|
3
4
|
/^@(?:jsxFrag|jsxImportSource|jsxRuntime|jsx)\b/u,
|
|
4
5
|
/^@(?:ts-check|ts-expect-error|ts-ignore|ts-nocheck)\b/u,
|
|
@@ -17,9 +18,11 @@ const TOOL_DIRECTIVE_COMMENT_PATTERNS = [
|
|
|
17
18
|
/^eslint\b/u,
|
|
18
19
|
/^eslint-/u,
|
|
19
20
|
/^exported\b/u,
|
|
21
|
+
/^flowlint(?:-next-line|-line)?\b/u,
|
|
20
22
|
/^globals?\b/u,
|
|
21
23
|
/^istanbul\b/u,
|
|
22
24
|
/^jshint\b/u,
|
|
25
|
+
/^node:coverage (?:disable|enable|ignore next(?: [1-9]\d*)?)$/u,
|
|
23
26
|
/^nyc\b/u,
|
|
24
27
|
/^oxlint\b/u,
|
|
25
28
|
/^prettier-ignore\b/u,
|
|
@@ -29,6 +32,7 @@ const TOOL_DIRECTIVE_COMMENT_PATTERNS = [
|
|
|
29
32
|
/^tslint\b/u,
|
|
30
33
|
/^v8\b/u,
|
|
31
34
|
/^vite-ignore\b/u,
|
|
35
|
+
/^\$(?:FlowExpectedError|FlowFixMe)\b/u,
|
|
32
36
|
];
|
|
33
37
|
const BUNDLER_DIRECTIVE_COMMENT_PATTERNS = [
|
|
34
38
|
/^webpack(?:ChunkName|Exclude|Ignore|Include|Mode|Prefetch|Preload)\b/u,
|
|
@@ -2,3 +2,4 @@ import type { CommentRange } from './comment-ranges.js';
|
|
|
2
2
|
export declare function shouldSkipLineComment(text: string, comment: CommentRange): boolean;
|
|
3
3
|
export declare function shouldSkipBlockComment(text: string, comment: CommentRange): boolean;
|
|
4
4
|
export declare function hasPreserveCommentMarker(rawComment: string): boolean;
|
|
5
|
+
export declare function hasFlowCommentTypeMarker(rawComment: string): boolean;
|
|
@@ -9,7 +9,7 @@ export function shouldSkipLineComment(text, comment) {
|
|
|
9
9
|
}
|
|
10
10
|
export function shouldSkipBlockComment(text, comment) {
|
|
11
11
|
const raw = text.slice(comment.start, comment.end);
|
|
12
|
-
if (raw.startsWith('/**') || hasPreserveCommentMarker(raw)) {
|
|
12
|
+
if (raw.startsWith('/**') || hasPreserveCommentMarker(raw) || hasFlowCommentTypeMarker(raw)) {
|
|
13
13
|
return true;
|
|
14
14
|
}
|
|
15
15
|
const body = normalizeBlockCommentBody(raw);
|
|
@@ -18,3 +18,6 @@ export function shouldSkipBlockComment(text, comment) {
|
|
|
18
18
|
export function hasPreserveCommentMarker(rawComment) {
|
|
19
19
|
return rawComment.startsWith('/*!') || rawComment.startsWith('//!');
|
|
20
20
|
}
|
|
21
|
+
export function hasFlowCommentTypeMarker(rawComment) {
|
|
22
|
+
return /^\/\*[ \t]*(?::|flow-include)/u.test(rawComment);
|
|
23
|
+
}
|
|
@@ -1,21 +1,39 @@
|
|
|
1
1
|
import { getLineEnd, getLinePrefix, getLineStart } from '../utils/source-lines.js';
|
|
2
|
+
import { isEcmaScriptHorizontalWhitespace } from '../utils/whitespace.js';
|
|
2
3
|
export function isStandaloneComment(text, comment) {
|
|
3
4
|
return comment.kind === 'line' ? isStandaloneLineComment(text, comment) : isStandaloneBlockComment(text, comment);
|
|
4
5
|
}
|
|
5
6
|
export function isStandaloneLineComment(text, comment) {
|
|
6
|
-
return
|
|
7
|
+
return isOnlyHorizontalWhitespace(getLinePrefix(text, comment.start));
|
|
7
8
|
}
|
|
8
9
|
export function isStandaloneBlockComment(text, comment) {
|
|
9
10
|
const before = text.slice(getLineStart(text, comment.start), comment.start);
|
|
10
11
|
const after = text.slice(comment.end, getLineEnd(text, comment.end));
|
|
11
|
-
return
|
|
12
|
+
return isOnlyHorizontalWhitespace(before) && isOnlyHorizontalWhitespace(after);
|
|
12
13
|
}
|
|
13
14
|
export function areCommentsOnAdjacentLines(text, previousComment, comment) {
|
|
14
|
-
return
|
|
15
|
+
return isOnlyHorizontalWhitespaceAroundNewline(text.slice(previousComment.end, comment.start));
|
|
15
16
|
}
|
|
16
17
|
export function isCommentAdjacentBeforeIndex(text, comment, index) {
|
|
17
|
-
return
|
|
18
|
+
return isOnlyHorizontalWhitespaceAroundNewline(text.slice(comment.end, index));
|
|
18
19
|
}
|
|
19
|
-
function
|
|
20
|
-
|
|
20
|
+
function isOnlyHorizontalWhitespaceAroundNewline(text) {
|
|
21
|
+
let newlineStart = 0;
|
|
22
|
+
while (newlineStart < text.length) {
|
|
23
|
+
const character = text[newlineStart];
|
|
24
|
+
if (character === undefined || !isEcmaScriptHorizontalWhitespace(character)) {
|
|
25
|
+
break;
|
|
26
|
+
}
|
|
27
|
+
newlineStart += 1;
|
|
28
|
+
}
|
|
29
|
+
const newline = /^(?:\r\n|[\n\r\u2028\u2029])/u.exec(text.slice(newlineStart))?.[0];
|
|
30
|
+
return newline !== undefined && isOnlyHorizontalWhitespace(text.slice(newlineStart + newline.length));
|
|
31
|
+
}
|
|
32
|
+
function isOnlyHorizontalWhitespace(text) {
|
|
33
|
+
for (const character of text) {
|
|
34
|
+
if (!isEcmaScriptHorizontalWhitespace(character)) {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return true;
|
|
21
39
|
}
|
|
@@ -8,6 +8,5 @@ export type EmbeddedTrailingLineCommentMove = {
|
|
|
8
8
|
removeStart: number;
|
|
9
9
|
};
|
|
10
10
|
export declare function collectEmbeddedExpressionRanges(ast: unknown): EmbeddedExpressionRange[];
|
|
11
|
-
export declare function
|
|
12
|
-
export declare function
|
|
13
|
-
export declare function doesBlockCommentSeparateEmbeddedTrailingLineComment(text: string, blockComment: CommentRange, nextComment: CommentRange | undefined, ranges: EmbeddedExpressionRange[]): boolean;
|
|
11
|
+
export declare function getEmbeddedTrailingLineCommentMove(text: string, comment: CommentRange, range: EmbeddedExpressionRange | undefined): EmbeddedTrailingLineCommentMove | undefined;
|
|
12
|
+
export declare function doesBlockCommentSeparateEmbeddedTrailingLineComment(text: string, blockComment: CommentRange, nextComment: CommentRange | undefined, range: EmbeddedExpressionRange | undefined, nextRange: EmbeddedExpressionRange | undefined): boolean;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { getAstNodeRange, visitAstNodes } from '../utils/ast.js';
|
|
2
2
|
import { getLineEnd } from '../utils/source-lines.js';
|
|
3
3
|
import { isRecord, numberOrUndefined } from '../utils/type-guards.js';
|
|
4
|
+
import { skipWhitespace, trimWhitespaceEnd } from '../utils/whitespace.js';
|
|
4
5
|
const JSX_EMBEDDED_EXPRESSION_TYPES = new Set(['JSXExpressionContainer', 'JSXSpreadAttribute', 'JSXSpreadChild']);
|
|
5
6
|
export function collectEmbeddedExpressionRanges(ast) {
|
|
6
7
|
const ranges = [];
|
|
@@ -27,36 +28,59 @@ export function collectEmbeddedExpressionRanges(ast) {
|
|
|
27
28
|
});
|
|
28
29
|
return ranges.sort((left, right) => left.start - right.start || right.end - left.end);
|
|
29
30
|
}
|
|
30
|
-
export function
|
|
31
|
-
return getSmallestContainingRange(comment, ranges) !== undefined;
|
|
32
|
-
}
|
|
33
|
-
export function getEmbeddedTrailingLineCommentMove(text, comment, ranges) {
|
|
34
|
-
const range = getSmallestContainingRange(comment, ranges);
|
|
31
|
+
export function getEmbeddedTrailingLineCommentMove(text, comment, range) {
|
|
35
32
|
const expression = range?.expression;
|
|
36
|
-
if (expression === undefined
|
|
37
|
-
expression.end > comment.start ||
|
|
38
|
-
!/^[\t ]*$/u.test(text.slice(expression.end, comment.start))) {
|
|
33
|
+
if (range === undefined || expression === undefined) {
|
|
39
34
|
return undefined;
|
|
40
35
|
}
|
|
41
|
-
|
|
36
|
+
const rootExpression = getRootExpressionRangeBefore(text, expression, comment.start, range.start);
|
|
37
|
+
return rootExpression === undefined ? undefined : { insertAt: rootExpression.start, removeStart: rootExpression.end };
|
|
42
38
|
}
|
|
43
|
-
export function doesBlockCommentSeparateEmbeddedTrailingLineComment(text, blockComment, nextComment,
|
|
39
|
+
export function doesBlockCommentSeparateEmbeddedTrailingLineComment(text, blockComment, nextComment, range, nextRange) {
|
|
44
40
|
if (blockComment.kind !== 'block' ||
|
|
45
41
|
nextComment?.kind !== 'line' ||
|
|
46
42
|
blockComment.end >= nextComment.start ||
|
|
47
43
|
nextComment.end > getLineEnd(text, blockComment.start)) {
|
|
48
44
|
return false;
|
|
49
45
|
}
|
|
50
|
-
const range = getSmallestContainingRange(blockComment, ranges);
|
|
51
|
-
const nextRange = getSmallestContainingRange(nextComment, ranges);
|
|
52
46
|
const expression = range?.expression;
|
|
47
|
+
const rootExpression = range === undefined || expression === undefined
|
|
48
|
+
? undefined
|
|
49
|
+
: getRootExpressionRangeBefore(text, expression, blockComment.start, range.start);
|
|
53
50
|
return (range !== undefined &&
|
|
54
51
|
range === nextRange &&
|
|
55
|
-
|
|
56
|
-
expression.end <= blockComment.start &&
|
|
57
|
-
/^[\t ]*$/u.test(text.slice(expression.end, blockComment.start)) &&
|
|
52
|
+
rootExpression !== undefined &&
|
|
58
53
|
/^[\t ]*$/u.test(text.slice(blockComment.end, nextComment.start)));
|
|
59
54
|
}
|
|
55
|
+
function getRootExpressionRangeBefore(text, expression, before, containerStart) {
|
|
56
|
+
if (expression.end > before) {
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
let cursor = expression.end;
|
|
60
|
+
let expandedEnd = expression.end;
|
|
61
|
+
let parenthesisCount = 0;
|
|
62
|
+
while (cursor < before) {
|
|
63
|
+
cursor = skipWhitespace(text, cursor);
|
|
64
|
+
if (cursor >= before) {
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
67
|
+
if (text[cursor] !== ')') {
|
|
68
|
+
return undefined;
|
|
69
|
+
}
|
|
70
|
+
cursor += 1;
|
|
71
|
+
expandedEnd = cursor;
|
|
72
|
+
parenthesisCount += 1;
|
|
73
|
+
}
|
|
74
|
+
let expandedStart = expression.start;
|
|
75
|
+
for (let index = 0; index < parenthesisCount; index += 1) {
|
|
76
|
+
expandedStart = trimWhitespaceEnd(text, containerStart, expandedStart);
|
|
77
|
+
if (expandedStart <= containerStart || text[expandedStart - 1] !== '(') {
|
|
78
|
+
return undefined;
|
|
79
|
+
}
|
|
80
|
+
expandedStart -= 1;
|
|
81
|
+
}
|
|
82
|
+
return { end: expandedEnd, start: expandedStart };
|
|
83
|
+
}
|
|
60
84
|
function collectTemplateInterpolationRanges(node, expressionsKey, ranges) {
|
|
61
85
|
const quasis = node['quasis'];
|
|
62
86
|
const expressions = node[expressionsKey];
|
|
@@ -91,15 +115,3 @@ function getNodeBoundary(value) {
|
|
|
91
115
|
}
|
|
92
116
|
return { end, start };
|
|
93
117
|
}
|
|
94
|
-
function getSmallestContainingRange(comment, ranges) {
|
|
95
|
-
let containingRange;
|
|
96
|
-
for (const range of ranges) {
|
|
97
|
-
if (comment.start <= range.start || comment.end >= range.end) {
|
|
98
|
-
continue;
|
|
99
|
-
}
|
|
100
|
-
if (containingRange === undefined || range.end - range.start < containingRange.end - containingRange.start) {
|
|
101
|
-
containingRange = range;
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
return containingRange;
|
|
105
|
-
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { CommentRange } from './comment-ranges.js';
|
|
2
2
|
import type { PrinterCommentLayout } from './printer-layout.js';
|
|
3
3
|
import type { BlockCommentLayout } from './wrap-block-comment.js';
|
|
4
|
-
import type { SourceRange } from '../utils/ast.js';
|
|
4
|
+
import type { ContainingRangeMatch, SourceRange } from '../utils/ast.js';
|
|
5
5
|
export type JsxExpressionContainerRange = SourceRange & {
|
|
6
6
|
expression: SourceRange | undefined;
|
|
7
7
|
};
|
|
8
8
|
export declare function collectJsxExpressionContainerRanges(ast: unknown): JsxExpressionContainerRange[];
|
|
9
9
|
export declare function getPrintedJsxCommentMarkerColumn(text: string, container: SourceRange, tabWidth: number): number;
|
|
10
|
-
export declare function getJsxExpressionBlockCommentLayout(text: string, comment: CommentRange, previousComment: CommentRange | undefined,
|
|
10
|
+
export declare function getJsxExpressionBlockCommentLayout(text: string, comment: CommentRange, previousComment: CommentRange | undefined, containerMatch: ContainingRangeMatch<JsxExpressionContainerRange> | undefined, tabWidth: number, outputCommentLayout: PrinterCommentLayout | undefined, outputCommentMarkerColumns: Array<number | undefined>): BlockCommentLayout | undefined;
|
|
@@ -2,7 +2,7 @@ import { isStandaloneBlockComment } from './comment-location.js';
|
|
|
2
2
|
import { getAstNodeRange, visitAstNodes } from '../utils/ast.js';
|
|
3
3
|
import { getColumns } from '../utils/display-width.js';
|
|
4
4
|
import { getLeadingIndent } from '../utils/indentation.js';
|
|
5
|
-
import { getLinePrefix } from '../utils/source-lines.js';
|
|
5
|
+
import { getLinePrefix, getLineStart } from '../utils/source-lines.js';
|
|
6
6
|
import { isRecord } from '../utils/type-guards.js';
|
|
7
7
|
import { skipWhitespace, trimWhitespaceEnd } from '../utils/whitespace.js';
|
|
8
8
|
export function collectJsxExpressionContainerRanges(ast) {
|
|
@@ -28,15 +28,17 @@ export function getPrintedJsxCommentMarkerColumn(text, container, tabWidth) {
|
|
|
28
28
|
const lineIndent = getLeadingIndent(linePrefix);
|
|
29
29
|
return getColumns(lineIndent, tabWidth) + tabWidth;
|
|
30
30
|
}
|
|
31
|
-
export function getJsxExpressionBlockCommentLayout(text, comment, previousComment,
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
export function getJsxExpressionBlockCommentLayout(text, comment, previousComment, containerMatch, tabWidth, outputCommentLayout, outputCommentMarkerColumns) {
|
|
32
|
+
if (containerMatch === undefined) {
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
const container = containerMatch.range;
|
|
36
|
+
if (text[container.start] !== '{' || text[container.end - 1] !== '}') {
|
|
34
37
|
return undefined;
|
|
35
38
|
}
|
|
36
39
|
const hasExpressionBeforeComment = container.expression !== undefined && container.expression.start < comment.start;
|
|
37
40
|
const hasExpressionAfterComment = container.expression !== undefined && container.expression.end > comment.end;
|
|
38
|
-
const
|
|
39
|
-
const multilineMarkerColumn = outputCommentMarkerColumns[containerIndex] ??
|
|
41
|
+
const multilineMarkerColumn = outputCommentMarkerColumns[containerMatch.index] ??
|
|
40
42
|
getJsxExpressionContainerOutputColumn(text, container, tabWidth) + tabWidth;
|
|
41
43
|
const markerColumn = outputCommentLayout?.markerColumn ?? multilineMarkerColumn;
|
|
42
44
|
const contentColumn = multilineMarkerColumn + 3;
|
|
@@ -78,7 +80,14 @@ export function getJsxExpressionBlockCommentLayout(text, comment, previousCommen
|
|
|
78
80
|
}
|
|
79
81
|
if (!hasExpressionBeforeComment) {
|
|
80
82
|
if (isStandaloneBlockComment(text, comment)) {
|
|
81
|
-
return {
|
|
83
|
+
return {
|
|
84
|
+
contentColumn,
|
|
85
|
+
markerColumn,
|
|
86
|
+
multilineIndent: '',
|
|
87
|
+
placement: 'standalone',
|
|
88
|
+
preserveMultiline: getLineStart(text, comment.start) !== getLineStart(text, comment.end),
|
|
89
|
+
singleLineSuffixWidth: outputCommentLayout?.suffixWidth ?? 0,
|
|
90
|
+
};
|
|
82
91
|
}
|
|
83
92
|
const expressionStart = skipWhitespace(text, comment.end);
|
|
84
93
|
return {
|
|
@@ -103,15 +112,3 @@ function getJsxExpressionContainerOutputColumn(text, container, tabWidth) {
|
|
|
103
112
|
const lineIndent = getLeadingIndent(linePrefix);
|
|
104
113
|
return getColumns(lineIndent, tabWidth) + tabWidth;
|
|
105
114
|
}
|
|
106
|
-
function getSmallestContainingRange(comment, ranges) {
|
|
107
|
-
let containingRange;
|
|
108
|
-
for (const range of ranges) {
|
|
109
|
-
if (comment.start <= range.start || comment.end >= range.end) {
|
|
110
|
-
continue;
|
|
111
|
-
}
|
|
112
|
-
if (containingRange === undefined || range.end - range.start < containingRange.end - containingRange.start) {
|
|
113
|
-
containingRange = range;
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
return containingRange;
|
|
117
|
-
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { CommentEntry, CommentRange } from './comment-ranges.js';
|
|
2
2
|
import type { SourceRange } from '../utils/ast.js';
|
|
3
3
|
export declare function neutralizePrettierIgnoreForIgnoredComments<T>(text: string, ast: T): T;
|
|
4
|
+
export declare function getNeutralizedPrettierIgnoreOriginalText(comment: unknown): string | undefined;
|
|
4
5
|
export declare function collectPrettierIgnoredLineRanges(text: string, ast: unknown, comments: CommentEntry[]): SourceRange[];
|
|
5
|
-
export declare function isCommentInIgnoredLineRange(comment: CommentRange,
|
|
6
|
+
export declare function isCommentInIgnoredLineRange(comment: CommentRange, ignoredLineRange: SourceRange | undefined): boolean;
|
|
6
7
|
export declare function isPrettierIgnoredBlockComment(text: string, comments: CommentEntry[], index: number): boolean;
|
|
7
8
|
export declare function isPrettierIgnoredStandaloneLineComment(text: string, comments: CommentEntry[], index: number): boolean;
|
|
8
9
|
export declare function isPrettierIgnoredTrailingLineComment(text: string, comments: CommentEntry[], index: number): boolean;
|
|
@@ -3,10 +3,12 @@ import { isPrettierIgnoreComment } from './comment-directives.js';
|
|
|
3
3
|
import { shouldSkipBlockComment, shouldSkipLineComment } from './comment-eligibility.js';
|
|
4
4
|
import { areCommentsOnAdjacentLines, isCommentAdjacentBeforeIndex, isStandaloneBlockComment, isStandaloneComment, isStandaloneLineComment, } from './comment-location.js';
|
|
5
5
|
import { collectCommentEntries } from './comment-ranges.js';
|
|
6
|
-
import { getAstNodeRange, visitAstNodes } from '../utils/ast.js';
|
|
6
|
+
import { collectAstNodeRangesByStart, getAstNodeRange, visitAstNodes } from '../utils/ast.js';
|
|
7
7
|
import { getLineEnd, getLineStart } from '../utils/source-lines.js';
|
|
8
|
+
import { isRecord } from '../utils/type-guards.js';
|
|
8
9
|
import { skipWhitespace } from '../utils/whitespace.js';
|
|
9
10
|
const NEUTRALIZED_PRETTIER_IGNORE_COMMENT = 'prettier-ignore wrap-comments';
|
|
11
|
+
const neutralizedPrettierIgnoreOriginalTextKey = Symbol('neutralizedPrettierIgnoreOriginalText');
|
|
10
12
|
export function neutralizePrettierIgnoreForIgnoredComments(text, ast) {
|
|
11
13
|
const comments = collectCommentEntries(ast, text);
|
|
12
14
|
for (let index = 0; index < comments.length; index += 1) {
|
|
@@ -17,38 +19,55 @@ export function neutralizePrettierIgnoreForIgnoredComments(text, ast) {
|
|
|
17
19
|
((isPrettierIgnoredBlockComment(text, comments, index) && !shouldSkipBlockComment(text, entry.range)) ||
|
|
18
20
|
(isPrettierIgnoredStandaloneLineComment(text, comments, index) && !shouldSkipLineComment(text, entry.range)));
|
|
19
21
|
if (shouldNeutralize) {
|
|
22
|
+
if (previousEntry.range.kind === 'block') {
|
|
23
|
+
const originalText = text.slice(previousEntry.range.start, previousEntry.range.end);
|
|
24
|
+
previousEntry.raw[neutralizedPrettierIgnoreOriginalTextKey] =
|
|
25
|
+
originalText;
|
|
26
|
+
}
|
|
20
27
|
previousEntry.raw.value = NEUTRALIZED_PRETTIER_IGNORE_COMMENT;
|
|
21
28
|
}
|
|
22
29
|
}
|
|
23
30
|
return ast;
|
|
24
31
|
}
|
|
32
|
+
export function getNeutralizedPrettierIgnoreOriginalText(comment) {
|
|
33
|
+
if (!isRecord(comment)) {
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
const originalText = comment[neutralizedPrettierIgnoreOriginalTextKey];
|
|
37
|
+
return typeof originalText === 'string' ? originalText : undefined;
|
|
38
|
+
}
|
|
25
39
|
export function collectPrettierIgnoredLineRanges(text, ast, comments) {
|
|
26
|
-
const
|
|
40
|
+
const nodeRangesByStart = collectAstNodeRangesByStart(ast);
|
|
41
|
+
const jsxTargetRangesByIgnoreStart = collectJsxPrettierIgnoreTargetRanges(text, ast, comments);
|
|
27
42
|
const ignoredLineRanges = [];
|
|
28
43
|
for (let index = 0; index < comments.length; index += 1) {
|
|
29
44
|
const comment = comments[index]?.range;
|
|
45
|
+
const jsxTargetRange = comment === undefined ? undefined : jsxTargetRangesByIgnoreStart.get(comment.start);
|
|
30
46
|
if (comment === undefined ||
|
|
31
|
-
!isStandaloneComment(text, comment) ||
|
|
47
|
+
(!isStandaloneComment(text, comment) && jsxTargetRange === undefined) ||
|
|
32
48
|
!isPrettierIgnoreComment(getCommentBody(text, comment))) {
|
|
33
49
|
continue;
|
|
34
50
|
}
|
|
35
|
-
|
|
36
|
-
if (
|
|
37
|
-
|
|
51
|
+
let targetRange = jsxTargetRange;
|
|
52
|
+
if (targetRange === undefined) {
|
|
53
|
+
const targetStart = getPrettierIgnoreTargetStart(text, comments, index);
|
|
54
|
+
if (targetStart === undefined) {
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
targetRange = nodeRangesByStart.get(targetStart);
|
|
38
58
|
}
|
|
39
|
-
const targetRange = nodeRanges.find((range) => range.start === targetStart);
|
|
40
59
|
if (targetRange === undefined) {
|
|
41
60
|
continue;
|
|
42
61
|
}
|
|
43
|
-
ignoredLineRanges
|
|
62
|
+
appendMergedRange(ignoredLineRanges, {
|
|
44
63
|
end: getLineEnd(text, targetRange.end),
|
|
45
64
|
start: getLineStart(text, targetRange.start),
|
|
46
65
|
});
|
|
47
66
|
}
|
|
48
67
|
return ignoredLineRanges;
|
|
49
68
|
}
|
|
50
|
-
export function isCommentInIgnoredLineRange(comment,
|
|
51
|
-
return
|
|
69
|
+
export function isCommentInIgnoredLineRange(comment, ignoredLineRange) {
|
|
70
|
+
return (ignoredLineRange !== undefined && comment.start >= ignoredLineRange.start && comment.start < ignoredLineRange.end);
|
|
52
71
|
}
|
|
53
72
|
export function isPrettierIgnoredBlockComment(text, comments, index) {
|
|
54
73
|
const comment = comments[index]?.range;
|
|
@@ -107,15 +126,82 @@ export function isPrettierIgnoredTrailingLineComment(text, comments, index) {
|
|
|
107
126
|
}
|
|
108
127
|
return false;
|
|
109
128
|
}
|
|
110
|
-
function
|
|
111
|
-
const
|
|
129
|
+
function collectJsxPrettierIgnoreTargetRanges(text, ast, comments) {
|
|
130
|
+
const commentsByStart = new Map(comments.map((entry) => [entry.range.start, entry.range]));
|
|
131
|
+
const targetRangesByIgnoreStart = new Map();
|
|
112
132
|
visitAstNodes(ast, (node) => {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
133
|
+
if (!isJsxElementOrFragment(node)) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
const children = node['children'];
|
|
137
|
+
if (!Array.isArray(children)) {
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
for (let ignoreIndex = 0; ignoreIndex < children.length; ignoreIndex += 1) {
|
|
141
|
+
const ignoreContainer = children[ignoreIndex];
|
|
142
|
+
if (!isRecord(ignoreContainer)) {
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
const ignoreComment = getCommentOnlyJsxExpressionContainerComment(text, ignoreContainer, commentsByStart);
|
|
146
|
+
if (ignoreComment === undefined || !isPrettierIgnoreComment(getCommentBody(text, ignoreComment))) {
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
const target = getNextSignificantJsxChild(text, children, ignoreIndex);
|
|
150
|
+
if (target === undefined || !isJsxElementOrFragment(target)) {
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
const targetRange = getAstNodeRange(target);
|
|
154
|
+
if (targetRange !== undefined) {
|
|
155
|
+
targetRangesByIgnoreStart.set(ignoreComment.start, targetRange);
|
|
156
|
+
}
|
|
116
157
|
}
|
|
117
158
|
});
|
|
118
|
-
return
|
|
159
|
+
return targetRangesByIgnoreStart;
|
|
160
|
+
}
|
|
161
|
+
function getNextSignificantJsxChild(text, children, ignoreIndex) {
|
|
162
|
+
for (let index = ignoreIndex + 1; index < children.length; index += 1) {
|
|
163
|
+
const child = children[index];
|
|
164
|
+
if (!isRecord(child)) {
|
|
165
|
+
return undefined;
|
|
166
|
+
}
|
|
167
|
+
if (isMultilineWhitespaceJsxText(text, child)) {
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
return child;
|
|
171
|
+
}
|
|
172
|
+
return undefined;
|
|
173
|
+
}
|
|
174
|
+
function isMultilineWhitespaceJsxText(text, node) {
|
|
175
|
+
if (node['type'] !== 'JSXText') {
|
|
176
|
+
return false;
|
|
177
|
+
}
|
|
178
|
+
const range = getAstNodeRange(node);
|
|
179
|
+
if (range === undefined) {
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
|
+
const raw = text.slice(range.start, range.end);
|
|
183
|
+
return raw.includes('\n') && /^[ \t\r\n]*$/u.test(raw);
|
|
184
|
+
}
|
|
185
|
+
function getCommentOnlyJsxExpressionContainerComment(text, container, commentsByStart) {
|
|
186
|
+
const expression = container['expression'];
|
|
187
|
+
if (container['type'] !== 'JSXExpressionContainer' ||
|
|
188
|
+
!isRecord(expression) ||
|
|
189
|
+
expression['type'] !== 'JSXEmptyExpression') {
|
|
190
|
+
return undefined;
|
|
191
|
+
}
|
|
192
|
+
const range = getAstNodeRange(container);
|
|
193
|
+
if (range === undefined || text[range.start] !== '{' || text[range.end - 1] !== '}') {
|
|
194
|
+
return undefined;
|
|
195
|
+
}
|
|
196
|
+
const commentStart = skipWhitespace(text, range.start + 1);
|
|
197
|
+
const comment = commentsByStart.get(commentStart);
|
|
198
|
+
if (comment === undefined || comment.kind !== 'block' || skipWhitespace(text, comment.end) !== range.end - 1) {
|
|
199
|
+
return undefined;
|
|
200
|
+
}
|
|
201
|
+
return comment;
|
|
202
|
+
}
|
|
203
|
+
function isJsxElementOrFragment(node) {
|
|
204
|
+
return node['type'] === 'JSXElement' || node['type'] === 'JSXFragment';
|
|
119
205
|
}
|
|
120
206
|
function getPrettierIgnoreTargetStart(text, comments, ignoreCommentIndex) {
|
|
121
207
|
const ignoreComment = comments[ignoreCommentIndex]?.range;
|
|
@@ -143,3 +229,11 @@ function isSkippableCommentBetweenIgnoreAndTarget(text, comment) {
|
|
|
143
229
|
}
|
|
144
230
|
return shouldSkipLineComment(text, comment) && !isPrettierIgnoreComment(getCommentBody(text, comment));
|
|
145
231
|
}
|
|
232
|
+
function appendMergedRange(ranges, range) {
|
|
233
|
+
const previousRange = ranges[ranges.length - 1];
|
|
234
|
+
if (previousRange === undefined || range.start > previousRange.end) {
|
|
235
|
+
ranges.push(range);
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
previousRange.end = Math.max(previousRange.end, range.end);
|
|
239
|
+
}
|
|
@@ -57,6 +57,7 @@ function getPrinterCommentLayout(text, comment, tabWidth) {
|
|
|
57
57
|
const suffix = text.slice(comment.end, lineEnd).replace(/[ \t]+$/u, '');
|
|
58
58
|
return {
|
|
59
59
|
lineIndentColumn: getColumns(lineIndent, tabWidth),
|
|
60
|
+
lineStart,
|
|
60
61
|
lineWidth: getColumns(lineText, tabWidth),
|
|
61
62
|
markerColumn: getColumns(linePrefix, tabWidth),
|
|
62
63
|
suffixWidth: getColumns(suffix, tabWidth),
|
|
@@ -37,7 +37,9 @@ function buildBlockReplacement(text, comment, formattedLines, options, layout) {
|
|
|
37
37
|
const singleLine = `/* ${formattedLines.join(' ')} */`;
|
|
38
38
|
const singleLineWidth = getColumns(singleLine, tabWidth);
|
|
39
39
|
const singleLineSuffixWidth = layout.singleLineSuffixWidth ?? 0;
|
|
40
|
-
if (
|
|
40
|
+
if (layout.preserveMultiline !== true &&
|
|
41
|
+
formattedLines.length === 1 &&
|
|
42
|
+
markerColumn + singleLineWidth + singleLineSuffixWidth <= getPrintWidth(options)) {
|
|
41
43
|
return singleLine;
|
|
42
44
|
}
|
|
43
45
|
if (layout.placement === 'inline') {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { shouldSkipLineComment } from './comment-eligibility.js';
|
|
2
2
|
import { isStandaloneBlockComment, isStandaloneLineComment } from './comment-location.js';
|
|
3
3
|
import { collectCommentEntries } from './comment-ranges.js';
|
|
4
|
-
import { collectEmbeddedExpressionRanges, doesBlockCommentSeparateEmbeddedTrailingLineComment, getEmbeddedTrailingLineCommentMove,
|
|
4
|
+
import { collectEmbeddedExpressionRanges, doesBlockCommentSeparateEmbeddedTrailingLineComment, getEmbeddedTrailingLineCommentMove, } from './embedded-expression-ranges.js';
|
|
5
5
|
import { collectJsxExpressionContainerRanges, getJsxExpressionBlockCommentLayout } from './jsx-expression-layout.js';
|
|
6
6
|
import { collectLineCommentGroup } from './line-comment-groups.js';
|
|
7
7
|
import { collectPrettierIgnoredLineRanges, isCommentInIgnoredLineRange, isPrettierIgnoredBlockComment, isPrettierIgnoredStandaloneLineComment, isPrettierIgnoredTrailingLineComment, } from './prettier-ignore.js';
|
|
@@ -9,7 +9,10 @@ import { getPrinterLayout } from './printer-layout.js';
|
|
|
9
9
|
import { wrapBlockComment } from './wrap-block-comment.js';
|
|
10
10
|
import { wrapLineCommentGroup } from './wrap-line-comment-group.js';
|
|
11
11
|
import { wrapTrailingLineComment } from './wrap-trailing-line-comment.js';
|
|
12
|
+
import { matchOrderedRangesToSmallestContainers } from '../utils/ast.js';
|
|
13
|
+
import { getColumnAt, getColumns } from '../utils/display-width.js';
|
|
12
14
|
import { applyReplacements } from '../utils/replacements.js';
|
|
15
|
+
import { getLineEnd, getLineStart } from '../utils/source-lines.js';
|
|
13
16
|
import { getTabWidth } from '../utils/wrap-options.js';
|
|
14
17
|
export async function wrapComments(text, ast, options, printerLayoutSource) {
|
|
15
18
|
return (await wrapCommentsWithMetadata(text, ast, options, printerLayoutSource)).text;
|
|
@@ -20,6 +23,8 @@ export async function wrapCommentsWithMetadata(text, ast, options, printerLayout
|
|
|
20
23
|
const embeddedExpressionRanges = collectEmbeddedExpressionRanges(ast);
|
|
21
24
|
const jsxExpressionContainers = collectJsxExpressionContainerRanges(ast);
|
|
22
25
|
const ignoredLineRanges = collectPrettierIgnoredLineRanges(text, ast, commentEntries);
|
|
26
|
+
const embeddedExpressionRangeMatches = matchOrderedRangesToSmallestContainers(comments, embeddedExpressionRanges);
|
|
27
|
+
const jsxExpressionContainerMatches = matchOrderedRangesToSmallestContainers(comments, jsxExpressionContainers);
|
|
23
28
|
if (comments.length === 0) {
|
|
24
29
|
return { jsxBlockCommentRewrites: [], text };
|
|
25
30
|
}
|
|
@@ -27,21 +32,34 @@ export async function wrapCommentsWithMetadata(text, ast, options, printerLayout
|
|
|
27
32
|
const jsxBlockCommentRewrites = [];
|
|
28
33
|
const tabWidth = getTabWidth(options);
|
|
29
34
|
const printerLayout = getPrinterLayout(text, commentEntries, jsxExpressionContainers, printerLayoutSource, tabWidth);
|
|
35
|
+
const sameLineWidthDeltas = {
|
|
36
|
+
output: new Map(),
|
|
37
|
+
source: new Map(),
|
|
38
|
+
};
|
|
30
39
|
let blockCommentIndex = -1;
|
|
40
|
+
let ignoredLineRangeIndex = 0;
|
|
31
41
|
for (let index = 0; index < comments.length; index += 1) {
|
|
32
42
|
const comment = comments[index];
|
|
33
43
|
const outputCommentLayout = printerLayout.comments[index];
|
|
34
44
|
if (comment?.kind === 'block') {
|
|
35
45
|
blockCommentIndex += 1;
|
|
36
46
|
}
|
|
37
|
-
if (comment === undefined
|
|
47
|
+
if (comment === undefined) {
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
let ignoredLineRange = ignoredLineRanges[ignoredLineRangeIndex];
|
|
51
|
+
while (ignoredLineRange !== undefined && ignoredLineRange.end <= comment.start) {
|
|
52
|
+
ignoredLineRangeIndex += 1;
|
|
53
|
+
ignoredLineRange = ignoredLineRanges[ignoredLineRangeIndex];
|
|
54
|
+
}
|
|
55
|
+
if (isCommentInIgnoredLineRange(comment, ignoredLineRange)) {
|
|
38
56
|
continue;
|
|
39
57
|
}
|
|
40
58
|
if (comment.kind === 'block') {
|
|
41
59
|
if (isPrettierIgnoredBlockComment(text, commentEntries, index)) {
|
|
42
60
|
continue;
|
|
43
61
|
}
|
|
44
|
-
const jsxLayout = getJsxExpressionBlockCommentLayout(text, comment, comments[index - 1],
|
|
62
|
+
const jsxLayout = getJsxExpressionBlockCommentLayout(text, comment, comments[index - 1], jsxExpressionContainerMatches[index], tabWidth, outputCommentLayout, printerLayout.jsxCommentMarkerColumns);
|
|
45
63
|
if (jsxLayout?.placement === 'inline') {
|
|
46
64
|
continue;
|
|
47
65
|
}
|
|
@@ -52,7 +70,7 @@ export async function wrapCommentsWithMetadata(text, ast, options, printerLayout
|
|
|
52
70
|
placement: isStandaloneBlockComment(text, comment) ? 'standalone' : 'inline',
|
|
53
71
|
};
|
|
54
72
|
const replacement = await wrapBlockComment(text, comment, options, jsxLayout ?? outputLayout);
|
|
55
|
-
const preservesEmbeddedCommentOrder = doesBlockCommentSeparateEmbeddedTrailingLineComment(text, comment, comments[index + 1],
|
|
73
|
+
const preservesEmbeddedCommentOrder = doesBlockCommentSeparateEmbeddedTrailingLineComment(text, comment, comments[index + 1], embeddedExpressionRangeMatches[index]?.range, embeddedExpressionRangeMatches[index + 1]?.range);
|
|
56
74
|
if (Array.isArray(replacement)) {
|
|
57
75
|
if (!preservesEmbeddedCommentOrder) {
|
|
58
76
|
replacements.push(...replacement);
|
|
@@ -60,6 +78,7 @@ export async function wrapCommentsWithMetadata(text, ast, options, printerLayout
|
|
|
60
78
|
}
|
|
61
79
|
else if (replacement !== undefined) {
|
|
62
80
|
replacements.push(replacement);
|
|
81
|
+
recordSameLineReplacementWidthDelta(text, comment, replacement, outputCommentLayout, tabWidth, sameLineWidthDeltas);
|
|
63
82
|
if (jsxLayout !== undefined) {
|
|
64
83
|
jsxBlockCommentRewrites.push({ blockCommentIndex, text: replacement.text });
|
|
65
84
|
}
|
|
@@ -77,11 +96,13 @@ export async function wrapCommentsWithMetadata(text, ast, options, printerLayout
|
|
|
77
96
|
if (isPrettierIgnoredTrailingLineComment(text, commentEntries, index)) {
|
|
78
97
|
continue;
|
|
79
98
|
}
|
|
80
|
-
const
|
|
81
|
-
|
|
99
|
+
const embeddedExpressionRange = embeddedExpressionRangeMatches[index]?.range;
|
|
100
|
+
const embeddedMove = getEmbeddedTrailingLineCommentMove(text, comment, embeddedExpressionRange);
|
|
101
|
+
if (embeddedMove === undefined && embeddedExpressionRange !== undefined) {
|
|
82
102
|
continue;
|
|
83
103
|
}
|
|
84
|
-
const
|
|
104
|
+
const trailingLayout = getTrailingLineCommentLayout(text, comment, outputCommentLayout, tabWidth, sameLineWidthDeltas);
|
|
105
|
+
const replacement = await wrapTrailingLineComment(text, comment, options, trailingLayout, embeddedMove);
|
|
85
106
|
if (replacement !== undefined) {
|
|
86
107
|
replacements.push(...replacement);
|
|
87
108
|
}
|
|
@@ -99,3 +120,50 @@ export async function wrapCommentsWithMetadata(text, ast, options, printerLayout
|
|
|
99
120
|
text: applyReplacements(text, replacements),
|
|
100
121
|
};
|
|
101
122
|
}
|
|
123
|
+
function recordSameLineReplacementWidthDelta(text, comment, replacement, outputLayout, tabWidth, sameLineWidthDeltas) {
|
|
124
|
+
const original = text.slice(comment.start, comment.end);
|
|
125
|
+
if (containsLineTerminator(original) || containsLineTerminator(replacement.text)) {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
const sourceLineStart = getLineStart(text, comment.start);
|
|
129
|
+
const sourceWidthDelta = sameLineWidthDeltas.source.get(sourceLineStart) ?? 0;
|
|
130
|
+
const sourceMarkerColumn = getColumnAt(text, comment.start, tabWidth) + sourceWidthDelta;
|
|
131
|
+
const sourceReplacementWidthDelta = getReplacementWidthDelta(original, replacement.text, sourceMarkerColumn, tabWidth);
|
|
132
|
+
addLineWidthDelta(sameLineWidthDeltas.source, sourceLineStart, sourceReplacementWidthDelta);
|
|
133
|
+
if (outputLayout === undefined) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
const outputWidthDelta = sameLineWidthDeltas.output.get(outputLayout.lineStart) ?? 0;
|
|
137
|
+
const outputMarkerColumn = outputLayout.markerColumn + outputWidthDelta;
|
|
138
|
+
const outputReplacementWidthDelta = getReplacementWidthDelta(original, replacement.text, outputMarkerColumn, tabWidth);
|
|
139
|
+
addLineWidthDelta(sameLineWidthDeltas.output, outputLayout.lineStart, outputReplacementWidthDelta);
|
|
140
|
+
}
|
|
141
|
+
function getTrailingLineCommentLayout(text, comment, outputLayout, tabWidth, sameLineWidthDeltas) {
|
|
142
|
+
if (outputLayout !== undefined) {
|
|
143
|
+
const lineWidthDelta = sameLineWidthDeltas.output.get(outputLayout.lineStart) ?? 0;
|
|
144
|
+
return lineWidthDelta === 0
|
|
145
|
+
? outputLayout
|
|
146
|
+
: { ...outputLayout, lineWidth: outputLayout.lineWidth + lineWidthDelta };
|
|
147
|
+
}
|
|
148
|
+
const lineStart = getLineStart(text, comment.start);
|
|
149
|
+
const lineWidthDelta = sameLineWidthDeltas.source.get(lineStart) ?? 0;
|
|
150
|
+
if (lineWidthDelta === 0) {
|
|
151
|
+
return undefined;
|
|
152
|
+
}
|
|
153
|
+
const lineEnd = getLineEnd(text, comment.start);
|
|
154
|
+
const lineText = text.slice(lineStart, lineEnd).replace(/[ \t]+$/u, '');
|
|
155
|
+
return { lineWidth: getColumns(lineText, tabWidth) + lineWidthDelta };
|
|
156
|
+
}
|
|
157
|
+
function getReplacementWidthDelta(original, replacement, markerColumn, tabWidth) {
|
|
158
|
+
const originalWidth = getColumns(original, tabWidth, markerColumn);
|
|
159
|
+
const replacementWidth = getColumns(replacement, tabWidth, markerColumn);
|
|
160
|
+
return replacementWidth - originalWidth;
|
|
161
|
+
}
|
|
162
|
+
function addLineWidthDelta(lineWidthDeltas, lineStart, delta) {
|
|
163
|
+
if (delta !== 0) {
|
|
164
|
+
lineWidthDeltas.set(lineStart, (lineWidthDeltas.get(lineStart) ?? 0) + delta);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
function containsLineTerminator(text) {
|
|
168
|
+
return /[\r\n\u2028\u2029]/u.test(text);
|
|
169
|
+
}
|
|
@@ -3,7 +3,7 @@ import type { EmbeddedTrailingLineCommentMove } from './embedded-expression-rang
|
|
|
3
3
|
import type { Replacement } from '../utils/replacements.js';
|
|
4
4
|
import type { WrapOptions } from '../utils/wrap-options.js';
|
|
5
5
|
export type TrailingLineCommentLayout = {
|
|
6
|
-
lineIndentColumn
|
|
6
|
+
lineIndentColumn?: number;
|
|
7
7
|
lineWidth: number;
|
|
8
8
|
};
|
|
9
9
|
export declare function wrapTrailingLineComment(text: string, comment: CommentRange, options: WrapOptions, outputLayout?: TrailingLineCommentLayout, move?: EmbeddedTrailingLineCommentMove): Promise<Replacement[] | undefined>;
|
|
@@ -32,9 +32,12 @@ function createWrappedParser(parserName, parser) {
|
|
|
32
32
|
if (hasOffsetSensitiveFormatting(text, options)) {
|
|
33
33
|
return preprocessed;
|
|
34
34
|
}
|
|
35
|
+
if (!preprocessed.includes('//') && !preprocessed.includes('/*')) {
|
|
36
|
+
return preprocessed;
|
|
37
|
+
}
|
|
35
38
|
let ast;
|
|
36
39
|
try {
|
|
37
|
-
ast = await parser.parse(preprocessed, options);
|
|
40
|
+
ast = await parser.parse(preprocessed, { ...options, parser: parserName });
|
|
38
41
|
}
|
|
39
42
|
catch {
|
|
40
43
|
return preprocessed;
|
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
import { doc } from 'prettier';
|
|
2
2
|
import * as estreePlugin from 'prettier/plugins/estree';
|
|
3
|
+
import { getNeutralizedPrettierIgnoreOriginalText } from '../comments/prettier-ignore.js';
|
|
4
|
+
import { normalizeLineTerminators } from '../utils/source-lines.js';
|
|
3
5
|
import { isRecord } from '../utils/type-guards.js';
|
|
4
6
|
import { isRewrittenJsxBlockComment } from './jsx-comment-rewrite-metadata.js';
|
|
5
7
|
const { hardline, indent } = doc.builders;
|
|
8
|
+
const { mapDoc, replaceEndOfLine } = doc.utils;
|
|
6
9
|
export function createPrinters() {
|
|
7
10
|
const estreePrinter = estreePlugin.printers.estree;
|
|
11
|
+
const estreePrintComment = estreePrinter.printComment;
|
|
12
|
+
if (estreePrintComment === undefined) {
|
|
13
|
+
throw new Error('Expected the native estree printer to provide printComment');
|
|
14
|
+
}
|
|
8
15
|
return {
|
|
9
|
-
...estreePlugin.printers,
|
|
10
16
|
estree: {
|
|
11
17
|
...estreePrinter,
|
|
12
18
|
print(path, options, print, args) {
|
|
@@ -15,6 +21,21 @@ export function createPrinters() {
|
|
|
15
21
|
}
|
|
16
22
|
return estreePrinter.print(path, options, print, args);
|
|
17
23
|
},
|
|
24
|
+
printComment(path, options) {
|
|
25
|
+
const originalText = getNeutralizedPrettierIgnoreOriginalText(path.node);
|
|
26
|
+
if (originalText !== undefined) {
|
|
27
|
+
const normalizedOriginalText = normalizeLineTerminators(originalText);
|
|
28
|
+
return normalizedOriginalText.includes('\n')
|
|
29
|
+
? replaceEndOfLine(normalizedOriginalText)
|
|
30
|
+
: normalizedOriginalText;
|
|
31
|
+
}
|
|
32
|
+
if (isRewrittenMultilineBlockComment(path.node)) {
|
|
33
|
+
const printedComment = estreePrintComment(path, options);
|
|
34
|
+
const normalizedComment = mapDoc(printedComment, (currentDoc) => typeof currentDoc === 'string' ? normalizeLineTerminators(currentDoc) : currentDoc);
|
|
35
|
+
return replaceEndOfLine(normalizedComment, hardline);
|
|
36
|
+
}
|
|
37
|
+
return estreePrintComment(path, options);
|
|
38
|
+
},
|
|
18
39
|
},
|
|
19
40
|
};
|
|
20
41
|
}
|
|
@@ -34,5 +55,5 @@ function isRewrittenMultilineBlockComment(comment) {
|
|
|
34
55
|
return false;
|
|
35
56
|
}
|
|
36
57
|
const value = comment['value'];
|
|
37
|
-
return typeof value === 'string' &&
|
|
58
|
+
return typeof value === 'string' && /[\r\n\u2028\u2029]/u.test(value) && isRewrittenJsxBlockComment(comment);
|
|
38
59
|
}
|
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
import { format } from 'prettier';
|
|
2
|
+
import { neutralizePrettierIgnoreForIgnoredComments } from '../comments/prettier-ignore.js';
|
|
2
3
|
import { createPrinters } from './create-printers.js';
|
|
3
4
|
export async function getPrinterLayoutSource(text, ast, parserName, parser, options) {
|
|
5
|
+
const printerLayoutParser = {
|
|
6
|
+
...parser,
|
|
7
|
+
async parse(source, parserOptions) {
|
|
8
|
+
const printerLayoutAst = await parser.parse(source, parserOptions);
|
|
9
|
+
return neutralizePrettierIgnoreForIgnoredComments(source, printerLayoutAst);
|
|
10
|
+
},
|
|
11
|
+
};
|
|
4
12
|
const printerLayoutPlugin = {
|
|
5
|
-
parsers: { [parserName]:
|
|
13
|
+
parsers: { [parserName]: printerLayoutParser },
|
|
6
14
|
printers: createPrinters(),
|
|
7
15
|
};
|
|
8
16
|
try {
|
|
@@ -15,7 +23,7 @@ export async function getPrinterLayoutSource(text, ast, parserName, parser, opti
|
|
|
15
23
|
if (formattedText === text) {
|
|
16
24
|
return { ast, text };
|
|
17
25
|
}
|
|
18
|
-
const formattedAst = await parser.parse(formattedText, options);
|
|
26
|
+
const formattedAst = await parser.parse(formattedText, { ...options, parser: parserName });
|
|
19
27
|
return {
|
|
20
28
|
ast: formattedAst,
|
|
21
29
|
text: formattedText,
|
package/dist/utils/ast.d.ts
CHANGED
|
@@ -2,5 +2,11 @@ export type SourceRange = {
|
|
|
2
2
|
end: number;
|
|
3
3
|
start: number;
|
|
4
4
|
};
|
|
5
|
+
export type ContainingRangeMatch<Range extends SourceRange> = {
|
|
6
|
+
index: number;
|
|
7
|
+
range: Range;
|
|
8
|
+
};
|
|
5
9
|
export declare function visitAstNodes(ast: unknown, visitor: (node: Record<string, unknown>) => void): void;
|
|
6
10
|
export declare function getAstNodeRange(node: Record<string, unknown>): SourceRange | undefined;
|
|
11
|
+
export declare function collectAstNodeRangesByStart(ast: unknown): Map<number, SourceRange>;
|
|
12
|
+
export declare function matchOrderedRangesToSmallestContainers<Range extends SourceRange>(targets: readonly SourceRange[], containers: readonly Range[]): Array<ContainingRangeMatch<Range> | undefined>;
|
package/dist/utils/ast.js
CHANGED
|
@@ -44,6 +44,49 @@ export function getAstNodeRange(node) {
|
|
|
44
44
|
}
|
|
45
45
|
return { end, start };
|
|
46
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
|
+
}
|
|
47
90
|
function getRangeNumber(range, index) {
|
|
48
91
|
if (!Array.isArray(range)) {
|
|
49
92
|
return undefined;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export declare function getColumnAt(text: string, index: number, tabWidth: number): number;
|
|
2
|
-
export declare function getColumns(text: string, tabWidth: number): number;
|
|
2
|
+
export declare function getColumns(text: string, tabWidth: number, startColumn?: number): number;
|
|
@@ -3,8 +3,8 @@ import { getLineStart } from './source-lines.js';
|
|
|
3
3
|
export function getColumnAt(text, index, tabWidth) {
|
|
4
4
|
return getColumns(text.slice(getLineStart(text, index), index), tabWidth);
|
|
5
5
|
}
|
|
6
|
-
export function getColumns(text, tabWidth) {
|
|
7
|
-
let column =
|
|
6
|
+
export function getColumns(text, tabWidth, startColumn = 0) {
|
|
7
|
+
let column = startColumn;
|
|
8
8
|
let segmentStart = 0;
|
|
9
9
|
for (let index = 0; index < text.length; index += 1) {
|
|
10
10
|
if (text[index] === '\t') {
|
|
@@ -15,5 +15,5 @@ export function getColumns(text, tabWidth) {
|
|
|
15
15
|
segmentStart = index + 1;
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
|
-
return column + util.getStringWidth(text.slice(segmentStart));
|
|
18
|
+
return column + util.getStringWidth(text.slice(segmentStart)) - startColumn;
|
|
19
19
|
}
|
|
@@ -1,22 +1,61 @@
|
|
|
1
1
|
import { format } from 'prettier';
|
|
2
|
-
import { isBlankLine } from './source-lines.js';
|
|
2
|
+
import { isBlankLine, normalizeLineTerminators } from './source-lines.js';
|
|
3
3
|
import { getTabWidth } from './wrap-options.js';
|
|
4
|
+
const MAX_MARKDOWN_FORMAT_PASSES = 3;
|
|
4
5
|
export async function formatMarkdownLines(markdown, printWidth, options) {
|
|
5
|
-
const normalized = trimBlankEdges(markdown
|
|
6
|
+
const normalized = trimBlankEdges(normalizeLineTerminators(markdown));
|
|
7
|
+
let formatted;
|
|
6
8
|
try {
|
|
7
|
-
|
|
8
|
-
endOfLine: 'lf',
|
|
9
|
-
parser: 'markdown',
|
|
10
|
-
printWidth,
|
|
11
|
-
proseWrap: 'always',
|
|
12
|
-
tabWidth: getTabWidth(options),
|
|
13
|
-
useTabs: options.useTabs,
|
|
14
|
-
});
|
|
15
|
-
return formatted.replace(/\n$/, '').split('\n');
|
|
9
|
+
formatted = await formatMarkdown(normalized, printWidth, options);
|
|
16
10
|
}
|
|
17
11
|
catch {
|
|
18
12
|
return normalized.split('\n');
|
|
19
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]));
|
|
20
59
|
}
|
|
21
60
|
function trimBlankEdges(markdown) {
|
|
22
61
|
const lines = markdown.split('\n');
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { WrapOptions } from './wrap-options.js';
|
|
2
|
+
export declare function normalizeLineTerminators(text: string): string;
|
|
2
3
|
export declare function getPreferredNewline(text: string, options: WrapOptions): string;
|
|
3
4
|
export declare function getLinePrefix(text: string, index: number): string;
|
|
4
5
|
export declare function getLineStart(text: string, index: number): number;
|
package/dist/utils/whitespace.js
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
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
|
+
}
|
|
1
5
|
export function skipWhitespace(text, index) {
|
|
2
6
|
let cursor = index;
|
|
3
7
|
while (cursor < text.length) {
|
package/package.json
CHANGED
|
@@ -9,9 +9,15 @@
|
|
|
9
9
|
"prettier": "3.9.6",
|
|
10
10
|
"prettier-plugin-wrap-comments-stable": "npm:@aforemendude/prettier-plugin-wrap-comments@1.0.6",
|
|
11
11
|
"typescript": "7.0.2",
|
|
12
|
-
"vite": "8.
|
|
12
|
+
"vite": "8.2.0",
|
|
13
13
|
"vitest": "4.1.10"
|
|
14
14
|
},
|
|
15
|
+
"devEngines": {
|
|
16
|
+
"runtime": {
|
|
17
|
+
"name": "node",
|
|
18
|
+
"version": ">=22.12.0"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
15
21
|
"engines": {
|
|
16
22
|
"node": ">=20.0.0"
|
|
17
23
|
},
|
|
@@ -46,18 +52,20 @@
|
|
|
46
52
|
"url": "git+https://github.com/aforemendude/prettier-plugin-wrap-comments.git"
|
|
47
53
|
},
|
|
48
54
|
"scripts": {
|
|
55
|
+
"benchmark": "vitest bench --run",
|
|
49
56
|
"build": "npm run clean && tsc -p tsconfig.build.json",
|
|
50
57
|
"clean": "node scripts/clean.mjs",
|
|
51
|
-
"format": "prettier --write .",
|
|
52
|
-
"format:check": "prettier --check .",
|
|
58
|
+
"format": "prettier --write --cache --cache-strategy metadata .",
|
|
59
|
+
"format:check": "prettier --check --cache --cache-strategy metadata .",
|
|
60
|
+
"format:nocache": "prettier --write .",
|
|
53
61
|
"prepack": "npm install && git diff --exit-code -- package-lock.json && npm run verify",
|
|
54
62
|
"test": "vitest run",
|
|
55
63
|
"test:integration": "vitest run test/integration",
|
|
56
64
|
"test:unit": "vitest run test/unit",
|
|
57
65
|
"typecheck": "tsc -p tsconfig.json",
|
|
58
|
-
"verify": "
|
|
66
|
+
"verify": "prettier --check . && npm run typecheck && npm run build && npm run test"
|
|
59
67
|
},
|
|
60
68
|
"type": "module",
|
|
61
69
|
"types": "./dist/index.d.ts",
|
|
62
|
-
"version": "1.1.
|
|
70
|
+
"version": "1.1.1"
|
|
63
71
|
}
|