@commonplaces/oxlint-plugin 0.8.0 → 0.9.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/README.md CHANGED
@@ -1,9 +1,9 @@
1
1
  # @commonplaces/oxlint-plugin
2
2
 
3
3
  108 custom Oxlint rules for strict Effect TypeScript, Alchemy and general code
4
- checks. Version 0.8.0 supports **Oxlint 1.87.0, 1.88.0 or 1.89.0** and **Node 22.14 or newer**.
4
+ checks. Version 0.9.0 supports **Oxlint 1.88.0, 1.89.0 or 1.90.0** and **Node 22.14 or newer**.
5
5
  Oxlint's JavaScript plugin API is experimental, so upgrade it only after running
6
- the rule tests. The development tools use 1.89.0; consuming projects may use any supported version.
6
+ the rule tests. The development tools use 1.90.0; consuming projects may use any supported version.
7
7
  All three exact versions are checked with independently installed archives. ESLint is not a supported host.
8
8
 
9
9
  Version 0.6.0 adds native Effect correctness and immutable accumulation checks.
@@ -12,15 +12,17 @@ Effect setup below after dependency installation.
12
12
 
13
13
  ## Install
14
14
 
15
- Install version 0.8.0 and save its exact version:
15
+ Install version 0.9.0 and save its exact version:
16
16
 
17
17
  ```sh
18
- npm install --save-dev --save-exact @commonplaces/oxlint-plugin@0.8.0 \
19
- @effect/tsgo@0.55.0 oxlint@1.89.0 oxlint-tsgolint@7.0.2004 typescript@7.0.2
18
+ npm install --save-dev --save-exact @commonplaces/oxlint-plugin@0.9.0 \
19
+ @effect/tsgo@0.63.0 oxlint@1.90.0 oxlint-tsgolint@7.0.2005 typescript@7.0.2
20
20
  ```
21
21
 
22
- Version 0.8.0 supports exactly Oxlint 1.87.0, 1.88.0 or 1.89.0. Older releases
23
- keep their earlier host lists. Consumer adoption is a separate step.
22
+ Version 0.9.0 supports exactly Oxlint 1.88.0, 1.89.0 or 1.90.0. Older releases
23
+ keep their earlier host lists. Version 0.9.0 removes 1.87.0 because the native
24
+ Effect checker no longer supports it. Upgrade Oxlint before adopting this release.
25
+ Consumer adoption is a separate step.
24
26
 
25
27
  The manifest and lockfile retain the resolved version. Historical GitHub release
26
28
  archives remain separate distribution artefacts; a GitHub release does not publish
@@ -67,18 +69,18 @@ the subpaths below. For example, a JSON configuration can enable one rule:
67
69
  ## Native Effect correctness
68
70
 
69
71
  Both `effect` and `strict` include every check in the pinned
70
- `@effect/tsgo` **0.55.0** correctness preset, all at error level. This catches
72
+ `@effect/tsgo` **0.63.0** correctness preset, all at error level. This catches
71
73
  forgotten Effect programs, incorrect generator yields, unhandled errors and
72
74
  requirements, unchecked Effect type assertions, missing Schema class brands and
73
75
  unstable APIs. Type-aware checking is enabled by the group itself; consumers
74
76
  do not need to import another preset. Upstream's unrelated style and maintainer
75
77
  preferences are not added to the public group.
76
78
 
77
- The package ships `@effect/tsgo` **0.55.0** and `oxlint-tsgolint` **7.0.2004**
79
+ The package ships `@effect/tsgo` **0.63.0** and `oxlint-tsgolint` **7.0.2005**
78
80
  as exact regular dependencies. Full Effect projects should also declare their
79
81
  compiler and checker tools directly, as in the install command above. This
80
82
  keeps the selected local tools explicit. The pinned checker supports TypeScript
81
- **7.0.2** or **7.1.0-dev.20261019.1**, alongside the three Oxlint versions above.
83
+ **7.0.2**, **7.1.0-beta** or **7.1.0-dev.20261028.1**, alongside the three Oxlint versions above.
82
84
  The archive tests use TypeScript 7.0.2.
83
85
  Check the installed checker's supported versions before changing these tools.
84
86
 
@@ -250,7 +252,7 @@ Both versions contain `no-unmodified-loop-condition` with
250
252
  `checkConditionalExpressions: true`; the supported hosts accept that option.
251
253
  Ultracite 8 requires Oxlint 1.86 or newer, so 1.80 and 1.85 are no longer
252
254
  supported. Its Oxfmt peer requires at least 0.71; repository tooling uses 0.74.0.
253
- The current archive is checked on exactly 1.87.0, 1.88.0 and 1.89.0, matching the
255
+ The current archive is checked on exactly 1.88.0, 1.89.0 and 1.90.0, matching the
254
256
  pinned native Effect checker. Published 0.5.0 was qualified on 1.86.0 and 1.89.0.
255
257
 
256
258
  ## Project settings
@@ -301,6 +303,11 @@ plus supplied operations in native `Layer.mock` replacements,
301
303
  preserving canonical names through named imports, namespace imports and immutable
302
304
  aliases. Shadowed names cannot borrow import identity. Every explicit operation
