Skip to content

Commit b9a98f1

Browse files
authored
ci: introduce linting (#4)
1 parent b348aab commit b9a98f1

24 files changed

+4112
-579
lines changed

.editorconfig

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
1-
root = true
2-
31
[*]
42
indent_style = tab
53
tab_width = 2
64
end_of_line = lf
75
charset = utf-8
86
trim_trailing_whitespace = true
97
insert_final_newline = true
10-
max_line_length = 100
8+
max_line_length = 120
119

1210
[*.yml]
1311
indent_style = space

.github/workflows/ci.yml

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -13,27 +13,27 @@ permissions:
1313
contents: read
1414

1515
jobs:
16-
# ToDo: Configure linter
17-
# lint:
18-
# name: "Lint"
19-
# runs-on: ubuntu-latest
20-
# steps:
21-
# - name: "Checkout"
22-
# uses: actions/checkout@v4
23-
# with:
24-
# fetch-depth: 0
25-
# - name: 'Enable corepack'
26-
# run: corepack enable
27-
# - uses: actions/setup-node@v4
28-
# with:
29-
# node-version: 22
30-
# cache: 'yarn'
31-
#
32-
# - name: "Install dependencies"
33-
# run: yarn install --immutable
34-
#
35-
# - name: "Lint"
36-
# run: yarn lint -f @react-hookz/gha
16+
17+
lint:
18+
name: "Lint"
19+
runs-on: ubuntu-latest
20+
steps:
21+
- name: "Checkout"
22+
uses: actions/checkout@v4
23+
with:
24+
fetch-depth: 0
25+
- name: 'Enable corepack'
26+
run: corepack enable
27+
- uses: actions/setup-node@v4
28+
with:
29+
node-version: 22
30+
cache: 'yarn'
31+
32+
- name: "Install dependencies"
33+
run: yarn install --immutable
34+
35+
- name: "Lint"
36+
run: yarn lint -f @react-hookz/gha
3737

3838
build:
3939
name: "Build"
@@ -98,7 +98,7 @@ jobs:
9898
permissions:
9999
pull-requests: write
100100
contents: write
101-
needs: [ "test", "build" ]
101+
needs: [ "test", "build", "lint" ]
102102
if: github.actor == 'dependabot[bot]' && github.event_name == 'pull_request'
103103
steps:
104104
- uses: fastify/github-action-merge-dependabot@v3
@@ -108,7 +108,7 @@ jobs:
108108
semantic-release:
109109
name: "Release"
110110
runs-on: ubuntu-latest
111-
needs: [ "test", "build" ]
111+
needs: [ "test", "build", "lint" ]
112112
if: github.event_name == 'push' || github.event_name == 'workflow_dispatch'
113113
outputs:
114114
new-release-published: ${{ steps.release.outputs.new-release-published }}

.husky/pre-commit

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
yarn lint-staged

.prettierrc.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import ver0Cfg from '@ver0/eslint-config/.prettierrc.js';
2+
3+
/**
4+
* @type {import("prettier").Config}
5+
*/
6+
export default {
7+
...ver0Cfg,
8+
};

eslint.config.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import {buildConfig} from '@ver0/eslint-config';
2+
3+
/** @typedef {import('eslint').Linter} Linter */
4+
5+
/** @type {Linter.Config[]} */
6+
const cfg = [
7+
...buildConfig({
8+
globals: 'node',
9+
react: true,
10+
vitest: true,
11+
}),
12+
{
13+
files: ['README.md'],
14+
language: 'markdown/gfm',
15+
},
16+
];
17+
18+
export default cfg;

package.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121
},
2222
"license": "MIT",
2323
"author": "Anton Zinovyev <xog3@yandex.ru>",
24+
"engines": {
25+
"node": ">=18"
26+
},
2427
"type": "module",
2528
"files": [
2629
"dist"
@@ -31,16 +34,21 @@
3134
"postinstall": "husky",
3235
"build": "yarn build:clean && yarn tsc -p tsconfig.build.json",
3336
"build:clean": "rimraf dist",
37+
"lint": "eslint",
38+
"lint:fix": "eslint --fix",
3439
"test": "vitest --run",
3540
"test:coverage": "vitest --run --coverage"
3641
},
3742
"packageManager": "yarn@4.6.0",
3843
"devDependencies": {
3944
"@commitlint/cli": "^19.7.1",
4045
"@commitlint/config-conventional": "^19.7.1",
46+
"@react-hookz/eslint-formatter-gha": "^3.0.3",
4147
"@types/react": "^19.0.8",
4248
"@types/react-dom": "^19.0.3",
49+
"@ver0/eslint-config": "^1.1.1",
4350
"@vitest/coverage-v8": "^3.0.5",
51+
"eslint": "^9.19.0",
4452
"husky": "^9.1.7",
4553
"jsdom": "^26.0.0",
4654
"lint-staged": "^15.4.3",

src/act.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
1-
import {act as reAct} from "react";
1+
import {act as reAct} from 'react';
22

3-
type reactGlobal = typeof globalThis & { IS_REACT_ACT_ENVIRONMENT?: boolean }
3+
type reactGlobal = typeof globalThis & {IS_REACT_ACT_ENVIRONMENT?: boolean};
44

5-
6-
/* v8 ignore next 7 */
5+
/* v8 ignore next 9 */
76
function getGlobalThis(): reactGlobal {
87
if (typeof globalThis !== 'undefined') return globalThis;
8+
// eslint-disable-next-line unicorn/prefer-global-this
99
if (typeof self !== 'undefined') return self;
10+
// eslint-disable-next-line unicorn/prefer-global-this
1011
if (typeof window !== 'undefined') return window;
1112

1213
throw new Error('Unable to locate global object');
@@ -20,7 +21,7 @@ function setIsReactEnvironment() {
2021

2122
return () => {
2223
g.IS_REACT_ACT_ENVIRONMENT = initial;
23-
}
24+
};
2425
}
2526

2627
/**

src/cleanup.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export async function hooksCleanup() {
99
for (const cb of cbs) {
1010
cbs.delete(cb);
1111

12+
// eslint-disable-next-line no-await-in-loop
1213
await cb();
1314
}
1415
}
@@ -18,8 +19,6 @@ export async function hooksCleanup() {
1819
*/
1920
export function cleanupAdd(cb: CleanupCallback) {
2021
cbs.add(cb);
21-
22-
return () => cleanupRemove(cb);
2322
}
2423

2524
/**

src/create-hook-renderer.ts

Lines changed: 41 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,31 @@
1-
import {JSXElementConstructor, ReactNode} from "react";
2-
import type {RootOptions} from "react-dom/client";
3-
import {cleanupAdd, cleanupRemove} from "./cleanup.js";
1+
import type {JSXElementConstructor, ReactNode} from 'react';
2+
import type {RootOptions} from 'react-dom/client';
3+
import {cleanupAdd, cleanupRemove} from './cleanup.js';
44

55
/**
66
* Represents the result of rendering a hook.
77
*/
8-
export type ResultValue<T> = {
9-
readonly value: undefined;
10-
readonly error: Error;
11-
} | {
12-
readonly value: T;
13-
readonly error: undefined;
14-
}
8+
export type ResultValue<T> =
9+
| {
10+
readonly value: undefined;
11+
readonly error: Error;
12+
}
13+
| {
14+
readonly value: T;
15+
readonly error: undefined;
16+
};
1517

1618
/**
1719
* Represents the last rendered hook result and all previous results.
1820
*/
1921
export type ResultValues<T> = ResultValue<T> & {
20-
readonly all: ResultValue<T>[];
21-
}
22+
readonly all: Array<ResultValue<T>>;
23+
};
2224

