Skip to content

Commit 4e0eb04

Browse files
authored
Merge pull request #696 from ap-1/oxc
feat: add oxlint and oxfmt hooks
2 parents c06f90f + bf83e95 commit 4e0eb04

3 files changed

Lines changed: 297 additions & 0 deletions

File tree

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,8 @@ hooks](modules/pre-commit.nix).
423423
- [denofmt](https://docs.deno.com/runtime/reference/cli/fmt/)
424424
- [denolint](https://docs.deno.com/runtime/reference/cli/lint/)
425425
- [eslint](https://github.com/eslint/eslint)
426+
- [oxfmt](https://oxc.rs/docs/guide/usage/formatter)
427+
- [oxlint](https://oxc.rs/docs/guide/usage/linter)
426428
- rome: (alias to the biome hook)
427429

428430
### JSON

modules/hooks.nix

Lines changed: 291 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1104,6 +1104,234 @@ in
11041104
};
11051105
};
11061106
};
1107+
oxfmt = mkOption {
1108+
description = "oxfmt hook";
1109+
type = types.submodule {
1110+
imports = [ hookModule ];
1111+
options.settings = {
1112+
binPath =
1113+
mkOption {
1114+
type = types.nullOr (types.oneOf [ types.str types.path ]);
1115+
description = ''
1116+
`oxfmt` binary path.
1117+
For example, if you want to use the `oxfmt` binary from `node_modules`, use `"./node_modules/.bin/oxfmt"`.
1118+
Use a string instead of a path to avoid having to Git track the file in projects that use Nix flakes.
1119+
'';
1120+
default = null;
1121+
defaultText = lib.literalExpression ''
1122+
"''${tools.oxfmt}/bin/oxfmt"
1123+
'';
1124+
example = lib.literalExpression ''
1125+
"./node_modules/.bin/oxfmt"
1126+
'';
1127+
};
1128+
1129+
mode =
1130+
mkOption {
1131+
type = types.enum [ "write" "check" "list-different" ];
1132+
description = ''
1133+
Output mode.
1134+
* `write` - Format and write files in place (default).
1135+
* `check` - Check if files are formatted, also show statistics.
1136+
* `list-different` - List files that would be changed.
1137+
'';
1138+
default = "write";
1139+
};
1140+
1141+
threads =
1142+
mkOption {
1143+
type = types.nullOr types.int;
1144+
description = "Number of threads to use. Set to 1 for using only 1 CPU core.";
1145+
default = null;
1146+
};
1147+
1148+
configPath =
1149+
mkOption {
1150+
type = types.nullOr (types.oneOf [ types.str types.path ]);
1151+
description = "Path to the configuration file.";
1152+
default = null;
1153+
example = "./oxfmtrc.json";
1154+
};
1155+
};
1156+
};
1157+
};
1158+
oxlint = mkOption {
1159+
description = "oxlint hook";
1160+
type = types.submodule {
1161+
imports = [ hookModule ];
1162+
options.settings = {
1163+
allow =
1164+
mkOption {
1165+
type = types.listOf types.str;
1166+
description = "Allow the rule or category (suppress the lint).";
1167+
default = [ ];
1168+
example = [ "correctness" "no-debugger" ];
1169+
};
1170+
1171+
warn =
1172+
mkOption {
1173+
type = types.listOf types.str;
1174+
description = "Warn on the rule or category (emit a warning).";
1175+
default = [ ];
1176+
example = [ "suspicious" ];
1177+
};
1178+
1179+
deny =
1180+
mkOption {
1181+
type = types.listOf types.str;
1182+
description = "Deny the rule or category (emit an error).";
1183+
default = [ ];
1184+
example = [ "correctness" "perf" ];
1185+
};
1186+
1187+
plugins =
1188+
mkOption {
1189+
type = types.listOf (types.enum [ "import" "jest" "jsdoc" "jsx-a11y" "nextjs" "node" "oxc" "promise" "react" "react-perf" "typescript" "unicorn" "vitest" "vue" ]);
1190+
description = "Plugins to enable. Plugins not in this list will be disabled.";
1191+
default = [ "oxc" "unicorn" "typescript" ];
1192+
};
1193+
1194+
binPath =
1195+
mkOption {
1196+
type = types.nullOr (types.oneOf [ types.str types.path ]);
1197+
description = ''
1198+
`oxlint` binary path.
1199+
For example, if you want to use the `oxlint` binary from `node_modules`, use `"./node_modules/.bin/oxlint"`.
1200+
Use a string instead of a path to avoid having to Git track the file in projects that use Nix flakes.
1201+
'';
1202+
default = null;
1203+
defaultText = lib.literalExpression ''
1204+
"''${tools.oxlint}/bin/oxlint"
1205+
'';
1206+
example = lib.literalExpression ''
1207+
"./node_modules/.bin/oxlint"
1208+
'';
1209+
};
1210+
1211+
fix =
1212+
mkOption {
1213+
type = types.listOf (types.enum [ "safe" "suggestions" "dangerously" ]);
1214+
description = ''
1215+
Which fix tiers to enable. Each tier is independent and combinable.
1216+
* `safe` - Fix as many issues as possible (`--fix`).
1217+
* `suggestions` - Apply auto-fixable suggestions that may change program behavior (`--fix-suggestions`).
1218+
* `dangerously` - Apply dangerous fixes and suggestions (`--fix-dangerously`).
1219+
'';
1220+
default = [ ];
1221+
example = [ "safe" "suggestions" ];
1222+
};
1223+
1224+
denyWarnings =
1225+
mkOption {
1226+
type = types.bool;
1227+
description = "Ensure warnings produce a non-zero exit code.";
1228+
default = false;
1229+
};
1230+
1231+
format =
1232+
mkOption {
1233+
type = types.enum [ "default" "checkstyle" "github" "gitlab" "json" "junit" "stylish" "unix" ];
1234+
description = "Output format.";
1235+
default = "default";
1236+
};
1237+
1238+
quiet =
1239+
mkOption {
1240+
type = types.bool;
1241+
description = "Disable reporting on warnings, only errors are reported.";
1242+
default = false;
1243+
};
1244+
1245+
maxWarnings =
1246+
mkOption {
1247+
type = types.nullOr types.int;
1248+
description = "Specify a warning threshold, which can be used to force exit with an error status if there are too many warning-level rule violations.";
1249+
default = null;
1250+
};
1251+
1252+
silent =
1253+
mkOption {
1254+
type = types.bool;
1255+
description = "Do not display any diagnostics.";
1256+
default = false;
1257+
};
1258+
1259+
typeAware =
1260+
mkOption {
1261+
type = types.bool;
1262+
description = "Enable rules that require type information.";
1263+
default = false;
1264+
};
1265+
1266+
typeCheck =
1267+
mkOption {
1268+
type = types.bool;
1269+
description = "Enable experimental type checking (includes TypeScript compiler diagnostics).";
1270+
default = false;
1271+
};
1272+
1273+
disableNestedConfig =
1274+
mkOption {
1275+
type = types.bool;
1276+
description = "Disable the automatic loading of nested configuration files.";
1277+
default = false;
1278+
};
1279+
1280+
ignorePath =
1281+
mkOption {
1282+
type = types.nullOr (types.oneOf [ types.str types.path ]);
1283+
description = "Specify the file to use as your ignore file.";
1284+
default = null;
1285+
example = "./.eslintignore";
1286+
};
1287+
1288+
ignorePattern =
1289+
mkOption {
1290+
type = types.listOf types.str;
1291+
description = "Patterns of files to ignore (in addition to those in ignore files).";
1292+
default = [ ];
1293+
example = [ "*.test.js" "dist/" ];
1294+
};
1295+
1296+
noIgnore =
1297+
mkOption {
1298+
type = types.bool;
1299+
description = "Disable excluding files from ignore files and ignore patterns.";
1300+
default = false;
1301+
};
1302+
1303+
threads =
1304+
mkOption {
1305+
type = types.nullOr types.int;
1306+
description = "Number of threads to use. Set to 1 for using only 1 CPU core.";
1307+
default = null;
1308+
};
1309+
1310+
reportUnusedDisableDirectivesSeverity =
1311+
mkOption {
1312+
type = types.nullOr (types.enum [ "error" "warn" "log" "debug" ]);
1313+
description = "Severity level for unused disable directives.";
1314+
default = null;
1315+
};
1316+
1317+
tsconfig =
1318+
mkOption {
1319+
type = types.nullOr (types.oneOf [ types.str types.path ]);
1320+
description = "TypeScript tsconfig.json path for reading path alias and project references.";
1321+
default = null;
1322+
example = "./tsconfig.json";
1323+
};
1324+
1325+
configPath =
1326+
mkOption {
1327+
type = types.nullOr (types.oneOf [ types.str types.path ]);
1328+
description = "Path to the configuration file.";
1329+
default = null;
1330+
example = "./oxlintrc.json";
1331+
};
1332+
};
1333+
};
1334+
};
11071335
php-cs-fixer = mkOption {
11081336
description = "php-cs-fixer hook";
11091337
type = types.submodule {
@@ -3770,6 +3998,69 @@ lib.escapeShellArgs (lib.concatMap (ext: [ "--ghc-opt" "-X${ext}" ]) hooks.fourm
37703998
"${hooks.ormolu.package}/bin/ormolu --mode inplace ${extensions} ${cabalExtensions}";
37713999
files = "\\.l?hs(-boot)?$";
37724000
};
4001+
oxfmt =
4002+
{
4003+
name = "oxfmt";
4004+
description = "A fast formatter for JavaScript and TypeScript";
4005+
types_or = [ "javascript" "jsx" "ts" "tsx" ];
4006+
4007+
package = tools.oxfmt;
4008+
entry =
4009+
let
4010+
binPath = migrateBinPathToPackage hooks.oxfmt "/bin/oxfmt";
4011+
cmdArgs =
4012+
mkCmdArgs [
4013+
[ (hooks.oxfmt.settings.mode != "write") "--${hooks.oxfmt.settings.mode}" ]
4014+
[ (hooks.oxfmt.settings.threads != null) "--threads ${toString hooks.oxfmt.settings.threads}" ]
4015+
[ (hooks.oxfmt.settings.configPath != null) "--config ${builtins.toString hooks.oxfmt.settings.configPath}" ]
4016+
];
4017+
in
4018+
"${binPath} ${cmdArgs}";
4019+
};
4020+
oxlint =
4021+
{
4022+
name = "oxlint";
4023+
description = "A fast linter for JavaScript and TypeScript";
4024+
types_or = [ "javascript" "jsx" "ts" "tsx" ];
4025+
4026+
package = tools.oxlint;
4027+
entry =
4028+
let
4029+
binPath = migrateBinPathToPackage hooks.oxlint "/bin/oxlint";
4030+
pluginsDefault = [ "oxc" "unicorn" "typescript" ];
4031+
pluginFlags = lib.concatStringsSep " " (lib.flatten [
4032+
(map (p: "--${p}-plugin") (lib.subtractLists pluginsDefault hooks.oxlint.settings.plugins))
4033+
(map (p: "--disable-${p}-plugin") (lib.subtractLists hooks.oxlint.settings.plugins pluginsDefault))
4034+
]);
4035+
cmdArgs =
4036+
mkCmdArgs
4037+
(with hooks.oxlint.settings; [
4038+
[ (deny != [ ]) (lib.concatMapStringsSep " " (r: "--deny ${r}") deny) ]
4039+
[ (warn != [ ]) (lib.concatMapStringsSep " " (r: "--warn ${r}") warn) ]
4040+
[ (allow != [ ]) (lib.concatMapStringsSep " " (r: "--allow ${r}") allow) ]
4041+
[ (plugins != pluginsDefault) pluginFlags ]
4042+
[ (builtins.elem "safe" fix) "--fix" ]
4043+
[ (builtins.elem "suggestions" fix) "--fix-suggestions" ]
4044+
[ (builtins.elem "dangerously" fix) "--fix-dangerously" ]
4045+
[ (denyWarnings) "--deny-warnings" ]
4046+
[ (format != "default") "--format ${format}" ]
4047+
[ (quiet) "--quiet" ]
4048+
[ (silent) "--silent" ]
4049+
[ (maxWarnings != null) "--max-warnings ${toString maxWarnings}" ]
4050+
[ (threads != null) "--threads ${toString threads}" ]
4051+
[ (reportUnusedDisableDirectivesSeverity != null) "--report-unused-disable-directives-severity ${reportUnusedDisableDirectivesSeverity}" ]
4052+
[ (typeAware) "--type-aware" ]
4053+
[ (typeCheck) "--type-check" ]
4054+
[ (disableNestedConfig) "--disable-nested-config" ]
4055+
[ (ignorePath != null) "--ignore-path ${builtins.toString ignorePath}" ]
4056+
[ (ignorePattern != [ ]) (lib.concatMapStringsSep " " (p: "--ignore-pattern ${p}") ignorePattern) ]
4057+
[ (noIgnore) "--no-ignore" ]
4058+
[ (tsconfig != null) "--tsconfig ${builtins.toString tsconfig}" ]
4059+
[ (configPath != null) "--config ${builtins.toString configPath}" ]
4060+
]);
4061+
in
4062+
"${binPath} ${cmdArgs}";
4063+
};
37734064
php-cs-fixer =
37744065
{
37754066
name = "php-cs-fixer";

nix/tools.nix

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@
6868
, opam
6969
, opentofu
7070
, ormolu
71+
, oxfmt ? placeholder "oxfmt"
72+
, oxlint
7173
, pkgsBuildBuild
7274
, poetry
7375
, pre-commit-hook-ensure-sops ? placeholder "pre-commit-hook-ensure-sops"
@@ -180,6 +182,8 @@ in
180182
opam
181183
opentofu
182184
ormolu
185+
oxfmt
186+
oxlint
183187
pre-commit-hook-ensure-sops
184188
prettier
185189
poetry

0 commit comments

Comments
 (0)