303
305
  requires its exact `Effect.fn("Orders.create")` or `Effect.withSpan` name.
306
+ Readonly resource groups are traversed recursively, including local immutable
307
+ aliases: `Orders.session.read` requires that full canonical span. Each supplied
308
+ leaf in `Layer.mock` is checked too. Empty resource groups, cyclic groups,
309
+ spreads and dynamic property names fail closed; local nested contract types
310
+ continue to identify data and Stream members.
304
311
  Legacy `serviceNames` selection remains available for existing consumers.
305
312
  An empty selector is rejected.
306
313
 
@@ -206,30 +206,48 @@ export const requireServiceOperationInstrumentation = defineRule({
206
206
  context.report({ node, messageId: "unsupportedOperation" });
207
207
  return;
208
208
  }
209
- Array.forEach(argument.value.properties, (property) => {
210
- if (property.type !== "Property") {
211
- context.report({ node: property, messageId: "unsupportedOperation" });
209
+ const check = (object, prefix, members, visited = []) => {
210
+ if (Array.contains(visited, object) ||
211
+ (visited.length > 0 && object.properties.length === 0)) {
212
+ context.report({ node: object, messageId: "unsupportedOperation" });
212
213
  return;
213
214
  }
214
- const key = property.computed && property.key.type !== "Literal"
215
- ? Option.none()
216
- : propertyName(property.key);
217
- Option.match(key, {
218
- onNone: () => context.report({ node: property, messageId: "unsupportedOperation" }),
219
- onSome: (member) => {
220
- if (dataMember(context, fields, member) || streamValue(context, property.value))
221
- return;
222
- const name = `${owner.name}.${member}`;
223
- if (!instrumented(context, property.value, name) &&
224
- !adapted(context, property.value, name, options.operationAdapters ?? []))
225
- context.report({
226
- node: property,
227
- messageId: "missingInstrumentation",
228
- data: { operation: name },
229
- });
230
- },
215
+ Array.forEach(object.properties, (property) => {
216
+ if (property.type !== "Property") {
217
+ context.report({ node: property, messageId: "unsupportedOperation" });
218
+ return;
219
+ }
220
+ const key = property.computed && property.key.type !== "Literal"
221
+ ? Option.none()
222
+ : propertyName(property.key);
223
+ Option.match(key, {
224
+ onNone: () => context.report({ node: property, messageId: "unsupportedOperation" }),
225
+ onSome: (member) => {
226
+ if (dataMember(context, members, member) ||
227
+ streamValue(context, property.value))
228
+ return;
229
+ const name = `${prefix}.${member}`;
230
+ const value = valueOf(context, property.value);
231
+ if (Option.isSome(value) && value.value.type === "ObjectExpression") {
232
+ const nested = Array.findFirst(members, (field) => field.type === "TSPropertySignature" &&
233
+ Option.contains(propertyName(field.key), member)).pipe(Option.flatMap((field) => field.type === "TSPropertySignature"
234
+ ? Option.fromNullishOr(field.typeAnnotation?.typeAnnotation)
235
+ : Option.none()), Option.filter((type) => type.type === "TSTypeLiteral"), Option.map((type) => (type.type === "TSTypeLiteral" ? type.members : [])), Option.getOrElse(() => []));
236
+ check(value.value, name, nested, [...visited, object]);
237
+ return;
238
+ }
239
+ if (!instrumented(context, property.value, name) &&
240
+ !adapted(context, property.value, name, options.operationAdapters ?? []))
241
+ context.report({
242
+ node: property,
243
+ messageId: "missingInstrumentation",
244
+ data: { operation: name },
245
+ });
246
+ },
247
+ });
231
248
  });
232
- });
249
+ };
250
+ check(argument.value, owner.name, fields);
233
251
  },
234
252
  });
235
253
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@commonplaces/oxlint-plugin",
3
- "version": "0.8.0",
3
+ "version": "0.9.1",
4
4
  "description": "Shared Oxlint rules for strict Effect TypeScript, Alchemy and repository code.",
5
5
  "license": "MIT",
6
6
  "author": "Cooper Corbett",
@@ -72,10 +72,10 @@
72
72
  "prepack": "bun run build"
73
73
  },
74
74
  "dependencies": {
75
- "@effect/tsgo": "0.55.0",
76
- "@oxlint/plugins": "1.89.0",
75
+ "@effect/tsgo": "0.63.0",
76
+ "@oxlint/plugins": "1.90.0",
77
77
  "effect": "4.0.5",
78
- "oxlint-tsgolint": "7.0.2004",
78
+ "oxlint-tsgolint": "7.0.2005",
79
79
  "ultracite": "8.0.1"
80
80
  },
81
81
  "devDependencies": {
@@ -86,7 +86,7 @@
86
86
  "vitest": "5.0.5"
87
87
  },
88
88
  "peerDependencies": {
89
- "oxlint": "1.87.0 || 1.88.0 || 1.89.0"
89
+ "oxlint": "1.88.0 || 1.89.0 || 1.90.0"
90
90
  },
91
91
  "engines": {
92
92
  "node": ">=22.14.0"