@putout/printer 18.0.5 → 18.0.7

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,13 @@
1
+ 2026.03.05, v18.0.7
2
+
3
+ feature:
4
+ - dd7f874 @putout/printer: VariableDeclaration: inside ForOfStatement: indent
5
+
6
+ 2026.03.05, v18.0.6
7
+
8
+ feature:
9
+ - 2b1dbb4 @putout/printer: check-types: instrument
10
+
1
11
  2026.03.04, v18.0.5
2
12
 
3
13
  feature:
@@ -90,13 +90,15 @@ export const ClassDeclaration = {
90
90
  classVisitor(path, printer, semantics);
91
91
  }),
92
92
  afterIf(path) {
93
+ const {parentPath} = path;
94
+
93
95
  if (isFunctionLike(path))
94
96
  return true;
95
97
 
96
98
  if (isNext(path))
97
99
  return true;
98
100
 
99
- return isInsideTSModuleBlock(path);
101
+ return isInsideTSModuleBlock(parentPath);
100
102
  },
101
103
  after(path, {write}) {
102
104
  write.newline();
@@ -1,11 +1,7 @@
1
1
  import {types} from '@putout/babel';
2
2
  import {printParams} from '#print-params';
3
3
  import {markAfter} from '#mark';
4
- import {
5
- isInsideTSModuleBlock,
6
- isNext,
7
- isNextParent,
8
- } from '#is';
4
+ import {isNext, isNextParent} from '#is';
9
5
  import {createTypeChecker} from '#type-checker';
10
6
 
11
7
  const {
@@ -22,11 +18,7 @@ const isInsideExportDefaultWithBody = createTypeChecker([
22
18
  ['+', hasFnBody],
23
19
  ]);
24
20
 
25
- const isInsideBlockLike = createTypeChecker([
26
- ['+', isInsideTSModuleBlock],
27
- '-: parentPath -> !BlockStatement',
28
- ['+: -> !', hasFnBody],
29
- ]);
21
+ const isInsideBlockLike = createTypeChecker(['+: parentPath.parentPath -> TSModuleBlock', '-: parentPath -> !BlockStatement', ['+: -> !', hasFnBody]]);
30
22
 
31
23
  const not = (fn) => (...a) => !fn(...a);
32
24
  const notInsideExportDefaultWithBody = not(isInsideExportDefaultWithBody);
@@ -1,5 +1,4 @@
1
1
  import {types} from '@putout/babel';
2
- import {createTypeChecker} from '#type-checker';
3
2
 
4
3
  const {
5
4
  isStringLiteral,
@@ -16,21 +15,14 @@ const {
16
15
  isProgram,
17
16
  isBlockStatement,
18
17
  isTSModuleBlock,
18
+ isSwitchCase,
19
19
  } = types;
20
20
 
21
21
  export const isInsideProgram = (path) => isProgram(path.parentPath);
22
22
  export const isInsideBlock = (path) => isBlockStatement(path.parentPath);
23
+ export const isInsideSwitchCase = (path) => isSwitchCase(path.parentPath);
23
24
 
24
- export const isInsideBlockLike = createTypeChecker('path.parentPath', [
25
- 'Program',
26
- 'BlockStatement',
27
- 'TSModuleBlock',
28
- 'SwitchCase',
29
- ]);
30
-
31
- export const isInsideTSModuleBlock = ({parentPath}) => {
32
- return isTSModuleBlock(parentPath?.parentPath);
33
- };
25
+ export const isInsideTSModuleBlock = ({parentPath}) => isTSModuleBlock(parentPath);
34
26
 
35
27
  export const isInsideCall = ({parentPath}) => parentPath.isCallExpression();
36
28
 
@@ -7,25 +7,47 @@ import {
7
7
  isNewlineBetweenSiblings,
8
8
  exists,
9
9
  noTrailingComment,
10
- isInsideBlockLike,
11
10
  isInsideIf,
12
11
  isInsideBlock,
13
12
  isInsideExport,
13
+ isInsideTSModuleBlock,
14
+ isInsideProgram,
15
+ isInsideSwitchCase,
14
16
  } from '#is';
15
17
  import {maybeSpaceAfterKeyword} from './maybe-space-after-keyword.js';
16
18
  import {isConcatenation} from '../../expressions/binary-expression/concatenate.js';
17
19
  import {parseLeadingComments} from '../../comment/comment.js';
18
20
  import {maybeDeclare} from '../../maybe/maybe-declare.js';
19
21
 
22
+ export const isInsideForOfBody = (path) => {
23
+ const {parentPath} = path;
24
+
25
+ if (!isForOfStatement(parentPath))
26
+ return false;
27
+
28
+ return path.parentPath.node.body === path.node;
29
+ };
30
+
31
+ const isInsideBlockLike = createTypeChecker([
32
+ isInsideProgram,
33
+ isInsideBlock,
34
+ isInsideTSModuleBlock,
35
+ isInsideSwitchCase,
36
+ isInsideForOfBody,
37
+ ]);
38
+
20
39
  const isLast = (path) => path.parentPath?.isProgram() && !isNext(path);
21
40
 
22
- const {isExportDeclaration} = types;
41
+ const {
42
+ isExportDeclaration,
43
+ isForOfStatement,
44
+ } = types;
23
45
 
24
46
  const isParentTSModuleBlock = (path) => path.parentPath.isTSModuleBlock();
25
47
  const isParentSwitchCase = (path) => path.parentPath.isSwitchCase();
26
48
  const isFirstInSwitch = (path) => path.parentPath.get('consequent.0') === path;
27
49
 
28
- const isIsideParentLike = createTypeChecker('path.parentPath', [
50
+ const isInsideParentLike = createTypeChecker('path.parentPath', [
29
51
  'Program',
30
52
  'BlockStatement',
31
53
  'ExportNamedDeclaration',
@@ -33,14 +55,15 @@ const isIsideParentLike = createTypeChecker('path.parentPath', [
33
55
  ]);
34
56
 
35
57
  const isNeedSemicolon = createTypeChecker([
36
- isIsideParentLike,
58
+ isInsideParentLike,
37
59
  isParentSwitchCase,
38
60
  isParentTSModuleBlock,
39
61
  isInsideIf,
62
+ isInsideForOfBody,
40
63
  ]);
41
64
 
42
65
  const isNeedNewline = createTypeChecker([
43
- ['-: -> !', isIsideParentLike],
66
+ ['-: -> !', isInsideParentLike],
44
67
  ['-: -> !', isNext],
45
68
  ['+', noTrailingComment],
46
69
  ['+', isNewlineBetweenSiblings],
@@ -0,0 +1,33 @@
1
+ import {env} from 'node:process';
2
+
3
+ const STACK_INDEX = 3;
4
+
5
+ const parseCallLocation = ({stack}) => stack.split('\n')[STACK_INDEX];
6
+
7
+ const Coverage = new Map();
8
+
9
+ export const instrument = (typeNames, fn) => {
10
+ const {length} = typeNames;
11
+ const error = Error();
12
+ const location = parseCallLocation(error);
13
+ const on = env.TYPE_CHECK;
14
+ const covered = new Set();
15
+
16
+ if (on && !location.includes('type-checker.spec.js'))
17
+ Coverage.set(location, {
18
+ length,
19
+ covered,
20
+ typeNames,
21
+ });
22
+
23
+ return (path, options) => {
24
+ const [index, result] = fn(path, options);
25
+
26
+ if (on && index !== Infinity)
27
+ covered.add(index);
28
+
29
+ return result;
30
+ };
31
+ };
32
+
33
+ export const getCoverage = () => Coverage;
@@ -1,6 +1,6 @@
1
- import {isArray} from '@putout/compare/lib/is.js';
2
1
  import {createTuple} from './create-tuple.js';
3
2
 
3
+ const {isArray} = Array;
4
4
  const isString = (a) => typeof a === 'string';
5
5
 
6
6
  export const parseOperation = (operation) => {
@@ -1,7 +1,13 @@
1
1
  import {jessy} from 'jessy';
2
+ import {instrument} from '#type-checker/instrument';
2
3
  import {parseOperation, parseTypeNames} from './parsers.js';
3
4
  import {equal, maybeCall} from './comparators.js';
4
5
 
6
+ const SKIP = [
7
+ Infinity,
8
+ false,
9
+ ];
10
+
5
11
  export const createTypeChecker = (deepness, typeNames) => {
6
12
  if (!typeNames) {
7
13
  typeNames = deepness;
@@ -10,31 +16,37 @@ export const createTypeChecker = (deepness, typeNames) => {
10
16
 
11
17
  const tuples = parseTypeNames(typeNames);
12
18
 
13
- return (path, options) => {
19
+ return instrument(typeNames, (path, options) => {
14
20
  let i = deepness.split('.').length;
15
21
 
16
22
  while (--i)
17
23
  path = path?.parentPath;
18
24
 
19
25
  if (!path)
20
- return false;
26
+ return [
27
+ Infinity,
28
+ false,
29
+ ];
21
30
 
22
- for (const [operation, typeName] of tuples) {
31
+ for (const [index, [operation, typeName]] of tuples.entries()) {
23
32
  const [result, selector, not] = parseOperation(operation);
24
33
  let currentPath = path;
25
34
 
26
35
  if (selector)
27
36
  currentPath = jessy(selector, path);
28
37
 
38
+ if (!currentPath)
39
+ return SKIP;
40
+
29
41
  const {type} = currentPath;
30
42
 
31
43
  if (equal(not, type, typeName))
32
- return result;
44
+ return [index, result];
33
45
 
34
46
  if (maybeCall(typeName, not, currentPath, options))
35
- return result;
47
+ return [index, result];
36
48
  }
37
49
 
38
- return false;
39
- };
50
+ return SKIP;
51
+ });
40
52
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@putout/printer",
3
- "version": "18.0.5",
3
+ "version": "18.0.7",
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",
@@ -19,16 +19,16 @@
19
19
  "scripts": {
20
20
  "wisdom": "madrun wisdom",
21
21
  "test": "madrun test",
22
+ "test:check": "madrun test:check",
22
23
  "test:dts": "madrun test:dts",
23
24
  "watch:test": "madrun watch:test",
24
25
  "lint": "madrun lint",
25
26
  "lint:redlint": "madrun lint:redlint",
26
27
  "lint:putout": "madrun lint:putout",
27
- "fresh:lint": "madrun fresh:lint",
28
- "lint:fresh": "madrun lint:fresh",
28
+ "fresh": "madrun fresh",
29
29
  "fix:lint": "madrun fix:lint",
30
- "fix:lint:putout": "madrun fix:lint:putout",
31
30
  "fix:lint:redlint": "madrun fix:lint:redlint",
31
+ "fix:lint:putout": "madrun fix:lint:putout",
32
32
  "coverage": "madrun coverage",
33
33
  "coverage:html": "madrun coverage:html",
34
34
  "report": "madrun report"
@@ -64,7 +64,8 @@
64
64
  "#print-params": "./lib/tokenize/expressions/function/params.js",
65
65
  "#import-attributes": "./lib/tokenize/statements/import-declaration/import-attribute.js",
66
66
  "#types": "./lib/types.js",
67
- "#type-checker": "./lib/tokenize/type-checker/type-checker.js"
67
+ "#type-checker": "./lib/tokenize/type-checker/type-checker.js",
68
+ "#type-checker/instrument": "./lib/tokenize/type-checker/instrument.js"
68
69
  },
69
70
  "devDependencies": {
70
71
  "@babel/parser": "^7.28.5",
@@ -76,6 +77,7 @@
76
77
  "@putout/plugin-react-hook-form": "^6.0.0",
77
78
  "@putout/plugin-react-hooks": "^9.0.0",
78
79
  "acorn": "^8.8.2",
80
+ "chalk": "^5.6.2",
79
81
  "check-dts": "^0.9.0",
80
82
  "escover": "^6.0.0",
81
83
  "eslint": "^10.0.0",