@aforemendude/prettier-plugin-wrap-comments 1.0.2 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,7 +1,12 @@
1
1
  # @aforemendude/prettier-plugin-wrap-comments
2
2
 
3
- A Prettier plugin that wraps non-JSDoc JavaScript and TypeScript comments as Markdown. It uses the comment marker's real
4
- column to calculate the available content width, so nested comments wrap more narrowly than top-level comments.
3
+ A Prettier plugin that wraps non-JSDoc JavaScript and TypeScript comments as Markdown. It uses each comment marker's
4
+ real column to calculate the available content width, so nested comments wrap more narrowly than top-level comments.
5
+
6
+ ## Requirements
7
+
8
+ - Node.js 18 or newer
9
+ - Prettier 3 or newer
5
10
 
6
11
  ## Install
7
12
 
@@ -27,8 +32,17 @@ npx prettier --write .
27
32
 
28
33
  ## Behavior
29
34
 
30
- The plugin wraps standalone `//` and `/* ... */` comments before Prettier parses the file, then delegates formatting to
31
- Prettier's built-in JavaScript and TypeScript parsers.
35
+ The plugin wraps comments for Prettier's `babel`, `babel-ts`, and `typescript` parsers. It runs during parser
36
+ preprocessing: the underlying Prettier parser preprocesses and parses the source first, the plugin rewrites eligible
37
+ comments from that parsed comment list, and Prettier then formats the rewritten source with its built-in JavaScript and
38
+ TypeScript printers. If the parser cannot parse the preprocessed source, the plugin leaves the source unchanged.
39
+
40
+ Comment text is normalized and reflowed with Prettier's Markdown parser. The available content width is based on
41
+ Prettier's `printWidth` minus the column where the comment text starts. `tabWidth`, `useTabs`, and `endOfLine` are used
42
+ when measuring and rebuilding comments.
43
+
44
+ Standalone `//` comments are wrapped in place. Adjacent standalone line comments are combined and reflowed as one
45
+ Markdown block when they are directly next to each other and their `//` markers start in the same column.
32
46
 
33
47
  ```ts
34
48
  function example() {
@@ -38,18 +52,39 @@ function example() {
38
52
  }
39
53
  ```
40
54
 
41
- JSDoc comments are left unchanged:
55
+ Trailing `//` comments stay in place when the full source line fits within `printWidth`. If the source line is too long,
56
+ the comment is moved above the code and wrapped using the code line's indentation.
57
+
58
+ ```ts
59
+ function example() {
60
+ // This trailing comment moved above the statement because the original line
61
+ // was too long.
62
+ const value = 1;
63
+ }
64
+ ```
65
+
66
+ Non-JSDoc `/* ... */` comments are also normalized as Markdown. A block comment may stay on one line if the normalized
67
+ comment fits within `printWidth`; otherwise, only standalone block comments are expanded into star-prefixed blocks. Long
68
+ inline block comments are left unchanged when they cannot fit on one line.
42
69
 
43
70
  ```ts
44
- /**
45
- * This documentation comment is not wrapped by the plugin.
46
- */
71
+ if (ready) {
72
+ /*
73
+ * This block comment is wrapped with the nested indentation included in the
74
+ * available width calculation.
75
+ */
76
+ run();
77
+ }
47
78
  ```
48
79
 
49
- Tooling directives such as `eslint-disable`, `@ts-expect-error`, `prettier-ignore`, source maps, and TypeScript
50
- triple-slash directives are also left untouched so their meaning is not changed. Trailing comments after code stay in
51
- place when the full line fits the configured print width. If the line is too long, the comment is moved above the code
52
- and wrapped there.
80
+ The plugin leaves these comments unchanged:
81
+
82
+ - JSDoc comments that start with `/**`
83
+ - bang-preserved comments that start with `/*!` or `//!`
84
+ - TypeScript-style triple-slash line comments that start with `///`
85
+ - empty comment bodies
86
+ - directive comments such as `@license`, `@preserve`, JSX and TypeScript pragmas, source map directives, `#__PURE__`,
87
+ `@__PURE__`, lint/coverage/formatter directives, `vite-ignore`, and webpack magic comments
53
88
 
54
89
  ## Supported Parsers
55
90
 
@@ -61,19 +96,9 @@ and wrapped there.
61
96
 
