Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
5c5205f
feat(eslint): add rules for testing frameworks (#492)
Rei-x May 24, 2026
81e9778
chore(release): 2.4.0-beta.1 [skip ci]
semantic-release-bot May 24, 2026
399db7c
feat(eslint): add vitest unbound-method override (#963)
kguzek May 24, 2026
6e56f98
chore(release): 3.0.0-beta.1 [skip ci]
semantic-release-bot May 24, 2026
43b1e1c
feat(eslint): add preserve-caught-error (#964)
kguzek May 24, 2026
3f33710
chore(release): 3.0.0-beta.2 [skip ci]
semantic-release-bot May 24, 2026
7e41079
fix(scripts): replace default nestjs format script (#965)
kguzek May 24, 2026
89928e0
chore(release): 3.0.0-beta.3 [skip ci]
semantic-release-bot May 24, 2026
26d784c
feat(eslint): add eslint-plugin-regexp (#966)
kguzek May 24, 2026
533eb00
chore(release): 3.0.0-beta.4 [skip ci]
semantic-release-bot May 24, 2026
a9b4d29
feat(cli): make prompt output more meaningful (#967)
kguzek May 24, 2026
ec62b79
chore(release): 3.0.0-beta.5 [skip ci]
semantic-release-bot May 24, 2026
472e58a
fix(cli): false positive dirty version (#970)
kguzek May 24, 2026
0ae36e3
chore(release): 3.0.0-beta.6 [skip ci]
semantic-release-bot May 24, 2026
8c974ed
feat(eslint): add rules for zod (#941)
ssynowiec May 24, 2026
555abeb
chore(release): 3.0.0-beta.7 [skip ci]
semantic-release-bot May 24, 2026
0b06e46
feat(cli): show branch name if locally clean (#971)
kguzek May 24, 2026
28be333
chore(release): 3.0.0-beta.8 [skip ci]
semantic-release-bot May 24, 2026
9e8c29f
chore(package): run npm pkg fix (#972)
kguzek May 24, 2026
7348984
chore(deps): merge main branch into beta
kguzek Jun 25, 2026
0e625fe
chore: merge main branch into beta
kguzek Jun 29, 2026
ffc538a
chore(release): 3.0.0-beta.9 [skip ci]
semantic-release-bot Jun 29, 2026
1c9716f
feat(eslint): add lucide & shadcn plugin
kguzek May 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
619 changes: 387 additions & 232 deletions package-lock.json

Large diffs are not rendered by default.

12 changes: 10 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@solvro/config",
"version": "2.3.4",
"version": "3.0.0-beta.9",
"description": "Solvro's engineering style guide",
"keywords": [
"eslint",
Expand Down Expand Up @@ -34,7 +34,9 @@
"require": "./dist/commitlint/index.cjs"
}
},
"bin": "./bin/index.js",
"bin": {
"config": "bin/index.js"
},
"files": [
"dist"
],
Expand Down Expand Up @@ -70,20 +72,25 @@
"@tanstack/eslint-plugin-query": "^5.101.2",
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
"@typescript-eslint/eslint-plugin": "^8.62.0",
"@vitest/eslint-plugin": "^1.6.18",
"commander": "^15.0.0",
"eslint-config-flat-gitignore": "^2.3.0",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-antfu": "^3.2.3",
"eslint-plugin-import": "^2.32.0",
"eslint-plugin-jest": "^29.15.2",
"eslint-plugin-jsdoc": "^63.0.10",
"eslint-plugin-jsx-a11y": "^6.10.2",
"eslint-plugin-n": "^18.2.1",
"eslint-plugin-playwright": "^2.10.4",
"eslint-plugin-react": "^7.37.5",
"eslint-plugin-react-hooks": "^7.1.1",
"eslint-plugin-react-refresh": "^0.5.3",
"eslint-plugin-react-you-might-not-need-an-effect": "^1.0.1",
"eslint-plugin-regexp": "^3.1.0",
"eslint-plugin-unicorn": "^64.0.0",
"eslint-plugin-unused-imports": "^4.4.1",
"eslint-plugin-zod": "^4.5.1",
"execa": "^9.6.1",
"find-up-simple": "^1.0.1",
"globals": "^17.7.0",
Expand All @@ -100,6 +107,7 @@
"@commitlint/cli": "^21.1.0",
"@commitlint/types": "^21.1.0",
"@eslint/config-inspector": "^3.0.4",
"@nelsonlaidev/eslint-plugin": "^0.2.4",
"@next/eslint-plugin-next": "^16.2.9",
"@semantic-release/git": "^10.0.1",
"@types/node": "^26.0.1",
Expand Down
41 changes: 16 additions & 25 deletions src/cli/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import { confirmProjectType } from "../utils/confirm-project-type";
import { isGitClean } from "../utils/is-git-clean";
import { PackageJson } from "../utils/package-json";
import { polishConfirm } from "../utils/polish-confirm";
import { printIntro } from "../utils/print-intro";
import { printOutro } from "../utils/print-outro";
import { installCommitLint } from "./install-commitlint";
import { installEslint } from "./install-eslint";
import { installGithubActions } from "./install-ga";
Expand Down Expand Up @@ -44,9 +46,7 @@ const options: CliOptions = program.opts();
const isNonInteractive = checkIsNonInteractive();

async function main() {
if (!isNonInteractive) {
p.intro(c.bold(c.bgBlue(" @solvro/config ")));
}
printIntro();

const packageJson = new PackageJson();
packageJson.verifyPackageManager();
Expand All @@ -56,9 +56,8 @@ async function main() {
// Git clean check
if (options.force !== true && !isGitClean()) {
if (isNonInteractive) {
console.error(
"Repozytorium Git ma niezatwierdzone zmiany. Użyj --force, aby pominąć to sprawdzenie.",
);
p.log.error("Repozytorium Git ma niezatwierdzone zmiany.");
p.cancel("Użyj --force, aby pominąć to sprawdzenie.");
process.exit(1);
}

Expand All @@ -82,11 +81,11 @@ async function main() {
eslint?.version == null
? ""
: ` Obecnie zainstalowana jest wersja ${c.yellow(eslint.version)}.`;
const errorMessage = `ESLint w wersji powyżej 9 ${c.red("nie jest jeszcze wspierany")}.${versionInfo}`;
const errorMessage = `ESLint w wersji powyżej 9 nie jest jeszcze wspierany.${versionInfo}`;
const errorRetry = "Proszę zainstalować wersję 9 i spróbować ponownie.";
if (isNonInteractive) {
console.error(errorMessage);
console.error(errorRetry);
p.log.error(errorMessage);
p.cancel(errorRetry);
process.exit(1);
}
const isConfirmed = await polishConfirm({
Expand Down Expand Up @@ -163,8 +162,9 @@ async function main() {
}

if (toolsToInstall.length === 0) {
console.error(
"Nie wybrano żadnych narzędzi. Użyj --eslint, --prettier, --gh-action, --commitlint, lub --all",
p.log.error("Nie wybrano żadnych narzędzi.");
p.cancel(
"Użyj --eslint, --prettier, --gh-action, --commitlint, lub --all",
);
process.exit(1);
}
Expand Down Expand Up @@ -235,28 +235,19 @@ async function main() {
await packageJson.localExecute("prettier", "--write", "package.json");
}

const printSuccess = isNonInteractive ? console.info : p.outro;
printSuccess("✅ Konfiguracja zakończona pomyślnie!");
printOutro();
}

async function mainWrapper() {
try {
await main();
} catch (error: unknown) {
if (process.env.NODE_ENV === "development") {
console.error(
c.red("Unhandled error in main:"),
error instanceof Error ? error.message : error,
);
p.cancel("Unhandled error in main:");
console.error(error instanceof Error ? error.message : error);
} else {
const errorMessage =
"Wystąpił nieoczekiwany błąd :( Proszę zgłosić go twórcom:";
if (isNonInteractive) {
console.error(errorMessage);
console.error(BUG_TRACKER_URL);
} else {
p.cancel(`${errorMessage} ${BUG_TRACKER_URL}`);
}
p.log.error("Wystąpił nieoczekiwany błąd :(");
p.cancel(`Proszę zgłosić go twórcom: ${BUG_TRACKER_URL}`);
}
process.exit(1);
}
Expand Down
10 changes: 5 additions & 5 deletions src/cli/install-eslint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export const installEslint = async (isNonInteractive = false) => {

if (!is15) {
p.cancel(
"Next.js musi być w conajmniej wersji 15. Zaktualizuj Next.js i spróbuj ponownie.\nWięcej informacji tutaj: https://nextjs.org/docs/app/building-your-application/upgrading/version-15",
"Next.js musi być w co najmniej wersji 15. Zaktualizuj Next.js i spróbuj ponownie.\nWięcej informacji tutaj: https://nextjs.org/docs/app/building-your-application/upgrading/version-15",
);
process.exit(1);
}
Expand All @@ -60,7 +60,7 @@ export const installEslint = async (isNonInteractive = false) => {
);

if (eslintContent.includes("export default solvro(")) {
p.note("Eslint jest już skonfigurowany. Pomijam.");
p.log.warning("ESLint jest już skonfigurowany. Pomijam.");

return;
} else {
Expand All @@ -69,11 +69,11 @@ export const installEslint = async (isNonInteractive = false) => {
await fs.rm(path.join(root, eslintConfig));
} else {
const isConfirmed = await polishConfirm({
message: `Znaleziono plik konfiguracyjny Eslint. Czy chcesz go nadpisać?`,
message: `Znaleziono plik konfiguracyjny ESLint. Czy chcesz go nadpisać?`,
});

if (p.isCancel(isConfirmed) || !isConfirmed) {
p.cancel("Nadpisz plik konfiguracyjny Eslint i spróbuj ponownie.");
p.cancel("Nadpisz plik konfiguracyjny ESLint i spróbuj ponownie.");
process.exit(1);
}

Expand All @@ -94,5 +94,5 @@ export default solvro();
`,
);

p.note("Plik konfiguracyjny Eslint został utworzony.");
p.log.step("Plik konfiguracyjny ESLint został utworzony.");
};
11 changes: 11 additions & 0 deletions src/cli/install-ga.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,17 @@ export const installGithubActions = async () => {
pnpmVersion,
}),
);
// https://github.com/Solvro/lib-web-solvro-config/issues/431
const formatScriptUpdated = await packageJson.updateScriptIfExists(
"format",
"prettier --write .",
'prettier --write "src/**/*.ts" "test/**/*.ts"',
);
if (!formatScriptUpdated) {
p.log.warning(
"Aktualizacja skryptu format została pominięta, ponieważ został on zmieniony lub nie istnieje. Ręcznie zaktualizuj skrypt format w package.json, aby formatował wszystkie pliki projektu, a nie tylko te w katalogach src i test.",
);
}
}

if (!existsSync(path.join(gitDirectory, ".github/dependabot.yml"))) {
Expand Down
4 changes: 2 additions & 2 deletions src/cli/install-prettier.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export const installPrettier = async (isNonInteractive = false) => {

if (prettierConfig !== undefined || packageJson.json.prettier !== undefined) {
if (packageJson.json.prettier === solvroPrettierPath) {
p.note("Konfiguracja Prettiera jest już ustawiona. Pomijam.");
p.log.warning("Konfiguracja Prettiera jest już ustawiona. Pomijam.");
return;
}

Expand Down Expand Up @@ -73,5 +73,5 @@ export const installPrettier = async (isNonInteractive = false) => {

await packageJson.save();

p.note("Konfiguracja Prettiera została dodana.");
p.log.step("Konfiguracja Prettiera została dodana.");
};
2 changes: 1 addition & 1 deletion src/constants/github.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
const REPO_URL = "https://github.com/Solvro/lib-web-solvro-config";
export const REPO_URL = "https://github.com/Solvro/lib-web-solvro-config";
export const BUG_TRACKER_URL = `${REPO_URL}/issues/new`;
5 changes: 4 additions & 1 deletion src/eslint/configs/javascript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { ConfigWithExtends } from "@eslint/config-helpers";
import eslint from "@eslint/js";
import globals from "globals";

import { pluginAntfu, pluginUnusedImports } from "../plugins";
import { pluginAntfu, pluginRegexp, pluginUnusedImports } from "../plugins";

export function javascript(): ConfigWithExtends[] {
return [
Expand Down Expand Up @@ -35,10 +35,12 @@ export function javascript(): ConfigWithExtends[] {
name: "solvro/javascript/rules",
plugins: {
antfu: pluginAntfu,
regexp: pluginRegexp,
"unused-imports": pluginUnusedImports,
},
rules: {
...eslint.configs.recommended.rules,
...pluginRegexp.configs["flat/recommended"].rules,
"accessor-pairs": [
"error",
{ enforceForClassMembers: true, setWithoutGet: true },
Expand Down Expand Up @@ -201,6 +203,7 @@ export function javascript(): ConfigWithExtends[] {
"prefer-rest-params": "error",
"prefer-spread": "error",
"prefer-template": "error",
"preserve-caught-error": ["error", { requireCatchParameter: true }],
"symbol-description": "error",
"unicode-bom": ["error", "never"],
"unused-imports/no-unused-imports": "error",
Expand Down
25 changes: 17 additions & 8 deletions src/eslint/configs/react.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import type { ConfigWithExtends } from "@eslint/config-helpers";
import pluginQuery from "@tanstack/eslint-plugin-query";
import pluginReact from "eslint-plugin-react";
import pluginReactHooks from "eslint-plugin-react-hooks";
import pluginReactRefresh from "eslint-plugin-react-refresh";
import reactYouMightNotNeedAnEffect from "eslint-plugin-react-you-might-not-need-an-effect";
import type { ConfigWithExtends, Plugin } from "@eslint/config-helpers";
import { isPackageListedSync } from "local-pkg";

import {
pluginNelsonLai,
pluginQuery,
pluginReact,
pluginReactEffects,
pluginReactHooks,
pluginReactRefresh,
} from "../plugins";

const nextJsPackages = ["next"];
const vitePackages = ["vite"];

Expand Down Expand Up @@ -56,8 +60,8 @@ export async function react(): Promise<ConfigWithExtends[]> {
name: "solvro/react/setup",
plugins: {
react: pluginReact,

"react-hooks": pluginReactHooks,
"@nelsonlaidev": pluginNelsonLai as unknown as Plugin,
},
},
...nextjsConfig,
Expand Down Expand Up @@ -101,10 +105,15 @@ export async function react(): Promise<ConfigWithExtends[]> {
},
],
"react/no-array-index-key": "warn",
// https://github.com/nelsonlaidev/config/tree/main/packages/eslint-plugin
"@nelsonlaidev/lucide-icon-suffix": "error",
"@nelsonlaidev/shadcn-cn-wrap-variants": "error",
"@nelsonlaidev/shadcn-cva-variants-suffix": "error",
"@nelsonlaidev/shadcn-prefer-spinner": "error",
},
},
...pluginQuery.configs["flat/recommended"],
reactYouMightNotNeedAnEffect.configs.recommended,
pluginReactEffects.configs.recommended,
{
name: "solvro/react/disables",
files: ["**/components/ui/*.{jsx,tsx}"],
Expand Down
95 changes: 95 additions & 0 deletions src/eslint/configs/testing.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import type { ConfigWithExtends } from "@eslint/config-helpers";
import vitest from "@vitest/eslint-plugin";
import jest from "eslint-plugin-jest";
import playwright from "eslint-plugin-playwright";
import { isPackageListed } from "local-pkg";

export const testing = async ({
testFiles,
playwrightTestFiles,
}: {
testFiles: string[];
playwrightTestFiles: string[];
}): Promise<ConfigWithExtends[]> => {
const isVitest = await isPackageListed("vitest");
const isJest = await isPackageListed("jest");

if (isVitest && isJest) {
throw new Error(
"Both Vitest and Jest are installed: please uninstall one of them.",
);
}

const isPlaywright = await isPackageListed("@playwright/test");

const playwrightConfig = isPlaywright
? ([
{
...playwright.configs["flat/recommended"],
files: playwrightTestFiles,
rules: {
...playwright.configs["flat/recommended"].rules,
"playwright/prefer-native-locators": "warn",
"playwright/prefer-locator": "warn",
"playwright/prefer-comparison-matcher": "warn",
"playwright/require-top-level-describe": "error",
"playwright/prefer-hooks-on-top": "warn",
"playwright/prefer-hooks-in-order": "warn",
"playwright/prefer-equality-matcher": "error",
"playwright/no-raw-locators": "warn",
},
},
] as const satisfies ConfigWithExtends[])
: [];

const vitestConfig = isVitest
? ([
{
files: testFiles,
plugins: {
vitest,
},
rules: {
...vitest.configs.recommended.rules,
"vitest/consistent-test-it": [
"error",
{
fn: "it",
},
],
"vitest/no-focused-tests": ["warn"],
"vitest/no-mocks-import": ["error"],
"vitest/no-test-return-statement": ["error"],
"vitest/prefer-lowercase-title": ["error"],
"vitest/require-top-level-describe": ["warn"],
"vitest/prefer-vi-mocked": ["error"],
"vitest/prefer-hooks-in-order": ["warn"],
"vitest/prefer-hooks-on-top": ["warn"],
"vitest/prefer-each": ["warn"],
"vitest/no-conditional-tests": ["error"],
"vitest/prefer-comparison-matcher": ["error"],
"vitest/no-conditional-in-test": ["warn"],
"vitest/consistent-vitest-vi": ["error"],
"@typescript-eslint/unbound-method": "off",
"vitest/unbound-method": ["error"],
},
},
] as const satisfies ConfigWithExtends[])
: [];

const jestConfig = isJest
? ([
{
files: testFiles,
plugins: {
jest,
},
rules: {
...jest.configs.recommended.rules,
},
},
] as const)
: [];

return [...playwrightConfig, ...vitestConfig, ...jestConfig];
};
Loading
Loading