@mkrz/oxlint-config 0.1.0 → 0.1.1

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.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.1 (2026-09-06)
4
+
5
+ - Stop `--fix` from adding `new` to curried Error factories such as Effect's
6
+ `Schema.TaggedError`. Disable the name-based `unicorn/throw-new-error` rule;
7
+ type-aware `typescript/only-throw-error` remains enabled.
8
+ - Require separate top-level `import type` declarations and forbid inline type
9
+ specifiers, including in mixed imports. Preserve erased type imports under
10
+ `verbatimModuleSyntax` to avoid loading declaration-only modules.
11
+
12
+ - Fix false positives for `Record` types with finite keys, including aliases
13
+ and generic substitutions. Broad dictionary keys remain checked.
14
+
3
15
  ## 0.1.0 (2026-09-04)
4
16
 
5
17
  First release.
package/README.md CHANGED
@@ -131,6 +131,14 @@ of this package. Oxlint only honors `ignorePatterns`, `env`, `globals`, and
131
131
  wrapper folds them back in. The ignore list is also exported as
132
132
  `ignorePatterns`.
133
133
 
134
+ ### Type imports
135
+
136
+ Always use separate top-level `import type` declarations for types. Inline
137
+ type specifiers are forbidden, including in imports that also contain values.
138
+ With `verbatimModuleSyntax`, an all-inline type import leaves a runtime
139
+ `import {}` behind and can fail for packages that contain only declarations.
140
+ The config also checks for these unintended runtime dependencies.
141
+
134
142
  ## Repository types
135
143
 
136
144
  - `app`: no Oxlint or ESLint disable comment anywhere.
package/dist/index.js CHANGED
@@ -101,7 +101,7 @@ const base = {
101
101
  "always",
102
102
  { null: "ignore" }
103
103
  ],
104
- "import/consistent-type-specifier-style": ["error", "prefer-inline"],
104
+ "import/consistent-type-specifier-style": ["error", "prefer-top-level"],
105
105
  "import/no-duplicates": "error",
106
106
  "import/no-relative-parent-imports": "error",
107
107
  "no-alert": "error",
