Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
10 changes: 10 additions & 0 deletions .oxlintrc.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"automation/no-silent-error-swallow": "error",
"automation/no-manual-tag-comparison": "error",
"automation/no-manual-tagged-construction": "error",
"automation/no-service-option": "error",
"automation/prefer-effect-match": "error",
"automation/prefer-tagged-error-handling": "error",
"automation/no-ambient-nondeterminism": "error",
Expand Down Expand Up @@ -78,6 +79,7 @@
"automation/no-silent-error-swallow": "off",
"automation/no-manual-tag-comparison": "off",
"automation/no-manual-tagged-construction": "off",
"automation/no-service-option": "off",
"automation/prefer-effect-match": "off",
"automation/prefer-tagged-error-handling": "off",
"automation/no-ambient-nondeterminism": "off",
Expand All @@ -99,6 +101,14 @@
"automation/no-manual-tagged-construction": "off",
},
},
{
// Tests may inspect whether optional provider override services are present in context. This verifies
// request-scoping behavior; it does not model an optional production dependency.
"files": ["packages/*/test/**"],
"rules": {
"automation/no-service-option": "off",
},
},
{
// no-ambient-nondeterminism targets production seams. Tests deliberately use fixed or real
// time and node crypto, driven through explicit layers, so the rule is off for test files.
Expand Down
2 changes: 2 additions & 0 deletions tools/oxlint/automation/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import noAmbientNondeterminism from './rules/no-ambient-nondeterminism.ts'
import noDisableValidation from './rules/no-disable-validation.ts'
import noManualTagComparison from './rules/no-manual-tag-comparison.ts'
import noManualTaggedConstruction from './rules/no-manual-tagged-construction.ts'
import noServiceOption from './rules/no-service-option.ts'
import noShadowedStandardArrayStatic from './rules/no-shadowed-standard-array-static.ts'
import noSilentErrorSwallow from './rules/no-silent-error-swallow.ts'
import preferEffectMatch from './rules/prefer-effect-match.ts'
Expand All @@ -16,6 +17,7 @@ export default eslintCompatPlugin({
'no-disable-validation': noDisableValidation,
'no-manual-tag-comparison': noManualTagComparison,
'no-manual-tagged-construction': noManualTaggedConstruction,
'no-service-option': noServiceOption,
'no-shadowed-standard-array-static': noShadowedStandardArrayStatic,
'no-silent-error-swallow': noSilentErrorSwallow,
'prefer-effect-match': preferEffectMatch,
Expand Down
32 changes: 32 additions & 0 deletions tools/oxlint/automation/rules/no-service-option.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { defineRule } from '@oxlint/plugins'

// Vendored and adapted from typeonce-dev/ai-automation (rules/oxlint/src/rules/no-service-option.ts).
// This rule protects production dependency boundaries. Tests that explicitly inspect whether a service is
// present are excluded through `.oxlintrc.jsonc` rather than weakening the production rule.

export default defineRule({
meta: {
type: 'problem',
docs: {
description: 'Require Effect services directly and provide them at the owning layer or composition root.',
},
messages: {
optionalService:
'Do not use Effect.serviceOption for production dependencies. Require the service directly and provide it in the layer.',
},
},
createOnce(context) {
return {
MemberExpression(node) {
if (
node.object.type === 'Identifier' &&
node.object.name === 'Effect' &&
node.property.type === 'Identifier' &&
node.property.name === 'serviceOption'
) {
context.report({ node, messageId: 'optionalService' })
}
},
}
},
})
Loading