Skip to content

Commit 2173a52

Browse files
committed
feature: @putout/plugin-vitest: apply-hoisted: add
1 parent ff6c819 commit 2173a52

File tree

9 files changed

+186
-0
lines changed

9 files changed

+186
-0
lines changed

packages/plugin-vitest/README.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ npm i @putout/plugin-vitest -D
1717

1818
## Rules
1919

20+
-[apply-hoisted](#apply-hoisted);
2021
-[v3-apply-options-as-second-argument](#v3-apply-options-as-second-argument);
2122
-[v3-apply-browser-instances](#v3-apply-browser-instances);
2223

@@ -25,13 +26,51 @@ npm i @putout/plugin-vitest -D
2526
```json
2627
{
2728
"rules": {
29+
"vitest/apply-hoisted": "on",
2830
"vitest/v3-apply-options-as-second-argument": "on",
2931
"vitest/v3-apply-browser-instances": "on"
3032
},
3133
"plugins": ["vitest"]
3234
}
3335
```
3436

37+
## apply-hoisted
38+
39+
> All static import statements in ES modules are hoisted to the top of the file, so any code that is defined before the imports will actually be executed after imports are evaluated.
40+
>
41+
> (c) [vitest.dev](https://vitest.dev/api/vi.html#vi-hoisted)
42+
43+
Checkout in 🐊[**Putout Editor**](https://putout.cloudcmd.io/#/gist/1ce501a96baced5c46b07aac1cbdb1ff/70d2710641f57c20781208a28a81cf3a04c79a37).
44+
45+
### ❌ Example of incorrect code
46+
47+
```js
48+
let hello;
49+
let world;
50+
51+
it('hello', () => {
52+
hello.calledWith();
53+
});
54+
```
55+
56+
### ✅ Example of correct code
57+
58+
```js
59+
const hoisted = vi.hoisted({
60+
hello: vi.fn(),
61+
world: vi.fn(),
62+
});
63+
64+
beforeEach(() => {
65+
hello.mockClear();
66+
world.mockClear();
67+
});
68+
69+
it('hello', () => {
70+
hoisted.hello.calledWith();
71+
});
72+
```
73+
3574
## v3-apply-options-as-second-argument
3675

3776
> Vitest 3.0 prints a warning if you pass down an object as a third argument to test or describe functions.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import a from 'b';
2+
3+
const hoisted = vi.hoisted({
4+
hello: vi.fn(),
5+
world: vi.fn(),
6+
});
7+
beforeEach(() => {
8+
hello.mockClear();
9+
world.mockClear();
10+
});
11+
it('hello', () => {
12+
hoisted.hello.calledWith();
13+
});
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import a from 'b';
2+
3+
let hello;
4+
let world;
5+
6+
7+
it('hello', () => {
8+
hello.calledWith();
9+
});
10+
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import {
2+
template,
3+
types,
4+
operator,
5+
} from 'putout';
6+
7+
const {
8+
remove,
9+
rename,
10+
insertBefore,
11+
getPathAfterImports,
12+
} = operator;
13+
14+
const {
15+
isVariableDeclaration,
16+
expressionStatement,
17+
objectProperty,
18+
identifier,
19+
} = types;
20+
const {entries} = Object;
21+
22+
const createHoisted = template('const hoisted = vi.hoisted({})');
23+
const createMockClear = template('NAME.mockClear();');
24+
const createBeforeEach = template('beforeEach(() => {})');
25+
26+
export const report = () => `Use 'vi.hoisted()'`;
27+
28+
export const fix = ({path, names}) => {
29+
const hoistedNode = createHoisted();
30+
const beforeEach = createBeforeEach();
31+
32+
const [object] = hoistedNode.declarations[0].init.arguments;
33+
const {body} = beforeEach.arguments[0].body;
34+
35+
for (const name of names) {
36+
const key = identifier(name);
37+
const value = template.ast('vi.fn()');
38+
const property = objectProperty(key, value);
39+
40+
object.properties.push(property);
41+
body.push(expressionStatement(createMockClear({
42+
NAME: name,
43+
})));
44+
}
45+
46+
const afterImportsPath = getPathAfterImports(path.get('body'));
47+
48+
insertBefore(afterImportsPath, hoistedNode);
49+
insertBefore(afterImportsPath, expressionStatement(beforeEach));
50+
};
51+
52+
export const traverse = ({push}) => ({
53+
Program(path) {
54+
const names = [];
55+
56+
for (const [name, binding] of entries(path.scope.bindings)) {
57+
const {parentPath} = binding.path;
58+
59+
if (!isVariableDeclaration(parentPath))
60+
continue;
61+
62+
if (parentPath.node.declarations[0].init)
63+
continue;
64+
65+
rename(path, name, `hoisted.${name}`);
66+
remove(binding.path);
67+
names.push(name);
68+
}
69+
70+
if (names.length)
71+
push({
72+
path,
73+
names,
74+
});
75+
},
76+
});
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import {createTest} from '@putout/test';
2+
import * as plugin from './index.js';
3+
4+
const test = createTest(import.meta.url, {
5+
plugins: [
6+
['apply-hoisted', plugin],
7+
],
8+
});
9+
10+
test('vitest: apply-hoisted: report', (t) => {
11+
t.report('apply-hoisted', `Use 'vi.hoisted()'`);
12+
t.end();
13+
});
14+
15+
test('vitest: apply-hoisted: transform', (t) => {
16+
t.transform('apply-hoisted');
17+
t.end();
18+
});
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1+
import * as applyHoisted from './apply-hoisted/index.js';
12
import * as v3ApplyOptionsAsSecondArgument from './v3-apply-options-as-second-argument/index.js';
23
import * as v3ApplyBrowserInstances from './v3-apply-browser-instances/index.js';
34

45
export const rules = {
56
'v3-apply-options-as-second-argument': v3ApplyOptionsAsSecondArgument,
67
'v3-apply-browser-instances': v3ApplyBrowserInstances,
8+
'apply-hoisted': applyHoisted,
79
};
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import a from 'b';
2+
3+
const hoisted = vi.hoisted({
4+
hello: vi.fn(),
5+
world: vi.fn(),
6+
});
7+
beforeEach(() => {
8+
hello.mockClear();
9+
world.mockClear();
10+
});
11+
it('hello', () => {
12+
hoisted.hello.calledWith();
13+
});
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import a from 'b';
2+
3+
let hello;
4+
let world;
5+
6+
7+
it('hello', () => {
8+
hello.calledWith();
9+
});
10+

packages/plugin-vitest/test/vitest.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,8 @@ test('plugin-vitest: transform: v3-apply-browser-instances', (t) => {
1616
t.transform('v3-apply-browser-instances');
1717
t.end();
1818
});
19+
20+
test('plugin-vitest: transform: apply-hoisted', (t) => {
21+
t.transform('apply-hoisted');
22+
t.end();
23+
});

0 commit comments

Comments
 (0)