2325
function newResults<T>() {
24-
const results: ResultValue<T>[] = []
26+
const results: Array<ResultValue<T>> = [];
2527

28+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
2629
const result = {
2730
get all() {
2831
return [...results];
@@ -35,7 +38,7 @@ function newResults<T>() {
3538
return undefined;
3639
}
3740

38-
return results[results.length - 1].value;
41+
return results.at(-1)!.value;
3942
},
4043
get error() {
4144
// it is impossible to test it in unit-tests - results are populated
@@ -45,9 +48,9 @@ function newResults<T>() {
4548
return undefined;
4649
}
4750

48-
return results[results.length - 1].error;
51+
return results.at(-1)!.error;
4952
},
50-
} as ResultValues<T>
53+
} as ResultValues<T>;
5154

5255
return {
5356
result,
@@ -56,7 +59,7 @@ function newResults<T>() {
5659
},
5760
setError(error: Error) {
5861
results.push(Object.freeze({value: undefined, error}));
59-
}
62+
},
6063
};
6164
}
6265

@@ -65,21 +68,24 @@ export type Renderer<Props> = {
6568
render(props?: Props): Promise<void>;
6669
rerender(props?: Props): Promise<void>;
6770
unmount(): Promise<void>;
68-
}
71+
};
6972

7073
// Describes the props that renderer creator receives, which allows it to expose callback invocation results.
7174
export type RendererProps<Props, Result> = {
7275
callback(props: Props): Result;
7376
setValue(value: Result): void;
7477
setError(error: Error): void;
75-
}
78+
};
7679