62
97
  ```sh
63
98
  npm install
64
- npm run check
65
- ```
66
-
67
- ## Publishing
68
-
69
- The npm package name is `@aforemendude/prettier-plugin-wrap-comments`.
70
-
71
- Before publishing, make sure the version in `package.json` has been updated, then run:
72
-
73
- ```sh
74
- npm run check
75
- npm publish --access public
99
+ npm run format:check
100
+ npm run test
76
101
  ```
77
102
 
78
- The package scripts already run the important publish checks: `prepublishOnly` runs `npm run check`, and `prepack`
79
- builds the `dist` files that are included in the published package.
103
+ `npm run test` builds `dist` before running the Node test suite. `npm run verify` runs `npm install`,
104
+ `npm run format:check`, and `npm run test`.
@@ -1,10 +1,10 @@
1
- import { isDirectiveComment, normalizeBlockCommentBody } from './core.js';
1
+ import { hasPreserveCommentMarker, isDirectiveComment, normalizeBlockCommentBody } from './core.js';
2
2
  import { formatMarkdownLines } from '../shared/markdown.js';
3
3
  import { getAvailableContentWidth, getPrintWidth, getTabWidth } from '../shared/options.js';
4
4
  import { getColumnAt, getColumns, getLinePrefix, getPreferredNewline, isStandaloneBlockComment, } from '../shared/text.js';
5
5
  export async function wrapBlockComment(text, comment, options) {
6
6
  const raw = text.slice(comment.start, comment.end);
7
- if (raw.startsWith('/**')) {
7
+ if (raw.startsWith('/**') || hasPreserveCommentMarker(raw)) {
8
8
  return undefined;
9
9
  }
10
10
  const markdown = normalizeBlockCommentBody(raw);
@@ -4,3 +4,4 @@ export declare function toCommentRange(comment: RawComment, text: string): Comme
4
4
  export declare function normalizeLineCommentBody(rawBody: string): string;
5
5
  export declare function normalizeBlockCommentBody(rawComment: string): string;
6
6
  export declare function isDirectiveComment(body: string): boolean;
7
+ export declare function hasPreserveCommentMarker(rawComment: string): boolean;
@@ -63,8 +63,53 @@ export function normalizeBlockCommentBody(rawComment) {
63
63
  })
64
64
  .join('\n');
65
65
  }