@@ -137,12 +137,13 @@ const base = {
137
137
  "typescript/consistent-indexed-object-style": "error",
138
138
  "typescript/consistent-type-definitions": ["error", "type"],
139
139
  "typescript/consistent-type-imports": ["error", {
140
- fixStyle: "inline-type-imports",
140
+ fixStyle: "separate-type-imports",
141
141
  prefer: "type-imports"
142
142
  }],
143
143
  "typescript/no-empty-object-type": "error",
144
144
  "typescript/no-explicit-any": "error",
145
145
  "typescript/no-inferrable-types": "error",
146
+ "typescript/no-import-type-side-effects": "error",
146
147
  "typescript/no-namespace": "error",
147
148
  "typescript/no-non-null-asserted-nullish-coalescing": "error",
148
149
  "typescript/no-non-null-assertion": "error",
@@ -161,7 +162,7 @@ const base = {
161
162
  "unicorn/prefer-date-now": "error",
162
163
  "unicorn/prefer-node-protocol": "error",
163
164
  "unicorn/prefer-number-properties": "error",
164
- "unicorn/throw-new-error": "error"
165
+ "unicorn/throw-new-error": "off"
165
166
  },
166
167
  overrides: [{
167
168
  files: [
@@ -195,34 +196,6 @@ const base = {
195
196
  }]
196
197
  };
197
198
  //#endregion
198
- //#region src/configs/monorepo.ts
199
- const message = "Move shared code into a package and import it from there instead.";
200
- /**
201
- * Overrides for the apps/packages layout.
202
- *
203
- * Relative imports that climb out of a package are already rejected by
204
- * `import/no-relative-parent-imports` in `base`, so only workspace package
205
- * names need a rule here.
206
- */
207
- function monorepo({ appsPath = ["apps/**"], packagesPath = ["packages/**"], appPackages = [] } = {}) {
208
- const packageRules = { "mkrz/package-disable-policy": "error" };
209
- if (appPackages.length > 0) {
210
- const patterns = appPackages.flatMap((name) => [name, `${name}/**`]);
211
- packageRules["mkrz/no-app-requires"] = ["error", patterns];
212
- packageRules["no-restricted-imports"] = ["error", { patterns: [{
213
- group: patterns,
214
- message
215
- }] }];
216
- }
217
- return { overrides: [{
218
- files: appsPath,
219
- rules: { "mkrz/no-oxlint-disable": "error" }
220
- }, {
221
- files: packagesPath,
222
- rules: packageRules
223
- }] };
224
- }
225
- //#endregion
226
199
  //#region src/configs/plugin-specifiers.ts
227
200
  const modulePath = fileURLToPath(import.meta.url);
228
201
  const moduleExtension = path.extname(modulePath);
@@ -368,6 +341,34 @@ const react = {
368
341
  }
369
342
  };
370
343
  //#endregion
344
+ //#region src/configs/monorepo.ts
345
+ const message = "Move shared code into a package and import it from there instead.";
346
+ /**
347
+ * Overrides for the apps/packages layout.
348
+ *
349
+ * Relative imports that climb out of a package are already rejected by
350
+ * `import/no-relative-parent-imports` in `base`, so only workspace package
351
+ * names need a rule here.
352
+ */
353
+ function monorepo({ appsPath = ["apps/**"], packagesPath = ["packages/**"], appPackages = [] } = {}) {
354
+ const packageRules = { "mkrz/package-disable-policy": "error" };
355
+ if (appPackages.length > 0) {
356
+ const patterns = appPackages.flatMap((name) => [name, `${name}/**`]);
357
+ packageRules["mkrz/no-app-requires"] = ["error", patterns];
358
+ packageRules["no-restricted-imports"] = ["error", { patterns: [{
359
+ group: patterns,
360
+ message
361
+ }] }];
362
+ }
363
+ return { overrides: [{
364
+ files: appsPath,
365
+ rules: { "mkrz/no-oxlint-disable": "error" }
366
+ }, {
367
+ files: packagesPath,
368
+ rules: packageRules
369
+ }] };
370
+ }
371
+ //#endregion
371
372
  //#region src/configs/repository.ts
372
373
  const monorepoDirectories = ["apps", "packages"];
373
374
  /**
@@ -604,8 +604,9 @@ function dictionaryValueTypes(type, environment, substitutions, resolvingAliases
604
604
  return wrapped === void 0 ? [] : dictionaryValueTypes(wrapped, environment, substitutions, resolvingAliases);
605
605
  }
606
606
  if (name === "Record" && isBuiltIn(name, environment)) {
607
- const value = unwrapped.typeArguments?.params[1] ?? null;
608
- return value === null ? [] : [{
607
+ const key = unwrapped.typeArguments?.params[0];
608
+ const value = unwrapped.typeArguments?.params[1];
609
+ return key === void 0 || value === void 0 || !isBroadMappedKey(key, environment, substitutions) ? [] : [{
609
610
  type: value,
610
611
  substitutions
611
612
  }];
@@ -656,7 +657,10 @@ function classifyWideningTarget(type, environment, shadowed = /* @__PURE__ */ ne
656
657
  const wrapped = unwrapped.typeArguments?.params[0];
657
658
  return wrapped === void 0 ? null : classifyWideningTarget(wrapped, environment, shadowed);
658
659
  }
659
- if (name === "Record" && isBuiltIn(name, environment)) return { kind: "open dictionary" };
660
+ if (name === "Record" && isBuiltIn(name, environment)) {
661
+ const key = unwrapped.typeArguments?.params[0];
662
+ return key !== void 0 && isBroadMappedKey(key, environment, shadowedMap) ? { kind: "open dictionary" } : { kind: "anonymous object" };
663
+ }
660
664
  const alias = environment.aliases.get(name);
661
665
  if (alias === void 0) return null;
662
666
  if ((alias.typeParameters?.params.length ?? 0) > 0) {
@@ -700,7 +704,10 @@ function classifyAliasBroadTarget(type, environment, substitutions, resolvingAli
700
704
  const wrapped = unwrapped.typeArguments?.params[0];
701
705
  return wrapped === void 0 ? null : classifyAliasBroadTarget(wrapped, environment, substitutions, resolvingAliases);
702
706
  }
703
- if (name === "Record" && isBuiltIn(name, environment)) return { kind: "open dictionary" };
707
+ if (name === "Record" && isBuiltIn(name, environment)) {
708
+ const key = unwrapped.typeArguments?.params[0];
709
+ return key !== void 0 && isBroadMappedKey(key, environment, substitutions) ? { kind: "open dictionary" } : null;
710
+ }
704
711
  const alias = environment.aliases.get(name);
705
712
  if (alias === void 0 || resolvingAliases.has(name)) return null;
706
713
  const nextSubstitutions = aliasSubstitution(alias, unwrapped, substitutions);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mkrz/oxlint-config",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Opinionated Oxlint ruleset for TypeScript and React",
5
5
  "keywords": [
6
6
  "lint",