7780
// Describes the renderer creator function that creates a renderer for a specific hook.
78-
export type RendererCreator<Props, Result, TRenderer extends Renderer<Props>> = (props: RendererProps<Props, Result>, options?: RendererOptions<NoInfer<Props>>) => TRenderer;
81+
export type RendererCreator<Props, Result, TRenderer extends Renderer<Props>> = (
82+
props: RendererProps<Props, Result>,
83+
options?: RendererOptions<NoInfer<Props>>
84+
) => TRenderer;
7985
export type RendererOptions<Props> = {
8086
initialProps?: Props;
81-
wrapper?: JSXElementConstructor<{ children: ReactNode }>
82-
} & Pick<RootOptions, 'onCaughtError' | 'onRecoverableError'>
87+
wrapper?: JSXElementConstructor<{children: ReactNode}>;
88+
} & Pick<RootOptions, 'onCaughtError' | 'onRecoverableError'>;
8389

8490
export function createHookRenderer<Props, Result, TRenderer extends Renderer<Props>>(
8591
createRenderer: RendererCreator<Props, Result, TRenderer>
@@ -90,24 +96,26 @@ export function createHookRenderer<Props, Result, TRenderer extends Renderer<Pro
9096
// this variable holds previous props, so it is possible to rerender
9197
// with previous props preserved
9298
let hookProps = options?.initialProps;
93-
const {render, rerender, unmount, ...rest} = createRenderer({
94-
callback,
95-
setValue,
96-
setError
97-
}, options)
98-
99+
const {render, rerender, unmount, ...rest} = createRenderer(
100+
{
101+
callback,
102+
setValue,
103+
setError,
104+
},
105+
options
106+
);
99107

100108
await render(hookProps);
101109

102110
const rerenderHook = async (props?: Props) => {
103111
hookProps = props ?? hookProps;
104112
await rerender(hookProps);
105-
}
113+
};
106114

107115
const unmountHook = async () => {
108-
cleanupRemove(unmountHook)
116+
cleanupRemove(unmountHook);
109117
await unmount();
110-
}
118+
};
111119

112120
cleanupAdd(unmountHook);
113121

@@ -116,6 +124,6 @@ export function createHookRenderer<Props, Result, TRenderer extends Renderer<Pro
116124
rerender: rerenderHook,
117125
unmount: unmountHook,
118126
...rest,
119-
}
120-
}
127+
};
128+
};
121129
}

0 commit comments

Comments
 (0)