66
+ const PRAGMA_DIRECTIVE_COMMENT_PATTERNS = [
67
+ /^@(?:license|preserve)\b/u,
68
+ /^@(?:jsxFrag|jsxImportSource|jsxRuntime|jsx)\b/u,
69
+ /^@(?:ts-check|ts-expect-error|ts-ignore|ts-nocheck)\b/u,
70
+ /^[@#]__(?:NO_SIDE_EFFECTS|PURE)__\b/u,
71
+ ];
72
+ const SOURCE_MAP_DIRECTIVE_COMMENT_PATTERNS = [
73
+ /^[#@][ \t]*sourceMappingURL=/u,
74
+ /^[#@][ \t]*sourceURL=/u,
75
+ /^sourceMappingURL=/u,
76
+ /^sourceURL=/u,
77
+ ];
78
+ const TOOL_DIRECTIVE_COMMENT_PATTERNS = [
79
+ /^biome-ignore\b/u,
80
+ /^c8\b/u,
81
+ /^deno-lint-ignore\b/u,
82
+ /^eslint\b/u,
83
+ /^eslint-/u,
84
+ /^exported\b/u,
85
+ /^globals?\b/u,
86
+ /^istanbul\b/u,
87
+ /^jshint\b/u,
88
+ /^nyc\b/u,
89
+ /^oxlint\b/u,
90
+ /^prettier-ignore\b/u,
91
+ /^prettier-ignore-start\b/u,
92
+ /^prettier-ignore-end\b/u,
93
+ /^stylelint\b/u,
94
+ /^tslint\b/u,
95
+ /^v8\b/u,
96
+ /^vite-ignore\b/u,
97
+ ];
98
+ const BUNDLER_DIRECTIVE_COMMENT_PATTERNS = [
99
+ /^webpack(?:ChunkName|Exclude|Ignore|Include|Mode|Prefetch|Preload)\b/u,
100
+ ];
101
+ const DIRECTIVE_COMMENT_PATTERNS = [
102
+ ...PRAGMA_DIRECTIVE_COMMENT_PATTERNS,
103
+ ...SOURCE_MAP_DIRECTIVE_COMMENT_PATTERNS,
104
+ ...TOOL_DIRECTIVE_COMMENT_PATTERNS,
105
+ ...BUNDLER_DIRECTIVE_COMMENT_PATTERNS,
106
+ ];
66
107
  export function isDirectiveComment(body) {
67
- return /^(?:@(?:__NO_SIDE_EFFECTS__|__PURE__|jsx|jsxImportSource|license|preserve|ts-check|ts-expect-error|ts-ignore|ts-nocheck)\b|#\s*sourceMappingURL=|[@#]__PURE__\b|biome-ignore\b|c8\b|deno-lint-ignore\b|eslint\b|eslint-|exported\b|globals?\b|istanbul\b|jshint\b|nyc\b|oxlint\b|prettier-ignore\b|prettier-ignore-start\b|prettier-ignore-end\b|sourceMappingURL=|stylelint\b|tslint\b|v8\b|vite-ignore\b|webpack(?:ChunkName|Exclude|Ignore|Include|Mode|Prefetch|Preload)\b)/u.test(body.trimStart());
108
+ const normalizedBody = body.trimStart();
109
+ return DIRECTIVE_COMMENT_PATTERNS.some((pattern) => pattern.test(normalizedBody));
110
+ }
111
+ export function hasPreserveCommentMarker(rawComment) {
112
+ return rawComment.startsWith('/*!') || rawComment.startsWith('//!');
68
113
  }
69
114
  function isBlankLine(line) {
70
115
  return line !== undefined && line.trim() === '';
@@ -1,4 +1,4 @@
1
- import { isDirectiveComment, normalizeLineCommentBody } from './core.js';
1
+ import { hasPreserveCommentMarker, isDirectiveComment, normalizeLineCommentBody } from './core.js';
2
2
  import { formatMarkdownLines } from '../shared/markdown.js';
3
3
  import { getAvailableContentWidth, getPrintWidth, getTabWidth } from '../shared/options.js';
4
4
  import { getColumnAt, getColumns, getContinuationIndent, getLineEnd, getLinePrefix, getLineStart, getPreferredNewline, } from '../shared/text.js';
@@ -67,7 +67,7 @@ export async function wrapTrailingLineComment(text, comment, options) {
67
67
  }
68
68
  export function shouldSkipLineComment(text, comment) {
69
69
  const raw = text.slice(comment.start, comment.end);
70
- if (raw.startsWith('///')) {
70
+ if (raw.startsWith('///') || hasPreserveCommentMarker(raw)) {
71
71
  return true;
72
72
  }
73
73
  return isDirectiveComment(normalizeLineCommentBody(raw.slice(2)));
@@ -1,11 +1,23 @@
1
1
  import { getTabWidth } from './options.js';
2
2
  export function applyReplacements(text, replacements) {
3
3
  let result = text;
4
- for (const replacement of [...replacements].sort((left, right) => right.start - left.start)) {
4
+ for (const replacement of getNonOverlappingReplacements(replacements).sort((left, right) => right.start - left.start)) {
5
5
  result = result.slice(0, replacement.start) + replacement.text + result.slice(replacement.end);
6
6
  }
7
7
  return result;
8
8
  }
9
+ function getNonOverlappingReplacements(replacements) {
10
+ return [...replacements]
11
+ .sort((left, right) => left.start - right.start || right.end - left.end)
12
+ .reduce((accepted, replacement) => {
13
+ const previous = accepted.at(-1);
14
+ if (previous === undefined || previous.end <= replacement.start) {
15
+ accepted.push(replacement);
16
+ return accepted;
17
+ }
18
+ return accepted;
19
+ }, []);
20
+ }
9
21
  export function getPreferredNewline(text, options) {
10
22
  if (options.endOfLine === 'crlf') {
11
23
  return '\r\n';
package/package.json CHANGED
@@ -51,5 +51,5 @@
51
51
  },
52
52
  "type": "module",
53
53
  "types": "./dist/index.d.ts",
54
- "version": "1.0.2"
54
+ "version": "1.0.3"
55
55
  }