@putout/printer 18.2.1 → 18.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/ChangeLog CHANGED
@@ -1,3 +1,17 @@
1
+ 2026.03.07, v18.2.3
2
+
3
+ fix:
4
+ - 355176d @putout/printer: instrument -> instrumentName
5
+
6
+ feature:
7
+ - 85a60d6 @putout/printer: type-checker: report
8
+
9
+ 2026.03.07, v18.2.2
10
+
11
+ feature:
12
+ - 7633d10 @putout/printer: ArrayExpression: isSimpleAfterObject
13
+ - c57048d @putout/printer: ArrayExpression: isCurrentNewLine: simplify
14
+
1
15
  2026.03.07, v18.2.1
2
16
 
3
17
  fix:
@@ -9,6 +9,7 @@ import {
9
9
  isNextObject,
10
10
  callWithNext,
11
11
  isInsideArray,
12
+ callWithPrev,
12
13
  } from '#is';
13
14
  import {
14
15
  isIncreaseIndent,
@@ -96,17 +97,18 @@ export const ArrayExpression = {
96
97
 
97
98
  maybe.indent.inc(indented && shouldIncreaseIndent);
98
99
 
99
- const isNewLine = isMultiLine(path, {
100
+ const needsNewline = isMultiLine(path, {
100
101
  maxElementsInOneLine,
101
102
  maxElementLengthInOneLine,
102
103
  });
103
104
 
104
105
  const n = elements.length - 1;
105
106
 
106
- maybe.print.newline(isNewLine && elements.length);
107
+ if (needsNewline)
108
+ print.newline();
107
109
 
108
110
  for (const [index, element] of elements.entries()) {
109
- const is = isNewLine && isCurrentNewLine(element);
111
+ const is = needsNewline && isCurrentNewLine(element);
110
112
 
111
113
  if (isSimpleAfterObject(element))
112
114
  print.newline();
@@ -139,10 +141,10 @@ export const ArrayExpression = {
139
141
  const isHideIdent = !isAroundStrings(path) || parentCountTwo;
140
142
 
141
143
  maybe.indent.dec(isHideIdent);
142
- maybe.indent(elements.length && isNewLine);
144
+ maybe.indent(elements.length && needsNewline);
143
145
  maybe.indent.inc(isHideIdent);
144
146
  } else if (!isArrayInsideArray(path) && !isObjectExpression(elements.at(-1))) {
145
- maybe.indent(elements.length && isNewLine);
147
+ maybe.indent(elements.length && needsNewline);
146
148
  }
147
149
 
148
150
  if (isSimpleAndNotEmptyObject(path) && !isSpreadElement(elements.at(-1)) && !isCallExpression(elements.at(-1))) {
@@ -173,15 +175,14 @@ export const ArrayExpression = {
173
175
  },
174
176
  };
175
177
 
176
- function isSimpleAfterObject(path) {
177
- if (!isSpreadElement(path) && !isIdentifier(path) && !isCallExpression(path))
178
- return;
179
-
180
- const prev = path.getPrevSibling();
181
- const next = path.getNextSibling();
182
-
183
- if (next.isObjectExpression())
184
- return false;
185
-
186
- return prev.isObjectExpression();
187
- }
178
+ const isSimple = createTypeChecker([
179
+ '-: -> SpreadElement',
180
+ '-: -> Identifier',
181
+ '+: -> !CallExpression',
182
+ ]);
183
+
184
+ const isSimpleAfterObject = createTypeChecker([
185
+ ['-', isSimple],
186
+ ['-', callWithNext(isObjectExpression)],
187
+ ['+', callWithPrev(isObjectExpression)],
188
+ ]);
@@ -338,9 +338,7 @@ const isStringAndObject = (elements) => {
338
338
  return isStringLiteral(first) && isObjectExpression(last);
339
339
  };
340
340
 
341
- export const isCurrentNewLine = (path) => {
342
- if (path.isSpreadElement())
343
- return true;
344
-
345
- return !path.isObjectExpression();
346
- };
341
+ export const isCurrentNewLine = createTypeChecker([
342
+ '+: -> SpreadElement',
343
+ '+: -> !ObjectExpression',
344
+ ]);
@@ -31,6 +31,7 @@ export const isInsideTSModuleBlock = ({parentPath}) => isTSModuleBlock(parentPat
31
31
  export const isInsideCall = ({parentPath}) => parentPath.isCallExpression();
32
32
  export const isInsideReturn = ({parentPath}) => parentPath.isReturnStatement();
33
33
  export const callWithNext = (fn) => (path) => fn(path.getNextSibling());
34
+ export const callWithPrev = (fn) => (path) => fn(path.getPrevSibling());
34
35
  export const callWithParent = (fn) => (path) => fn(path.parentPath);
35
36
 
36
37
  export const isNext = (path) => {
@@ -90,4 +90,3 @@ export const ExpressionStatement = {
90
90
  maybe.markAfter(store(), path);
91
91
  },
92
92
  };
93
-
@@ -10,13 +10,13 @@ export const instrument = (typeNames, fn, overrides = {}) => {
10
10
  const {
11
11
  env = _env,
12
12
  coverage = Coverage,
13
- instrument = true,
13
+ instrumentName = 'TYPE_CHECK',
14
14
  } = overrides;
15
15
 
16
16
  const {length} = typeNames;
17
17
  const error = Error();
18
18
  const location = parseCallLocation(error);
19
- const on = instrument && env.TYPE_CHECK;
19
+ const on = env[instrumentName];
20
20
  const covered = new Set();
21
21
 
22
22
  if (on && !location.includes('type-checker.spec.js'))
@@ -37,4 +37,3 @@ export const instrument = (typeNames, fn, overrides = {}) => {
37
37
  };
38
38
 
39
39
  export const getCoverage = () => Coverage;
40
-
@@ -0,0 +1,82 @@
1
+ import {styleText} from 'node:util';
2
+ import {codeFrameColumns} from '@putout/babel';
3
+
4
+ /* c8 ignore start */
5
+ const isString = (a) => typeof a === 'string';
6
+ const isFn = (a) => typeof a === 'function';
7
+ const difference = (a, b) => new Set(a).difference(new Set(b));
8
+ const red = (a) => styleText('red', String(a));
9
+
10
+ const SUCCESS = 0;
11
+ const FAIL = 1;
12
+
13
+ export const report = (coverage) => {
14
+ const lines = [];
15
+ const log = (a) => lines.push(a);
16
+ let exitCode = SUCCESS;
17
+
18
+ for (const [name, {length, covered, typeNames}] of coverage) {
19
+ if (length === covered.size)
20
+ continue;
21
+
22
+ for (const index of difference(fullSet(length), covered)) {
23
+ const currentType = typeNames[index];
24
+ const rawCode = createRawCode(currentType);
25
+ const code = codeFrameColumns(rawCode, {}, {
26
+ forceColor: true,
27
+ });
28
+
29
+ log(`🧨 Uncovered Checkers found at index: ${red(index + 1)}`);
30
+ log(`${code}\n`);
31
+ log(`${setLine(name, index)}\n`);
32
+ exitCode = FAIL;
33
+ }
34
+ }
35
+
36
+ if (exitCode === SUCCESS)
37
+ log('# 🌴 Checkers Covered');
38
+
39
+ return [exitCode, lines.join('\n')];
40
+ };
41
+
42
+ const setLine = (name, index) => {
43
+ const [at, uri, line, column] = name.split(':');
44
+ const newLine = Number(line) + index + 1;
45
+
46
+ return [
47
+ at,
48
+ uri,
49
+ newLine,
50
+ column,
51
+ ].join(':');
52
+ };
53
+
54
+ const fullSet = (n) => {
55
+ const result = new Set();
56
+ let i = 0;
57
+
58
+ do {
59
+ result.add(i++);
60
+ } while (i < n);
61
+
62
+ return result;
63
+ };
64
+
65
+ function createRawCode(currentType) {
66
+ if (isString(currentType))
67
+ return currentType;
68
+
69
+ if (isFn(currentType))
70
+ return currentType.name;
71
+
72
+ const [operator, fn] = currentType;
73
+
74
+ if (fn) {
75
+ const name = fn.name || fn;
76
+ return `['${operator}', ${name}]`;
77
+ }
78
+
79
+ return operator;
80
+ }
81
+
82
+ /* c8 ignore end */
@@ -12,6 +12,7 @@ export const createTypeChecker = (typeNames, overrides = {}) => {
12
12
  const {
13
13
  instrumentCoverage = _instrument,
14
14
  } = overrides;
15
+
15
16
  const tuples = parseTypeNames(typeNames);
16
17
  const typeChecker = (path, options) => {
17
18
  for (const [index, [operation, typeName]] of tuples.entries()) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@putout/printer",
3
- "version": "18.2.1",
3
+ "version": "18.2.3",
4
4
  "type": "module",
5
5
  "author": "coderaiser <mnemonic.enemy@gmail.com> (https://github.com/coderaiser)",
6
6
  "description": "Simplest possible opinionated Babel AST printer for 🐊Putout",
@@ -10,7 +10,9 @@
10
10
  ".": "./lib/printer.js",
11
11
  "./is": "./lib/tokenize/is.js",
12
12
  "./params": "./lib/tokenize/expressions/function/params.js",
13
- "./type-checker": "./lib/tokenize/type-checker/type-checker.js"
13
+ "./type-checker": "./lib/tokenize/type-checker/type-checker.js",
14
+ "./type-checker/instrument": "./lib/tokenize/type-checker/instrument.js",
15
+ "./type-checker/report": "./lib/tokenize/type-checker/report.js"
14
16
  },
15
17
  "repository": {
16
18
  "type": "git",
@@ -65,7 +67,8 @@
65
67
  "#import-attributes": "./lib/tokenize/statements/import-declaration/import-attribute.js",
66
68
  "#types": "./lib/types.js",
67
69
  "#type-checker": "./lib/tokenize/type-checker/type-checker.js",
68
- "#type-checker/instrument": "./lib/tokenize/type-checker/instrument.js"
70
+ "#type-checker/instrument": "./lib/tokenize/type-checker/instrument.js",
71
+ "#type-checker/report": "./lib/tokenize/type-checker/report.js"
69
72
  },
70
73
  "devDependencies": {
71
74
  "@babel/parser": "^7.28.5",
@@ -98,7 +101,7 @@
98
101
  },
99
102
  "license": "MIT",
100
103
  "engines": {
101
- "node": ">=22"
104
+ "node": ">=22.13"
102
105
  },
103
106
  "publishConfig": {
104
107
  "access": "public"