diff --git a/.changeset/tidy-layers-provide.md b/.changeset/tidy-layers-provide.md new file mode 100644 index 00000000..3974d328 --- /dev/null +++ b/.changeset/tidy-layers-provide.md @@ -0,0 +1,5 @@ +--- +"@effect/language-service": minor +--- + +add `redundantLayerMergeAllInProvide` to replace direct `Layer.mergeAll` arguments to `Effect.provide` with layer arrays. diff --git a/README.md b/README.md index 119e65a6..ab04a6f9 100644 --- a/README.md +++ b/README.md @@ -126,6 +126,7 @@ Some diagnostics are off by default or have a default severity of suggestion, bu missedPipeableOpportunity➖🔧Enforces the use of pipeable style for nested function calls✓✓ missingEffectServiceDependency➖Checks that Effect.Service dependencies satisfy all required layer inputs✓ nestedEffectGenYield➖Warns when yielding a nested bare Effect.gen inside an existing Effect generator context✓✓ + redundantLayerMergeAllInProvide💡🔧Replaces direct Layer.mergeAll arguments to Effect.provide with an array of layers✓✓ redundantSchemaTagIdentifier💡🔧Suggests removing redundant identifier argument when it equals the tag value in Schema.TaggedClass/TaggedError/TaggedRequest✓✓ schemaStructWithTag💡🔧Suggests using Schema.TaggedStruct instead of Schema.Struct with _tag field✓✓ schemaUnionOfLiterals➖🔧Simplifies Schema.Union of multiple Schema.Literal calls into single Schema.Literal✓ diff --git a/packages/harness-effect-v3/__snapshots__/completions.test.ts.snap b/packages/harness-effect-v3/__snapshots__/completions.test.ts.snap index 661bced5..275a3fb2 100644 --- a/packages/harness-effect-v3/__snapshots__/completions.test.ts.snap +++ b/packages/harness-effect-v3/__snapshots__/completions.test.ts.snap @@ -248,7 +248,7 @@ exports[`Completion effectDataClasses > effectDataClasses_directImportTaggedErro exports[`Completion effectDiagnosticsComment > effectDiagnosticsComment.ts at 2:5 1`] = ` [ { - "insertText": "@effect-diagnostics \${1|anyUnknownInErrorContext,asyncFunction,catchAllToMapError,catchUnfailableEffect,classSelfMismatch,cryptoRandomUUID,cryptoRandomUUIDInEffect,deterministicKeys,duplicatePackage,effectDoNotation,effectFnIife,effectFnImplicitAny,effectFnOpportunity,effectGenUsesAdapter,effectInFailure,effectInVoidSuccess,effectMapFlatten,effectMapVoid,effectSucceedWithVoid,extendsNativeError,flatMapToMap,floatingEffect,genericEffectServices,globalConsole,globalConsoleInEffect,globalDate,globalDateInEffect,globalErrorInEffectCatch,globalErrorInEffectFailure,globalFetch,globalFetchInEffect,globalRandom,globalRandomInEffect,globalTimers,globalTimersInEffect,importFromBarrel,instanceOfSchema,layerMergeAllWithDependencies,lazyPromiseInEffectSync,leakingRequirements,missedPipeableOpportunity,missingEffectContext,missingEffectError,missingEffectServiceDependency,missingLayerContext,missingReturnYieldStar,missingStarInYieldEffectGen,multipleEffectProvide,nestedEffectGenYield,newPromise,nodeBuiltinImport,nonObjectEffectServiceType,outdatedApi,outdatedEffectCodegen,overriddenSchemaConstructor,preferSchemaOverJson,processEnv,processEnvInEffect,redundantSchemaTagIdentifier,returnEffectInGen,runEffectInsideEffect,schemaStructWithTag,schemaSyncInEffect,schemaUnionOfLiterals,scopeInLayerEffect,serviceNotAsClass,strictBooleanExpressions,strictEffectProvide,tryCatchInEffectGen,unknownInEffectCatch,unnecessaryArrowBlock,unnecessaryEffectGen,unnecessaryFailYieldableError,unnecessaryPipe,unnecessaryPipeChain,unsafeEffectTypeAssertion,unsupportedServiceAccessors|}:\${2|off,warning,error,message,suggestion|}$0", + "insertText": "@effect-diagnostics \${1|anyUnknownInErrorContext,asyncFunction,catchAllToMapError,catchUnfailableEffect,classSelfMismatch,cryptoRandomUUID,cryptoRandomUUIDInEffect,deterministicKeys,duplicatePackage,effectDoNotation,effectFnIife,effectFnImplicitAny,effectFnOpportunity,effectGenUsesAdapter,effectInFailure,effectInVoidSuccess,effectMapFlatten,effectMapVoid,effectSucceedWithVoid,extendsNativeError,flatMapToMap,floatingEffect,genericEffectServices,globalConsole,globalConsoleInEffect,globalDate,globalDateInEffect,globalErrorInEffectCatch,globalErrorInEffectFailure,globalFetch,globalFetchInEffect,globalRandom,globalRandomInEffect,globalTimers,globalTimersInEffect,importFromBarrel,instanceOfSchema,layerMergeAllWithDependencies,lazyPromiseInEffectSync,leakingRequirements,missedPipeableOpportunity,missingEffectContext,missingEffectError,missingEffectServiceDependency,missingLayerContext,missingReturnYieldStar,missingStarInYieldEffectGen,multipleEffectProvide,nestedEffectGenYield,newPromise,nodeBuiltinImport,nonObjectEffectServiceType,outdatedApi,outdatedEffectCodegen,overriddenSchemaConstructor,preferSchemaOverJson,processEnv,processEnvInEffect,redundantLayerMergeAllInProvide,redundantSchemaTagIdentifier,returnEffectInGen,runEffectInsideEffect,schemaStructWithTag,schemaSyncInEffect,schemaUnionOfLiterals,scopeInLayerEffect,serviceNotAsClass,strictBooleanExpressions,strictEffectProvide,tryCatchInEffectGen,unknownInEffectCatch,unnecessaryArrowBlock,unnecessaryEffectGen,unnecessaryFailYieldableError,unnecessaryPipe,unnecessaryPipeChain,unsafeEffectTypeAssertion,unsupportedServiceAccessors|}:\${2|off,warning,error,message,suggestion|}$0", "isSnippet": true, "kind": "string", "name": "@effect-diagnostics", @@ -259,7 +259,7 @@ exports[`Completion effectDiagnosticsComment > effectDiagnosticsComment.ts at 2: "sortText": "11", }, { - "insertText": "@effect-diagnostics-next-line \${1|anyUnknownInErrorContext,asyncFunction,catchAllToMapError,catchUnfailableEffect,classSelfMismatch,cryptoRandomUUID,cryptoRandomUUIDInEffect,deterministicKeys,duplicatePackage,effectDoNotation,effectFnIife,effectFnImplicitAny,effectFnOpportunity,effectGenUsesAdapter,effectInFailure,effectInVoidSuccess,effectMapFlatten,effectMapVoid,effectSucceedWithVoid,extendsNativeError,flatMapToMap,floatingEffect,genericEffectServices,globalConsole,globalConsoleInEffect,globalDate,globalDateInEffect,globalErrorInEffectCatch,globalErrorInEffectFailure,globalFetch,globalFetchInEffect,globalRandom,globalRandomInEffect,globalTimers,globalTimersInEffect,importFromBarrel,instanceOfSchema,layerMergeAllWithDependencies,lazyPromiseInEffectSync,leakingRequirements,missedPipeableOpportunity,missingEffectContext,missingEffectError,missingEffectServiceDependency,missingLayerContext,missingReturnYieldStar,missingStarInYieldEffectGen,multipleEffectProvide,nestedEffectGenYield,newPromise,nodeBuiltinImport,nonObjectEffectServiceType,outdatedApi,outdatedEffectCodegen,overriddenSchemaConstructor,preferSchemaOverJson,processEnv,processEnvInEffect,redundantSchemaTagIdentifier,returnEffectInGen,runEffectInsideEffect,schemaStructWithTag,schemaSyncInEffect,schemaUnionOfLiterals,scopeInLayerEffect,serviceNotAsClass,strictBooleanExpressions,strictEffectProvide,tryCatchInEffectGen,unknownInEffectCatch,unnecessaryArrowBlock,unnecessaryEffectGen,unnecessaryFailYieldableError,unnecessaryPipe,unnecessaryPipeChain,unsafeEffectTypeAssertion,unsupportedServiceAccessors|}:\${2|off,warning,error,message,suggestion|}$0", + "insertText": "@effect-diagnostics-next-line \${1|anyUnknownInErrorContext,asyncFunction,catchAllToMapError,catchUnfailableEffect,classSelfMismatch,cryptoRandomUUID,cryptoRandomUUIDInEffect,deterministicKeys,duplicatePackage,effectDoNotation,effectFnIife,effectFnImplicitAny,effectFnOpportunity,effectGenUsesAdapter,effectInFailure,effectInVoidSuccess,effectMapFlatten,effectMapVoid,effectSucceedWithVoid,extendsNativeError,flatMapToMap,floatingEffect,genericEffectServices,globalConsole,globalConsoleInEffect,globalDate,globalDateInEffect,globalErrorInEffectCatch,globalErrorInEffectFailure,globalFetch,globalFetchInEffect,globalRandom,globalRandomInEffect,globalTimers,globalTimersInEffect,importFromBarrel,instanceOfSchema,layerMergeAllWithDependencies,lazyPromiseInEffectSync,leakingRequirements,missedPipeableOpportunity,missingEffectContext,missingEffectError,missingEffectServiceDependency,missingLayerContext,missingReturnYieldStar,missingStarInYieldEffectGen,multipleEffectProvide,nestedEffectGenYield,newPromise,nodeBuiltinImport,nonObjectEffectServiceType,outdatedApi,outdatedEffectCodegen,overriddenSchemaConstructor,preferSchemaOverJson,processEnv,processEnvInEffect,redundantLayerMergeAllInProvide,redundantSchemaTagIdentifier,returnEffectInGen,runEffectInsideEffect,schemaStructWithTag,schemaSyncInEffect,schemaUnionOfLiterals,scopeInLayerEffect,serviceNotAsClass,strictBooleanExpressions,strictEffectProvide,tryCatchInEffectGen,unknownInEffectCatch,unnecessaryArrowBlock,unnecessaryEffectGen,unnecessaryFailYieldableError,unnecessaryPipe,unnecessaryPipeChain,unsafeEffectTypeAssertion,unsupportedServiceAccessors|}:\${2|off,warning,error,message,suggestion|}$0", "isSnippet": true, "kind": "string", "name": "@effect-diagnostics-next-line", diff --git a/packages/harness-effect-v3/__snapshots__/diagnostics/multipleEffectProvide.ts.codefixes b/packages/harness-effect-v3/__snapshots__/diagnostics/multipleEffectProvide.ts.codefixes index f1590cb2..aaba74bd 100644 --- a/packages/harness-effect-v3/__snapshots__/diagnostics/multipleEffectProvide.ts.codefixes +++ b/packages/harness-effect-v3/__snapshots__/diagnostics/multipleEffectProvide.ts.codefixes @@ -1,12 +1,18 @@ -multipleEffectProvide_fix from 430 to 464 -multipleEffectProvide_skipNextLine from 430 to 464 -multipleEffectProvide_skipFile from 430 to 464 -multipleEffectProvide_fix from 564 to 598 -multipleEffectProvide_skipNextLine from 564 to 598 -multipleEffectProvide_skipFile from 564 to 598 -multipleEffectProvide_fix from 663 to 697 -multipleEffectProvide_skipNextLine from 663 to 697 -multipleEffectProvide_skipFile from 663 to 697 -multipleEffectProvide_fix from 793 to 827 -multipleEffectProvide_skipNextLine from 793 to 827 -multipleEffectProvide_skipFile from 793 to 827 \ No newline at end of file +multipleEffectProvide_fix from 553 to 587 +multipleEffectProvide_skipNextLine from 553 to 587 +multipleEffectProvide_skipFile from 553 to 587 +multipleEffectProvide_fix from 687 to 721 +multipleEffectProvide_skipNextLine from 687 to 721 +multipleEffectProvide_skipFile from 687 to 721 +multipleEffectProvide_fix from 786 to 820 +multipleEffectProvide_skipNextLine from 786 to 820 +multipleEffectProvide_skipFile from 786 to 820 +multipleEffectProvide_fix from 916 to 950 +multipleEffectProvide_skipNextLine from 916 to 950 +multipleEffectProvide_skipFile from 916 to 950 +multipleEffectProvide_fix from 1095 to 1151 +multipleEffectProvide_skipNextLine from 1095 to 1151 +multipleEffectProvide_skipFile from 1095 to 1151 +multipleEffectProvide_fix from 1380 to 1406 +multipleEffectProvide_skipNextLine from 1380 to 1406 +multipleEffectProvide_skipFile from 1380 to 1406 \ No newline at end of file diff --git a/packages/harness-effect-v3/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from1095to1151.output b/packages/harness-effect-v3/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from1095to1151.output new file mode 100644 index 00000000..12ae9545 --- /dev/null +++ b/packages/harness-effect-v3/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from1095to1151.output @@ -0,0 +1,56 @@ +// code fix multipleEffectProvide_fix output for range 1095 - 1151 +import * as Effect from "effect/Effect" +import * as Layer from "effect/Layer" + +class MyService1 extends Effect.Service()("MyService1", { + succeed: { value: 1 } +}) {} + +class MyService2 extends Effect.Service()("MyService2", { + succeed: { value: 2 } +}) {} + +class MyService3 extends Effect.Service()("MyService3", { + succeed: { value: 3 } +}) {} + +const layerTuple: [typeof MyService1.Default, typeof MyService2.Default] = [ + MyService1.Default, + MyService2.Default +] + +export const shouldReport = Effect.void.pipe( + Effect.provide(MyService1.Default), + Effect.provide(MyService2.Default) +) + +export const shouldReportSeparately = Effect.void.pipe( + Effect.provide(MyService1.Default), + Effect.provide(MyService2.Default), + Effect.ignoreLogged, + Effect.provide(MyService1.Default), + Effect.provide(MyService2.Default) +) + +export const shouldReportSingle = Effect.void.pipe( + Effect.provide(MyService1.Default), + Effect.provide(MyService2.Default), + Effect.provide(MyService3.Default) +) + +export const shouldReportAfterArrayRewrite = Effect.void.pipe( + Effect.provide(Layer.mergeAll(MyService1.Default, MyService2.Default, MyService3.Default)) +) + +export const shouldNotReportSingleArray = Effect.void.pipe( + Effect.provide([MyService1.Default, MyService2.Default]) +) + +export const shouldReportAfterTupleRewrite = Effect.void.pipe( + Effect.provide(layerTuple), + Effect.provide(MyService3.Default) +) + +export const shouldNotReportSingleTuple = Effect.void.pipe( + Effect.provide(layerTuple) +) diff --git a/packages/harness-effect-v3/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from1380to1406.output b/packages/harness-effect-v3/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from1380to1406.output new file mode 100644 index 00000000..4b7ac4bd --- /dev/null +++ b/packages/harness-effect-v3/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from1380to1406.output @@ -0,0 +1,56 @@ +// code fix multipleEffectProvide_fix output for range 1380 - 1406 +import * as Effect from "effect/Effect" +import * as Layer from "effect/Layer" + +class MyService1 extends Effect.Service()("MyService1", { + succeed: { value: 1 } +}) {} + +class MyService2 extends Effect.Service()("MyService2", { + succeed: { value: 2 } +}) {} + +class MyService3 extends Effect.Service()("MyService3", { + succeed: { value: 3 } +}) {} + +const layerTuple: [typeof MyService1.Default, typeof MyService2.Default] = [ + MyService1.Default, + MyService2.Default +] + +export const shouldReport = Effect.void.pipe( + Effect.provide(MyService1.Default), + Effect.provide(MyService2.Default) +) + +export const shouldReportSeparately = Effect.void.pipe( + Effect.provide(MyService1.Default), + Effect.provide(MyService2.Default), + Effect.ignoreLogged, + Effect.provide(MyService1.Default), + Effect.provide(MyService2.Default) +) + +export const shouldReportSingle = Effect.void.pipe( + Effect.provide(MyService1.Default), + Effect.provide(MyService2.Default), + Effect.provide(MyService3.Default) +) + +export const shouldReportAfterArrayRewrite = Effect.void.pipe( + Effect.provide([MyService1.Default, MyService2.Default]), + Effect.provide(MyService3.Default) +) + +export const shouldNotReportSingleArray = Effect.void.pipe( + Effect.provide([MyService1.Default, MyService2.Default]) +) + +export const shouldReportAfterTupleRewrite = Effect.void.pipe( + Effect.provide(Layer.mergeAll(...layerTuple, MyService3.Default)) +) + +export const shouldNotReportSingleTuple = Effect.void.pipe( + Effect.provide(layerTuple) +) diff --git a/packages/harness-effect-v3/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from430to464.output b/packages/harness-effect-v3/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from430to464.output index e213e916..af0f31b5 100644 --- a/packages/harness-effect-v3/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from430to464.output +++ b/packages/harness-effect-v3/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from430to464.output @@ -31,3 +31,12 @@ export const shouldReportSingle = Effect.void.pipe( Effect.provide(MyService2.Default), Effect.provide(MyService3.Default) ) + +export const shouldReportAfterArrayRewrite = Effect.void.pipe( + Effect.provide([MyService1.Default, MyService2.Default]), + Effect.provide(MyService3.Default) +) + +export const shouldNotReportSingleArray = Effect.void.pipe( + Effect.provide([MyService1.Default, MyService2.Default]) +) diff --git a/packages/harness-effect-v3/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from553to587.output b/packages/harness-effect-v3/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from553to587.output new file mode 100644 index 00000000..d16d4c70 --- /dev/null +++ b/packages/harness-effect-v3/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from553to587.output @@ -0,0 +1,56 @@ +// code fix multipleEffectProvide_fix output for range 553 - 587 +import * as Effect from "effect/Effect" +import * as Layer from "effect/Layer" + +class MyService1 extends Effect.Service()("MyService1", { + succeed: { value: 1 } +}) {} + +class MyService2 extends Effect.Service()("MyService2", { + succeed: { value: 2 } +}) {} + +class MyService3 extends Effect.Service()("MyService3", { + succeed: { value: 3 } +}) {} + +const layerTuple: [typeof MyService1.Default, typeof MyService2.Default] = [ + MyService1.Default, + MyService2.Default +] + +export const shouldReport = Effect.void.pipe( + Effect.provide(Layer.mergeAll(MyService1.Default, MyService2.Default)) +) + +export const shouldReportSeparately = Effect.void.pipe( + Effect.provide(MyService1.Default), + Effect.provide(MyService2.Default), + Effect.ignoreLogged, + Effect.provide(MyService1.Default), + Effect.provide(MyService2.Default) +) + +export const shouldReportSingle = Effect.void.pipe( + Effect.provide(MyService1.Default), + Effect.provide(MyService2.Default), + Effect.provide(MyService3.Default) +) + +export const shouldReportAfterArrayRewrite = Effect.void.pipe( + Effect.provide([MyService1.Default, MyService2.Default]), + Effect.provide(MyService3.Default) +) + +export const shouldNotReportSingleArray = Effect.void.pipe( + Effect.provide([MyService1.Default, MyService2.Default]) +) + +export const shouldReportAfterTupleRewrite = Effect.void.pipe( + Effect.provide(layerTuple), + Effect.provide(MyService3.Default) +) + +export const shouldNotReportSingleTuple = Effect.void.pipe( + Effect.provide(layerTuple) +) diff --git a/packages/harness-effect-v3/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from564to598.output b/packages/harness-effect-v3/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from564to598.output index ca40b355..48dc9f0f 100644 --- a/packages/harness-effect-v3/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from564to598.output +++ b/packages/harness-effect-v3/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from564to598.output @@ -31,3 +31,12 @@ export const shouldReportSingle = Effect.void.pipe( Effect.provide(MyService2.Default), Effect.provide(MyService3.Default) ) + +export const shouldReportAfterArrayRewrite = Effect.void.pipe( + Effect.provide([MyService1.Default, MyService2.Default]), + Effect.provide(MyService3.Default) +) + +export const shouldNotReportSingleArray = Effect.void.pipe( + Effect.provide([MyService1.Default, MyService2.Default]) +) diff --git a/packages/harness-effect-v3/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from663to697.output b/packages/harness-effect-v3/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from663to697.output index 2d0b3497..068128cc 100644 --- a/packages/harness-effect-v3/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from663to697.output +++ b/packages/harness-effect-v3/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from663to697.output @@ -31,3 +31,12 @@ export const shouldReportSingle = Effect.void.pipe( Effect.provide(MyService2.Default), Effect.provide(MyService3.Default) ) + +export const shouldReportAfterArrayRewrite = Effect.void.pipe( + Effect.provide([MyService1.Default, MyService2.Default]), + Effect.provide(MyService3.Default) +) + +export const shouldNotReportSingleArray = Effect.void.pipe( + Effect.provide([MyService1.Default, MyService2.Default]) +) diff --git a/packages/harness-effect-v3/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from687to721.output b/packages/harness-effect-v3/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from687to721.output new file mode 100644 index 00000000..67aeb4b4 --- /dev/null +++ b/packages/harness-effect-v3/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from687to721.output @@ -0,0 +1,56 @@ +// code fix multipleEffectProvide_fix output for range 687 - 721 +import * as Effect from "effect/Effect" +import * as Layer from "effect/Layer" + +class MyService1 extends Effect.Service()("MyService1", { + succeed: { value: 1 } +}) {} + +class MyService2 extends Effect.Service()("MyService2", { + succeed: { value: 2 } +}) {} + +class MyService3 extends Effect.Service()("MyService3", { + succeed: { value: 3 } +}) {} + +const layerTuple: [typeof MyService1.Default, typeof MyService2.Default] = [ + MyService1.Default, + MyService2.Default +] + +export const shouldReport = Effect.void.pipe( + Effect.provide(MyService1.Default), + Effect.provide(MyService2.Default) +) + +export const shouldReportSeparately = Effect.void.pipe( + Effect.provide(Layer.mergeAll(MyService1.Default, MyService2.Default)), + Effect.ignoreLogged, + Effect.provide(MyService1.Default), + Effect.provide(MyService2.Default) +) + +export const shouldReportSingle = Effect.void.pipe( + Effect.provide(MyService1.Default), + Effect.provide(MyService2.Default), + Effect.provide(MyService3.Default) +) + +export const shouldReportAfterArrayRewrite = Effect.void.pipe( + Effect.provide([MyService1.Default, MyService2.Default]), + Effect.provide(MyService3.Default) +) + +export const shouldNotReportSingleArray = Effect.void.pipe( + Effect.provide([MyService1.Default, MyService2.Default]) +) + +export const shouldReportAfterTupleRewrite = Effect.void.pipe( + Effect.provide(layerTuple), + Effect.provide(MyService3.Default) +) + +export const shouldNotReportSingleTuple = Effect.void.pipe( + Effect.provide(layerTuple) +) diff --git a/packages/harness-effect-v3/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from786to820.output b/packages/harness-effect-v3/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from786to820.output new file mode 100644 index 00000000..3632e268 --- /dev/null +++ b/packages/harness-effect-v3/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from786to820.output @@ -0,0 +1,56 @@ +// code fix multipleEffectProvide_fix output for range 786 - 820 +import * as Effect from "effect/Effect" +import * as Layer from "effect/Layer" + +class MyService1 extends Effect.Service()("MyService1", { + succeed: { value: 1 } +}) {} + +class MyService2 extends Effect.Service()("MyService2", { + succeed: { value: 2 } +}) {} + +class MyService3 extends Effect.Service()("MyService3", { + succeed: { value: 3 } +}) {} + +const layerTuple: [typeof MyService1.Default, typeof MyService2.Default] = [ + MyService1.Default, + MyService2.Default +] + +export const shouldReport = Effect.void.pipe( + Effect.provide(MyService1.Default), + Effect.provide(MyService2.Default) +) + +export const shouldReportSeparately = Effect.void.pipe( + Effect.provide(MyService1.Default), + Effect.provide(MyService2.Default), + Effect.ignoreLogged, + Effect.provide(Layer.mergeAll(MyService1.Default, MyService2.Default)) +) + +export const shouldReportSingle = Effect.void.pipe( + Effect.provide(MyService1.Default), + Effect.provide(MyService2.Default), + Effect.provide(MyService3.Default) +) + +export const shouldReportAfterArrayRewrite = Effect.void.pipe( + Effect.provide([MyService1.Default, MyService2.Default]), + Effect.provide(MyService3.Default) +) + +export const shouldNotReportSingleArray = Effect.void.pipe( + Effect.provide([MyService1.Default, MyService2.Default]) +) + +export const shouldReportAfterTupleRewrite = Effect.void.pipe( + Effect.provide(layerTuple), + Effect.provide(MyService3.Default) +) + +export const shouldNotReportSingleTuple = Effect.void.pipe( + Effect.provide(layerTuple) +) diff --git a/packages/harness-effect-v3/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from793to827.output b/packages/harness-effect-v3/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from793to827.output index a054ba3d..6545230b 100644 --- a/packages/harness-effect-v3/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from793to827.output +++ b/packages/harness-effect-v3/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from793to827.output @@ -30,3 +30,12 @@ export const shouldReportSeparately = Effect.void.pipe( export const shouldReportSingle = Effect.void.pipe( Effect.provide(Layer.mergeAll(MyService1.Default, MyService2.Default, MyService3.Default)) ) + +export const shouldReportAfterArrayRewrite = Effect.void.pipe( + Effect.provide([MyService1.Default, MyService2.Default]), + Effect.provide(MyService3.Default) +) + +export const shouldNotReportSingleArray = Effect.void.pipe( + Effect.provide([MyService1.Default, MyService2.Default]) +) diff --git a/packages/harness-effect-v3/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from916to950.output b/packages/harness-effect-v3/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from916to950.output new file mode 100644 index 00000000..c3c94520 --- /dev/null +++ b/packages/harness-effect-v3/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from916to950.output @@ -0,0 +1,55 @@ +// code fix multipleEffectProvide_fix output for range 916 - 950 +import * as Effect from "effect/Effect" +import * as Layer from "effect/Layer" + +class MyService1 extends Effect.Service()("MyService1", { + succeed: { value: 1 } +}) {} + +class MyService2 extends Effect.Service()("MyService2", { + succeed: { value: 2 } +}) {} + +class MyService3 extends Effect.Service()("MyService3", { + succeed: { value: 3 } +}) {} + +const layerTuple: [typeof MyService1.Default, typeof MyService2.Default] = [ + MyService1.Default, + MyService2.Default +] + +export const shouldReport = Effect.void.pipe( + Effect.provide(MyService1.Default), + Effect.provide(MyService2.Default) +) + +export const shouldReportSeparately = Effect.void.pipe( + Effect.provide(MyService1.Default), + Effect.provide(MyService2.Default), + Effect.ignoreLogged, + Effect.provide(MyService1.Default), + Effect.provide(MyService2.Default) +) + +export const shouldReportSingle = Effect.void.pipe( + Effect.provide(Layer.mergeAll(MyService1.Default, MyService2.Default, MyService3.Default)) +) + +export const shouldReportAfterArrayRewrite = Effect.void.pipe( + Effect.provide([MyService1.Default, MyService2.Default]), + Effect.provide(MyService3.Default) +) + +export const shouldNotReportSingleArray = Effect.void.pipe( + Effect.provide([MyService1.Default, MyService2.Default]) +) + +export const shouldReportAfterTupleRewrite = Effect.void.pipe( + Effect.provide(layerTuple), + Effect.provide(MyService3.Default) +) + +export const shouldNotReportSingleTuple = Effect.void.pipe( + Effect.provide(layerTuple) +) diff --git a/packages/harness-effect-v3/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from972to1028.output b/packages/harness-effect-v3/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from972to1028.output new file mode 100644 index 00000000..f94af31f --- /dev/null +++ b/packages/harness-effect-v3/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from972to1028.output @@ -0,0 +1,42 @@ +// code fix multipleEffectProvide_fix output for range 972 - 1028 +import * as Effect from "effect/Effect" +import * as Layer from "effect/Layer" + +class MyService1 extends Effect.Service()("MyService1", { + succeed: { value: 1 } +}) {} + +class MyService2 extends Effect.Service()("MyService2", { + succeed: { value: 2 } +}) {} + +class MyService3 extends Effect.Service()("MyService3", { + succeed: { value: 3 } +}) {} + +export const shouldReport = Effect.void.pipe( + Effect.provide(MyService1.Default), + Effect.provide(MyService2.Default) +) + +export const shouldReportSeparately = Effect.void.pipe( + Effect.provide(MyService1.Default), + Effect.provide(MyService2.Default), + Effect.ignoreLogged, + Effect.provide(MyService1.Default), + Effect.provide(MyService2.Default) +) + +export const shouldReportSingle = Effect.void.pipe( + Effect.provide(MyService1.Default), + Effect.provide(MyService2.Default), + Effect.provide(MyService3.Default) +) + +export const shouldReportAfterArrayRewrite = Effect.void.pipe( + Effect.provide(Layer.mergeAll(MyService1.Default, MyService2.Default, MyService3.Default)) +) + +export const shouldNotReportSingleArray = Effect.void.pipe( + Effect.provide([MyService1.Default, MyService2.Default]) +) diff --git a/packages/harness-effect-v3/__snapshots__/diagnostics/multipleEffectProvide.ts.output b/packages/harness-effect-v3/__snapshots__/diagnostics/multipleEffectProvide.ts.output index 6d8a28b4..1efc1c07 100644 --- a/packages/harness-effect-v3/__snapshots__/diagnostics/multipleEffectProvide.ts.output +++ b/packages/harness-effect-v3/__snapshots__/diagnostics/multipleEffectProvide.ts.output @@ -1,11 +1,17 @@ Effect.provide(MyService1.Default) -17:2 - 17:36 | 0 | This expression chains multiple `Effect.provide` calls. Providing Layers in multiple calls in a chain can break service lifecycle behavior compared with a single combined provide with merged layers. effect(multipleEffectProvide) +22:2 - 22:36 | 0 | This expression chains multiple `Effect.provide` calls. Providing Layers in multiple calls in a chain can break service lifecycle behavior compared with a single combined provide with merged layers. effect(multipleEffectProvide) Effect.provide(MyService1.Default) -22:2 - 22:36 | 0 | This expression chains multiple `Effect.provide` calls. Providing Layers in multiple calls in a chain can break service lifecycle behavior compared with a single combined provide with merged layers. effect(multipleEffectProvide) +27:2 - 27:36 | 0 | This expression chains multiple `Effect.provide` calls. Providing Layers in multiple calls in a chain can break service lifecycle behavior compared with a single combined provide with merged layers. effect(multipleEffectProvide) Effect.provide(MyService1.Default) -25:2 - 25:36 | 0 | This expression chains multiple `Effect.provide` calls. Providing Layers in multiple calls in a chain can break service lifecycle behavior compared with a single combined provide with merged layers. effect(multipleEffectProvide) +30:2 - 30:36 | 0 | This expression chains multiple `Effect.provide` calls. Providing Layers in multiple calls in a chain can break service lifecycle behavior compared with a single combined provide with merged layers. effect(multipleEffectProvide) Effect.provide(MyService1.Default) -30:2 - 30:36 | 0 | This expression chains multiple `Effect.provide` calls. Providing Layers in multiple calls in a chain can break service lifecycle behavior compared with a single combined provide with merged layers. effect(multipleEffectProvide) \ No newline at end of file +35:2 - 35:36 | 0 | This expression chains multiple `Effect.provide` calls. Providing Layers in multiple calls in a chain can break service lifecycle behavior compared with a single combined provide with merged layers. effect(multipleEffectProvide) + +Effect.provide([MyService1.Default, MyService2.Default]) +41:2 - 41:58 | 0 | This expression chains multiple `Effect.provide` calls. Providing Layers in multiple calls in a chain can break service lifecycle behavior compared with a single combined provide with merged layers. effect(multipleEffectProvide) + +Effect.provide(layerTuple) +50:2 - 50:28 | 0 | This expression chains multiple `Effect.provide` calls. Providing Layers in multiple calls in a chain can break service lifecycle behavior compared with a single combined provide with merged layers. effect(multipleEffectProvide) \ No newline at end of file diff --git a/packages/harness-effect-v3/__snapshots__/diagnostics/redundantLayerMergeAllInProvide.ts.codefixes b/packages/harness-effect-v3/__snapshots__/diagnostics/redundantLayerMergeAllInProvide.ts.codefixes new file mode 100644 index 00000000..ba943841 --- /dev/null +++ b/packages/harness-effect-v3/__snapshots__/diagnostics/redundantLayerMergeAllInProvide.ts.codefixes @@ -0,0 +1,12 @@ +redundantLayerMergeAllInProvide_fix from 335 to 349 +redundantLayerMergeAllInProvide_skipNextLine from 335 to 349 +redundantLayerMergeAllInProvide_skipFile from 335 to 349 +redundantLayerMergeAllInProvide_fix from 231 to 245 +redundantLayerMergeAllInProvide_skipNextLine from 231 to 245 +redundantLayerMergeAllInProvide_skipFile from 231 to 245 +redundantLayerMergeAllInProvide_fix from 437 to 451 +redundantLayerMergeAllInProvide_skipNextLine from 437 to 451 +redundantLayerMergeAllInProvide_skipFile from 437 to 451 +redundantLayerMergeAllInProvide_fix from 550 to 564 +redundantLayerMergeAllInProvide_skipNextLine from 550 to 564 +redundantLayerMergeAllInProvide_skipFile from 550 to 564 \ No newline at end of file diff --git a/packages/harness-effect-v3/__snapshots__/diagnostics/redundantLayerMergeAllInProvide.ts.output b/packages/harness-effect-v3/__snapshots__/diagnostics/redundantLayerMergeAllInProvide.ts.output new file mode 100644 index 00000000..92e2c26d --- /dev/null +++ b/packages/harness-effect-v3/__snapshots__/diagnostics/redundantLayerMergeAllInProvide.ts.output @@ -0,0 +1,11 @@ +Layer.mergeAll +9:17 - 9:31 | 2 | `Effect.provide` accepts an array of Layers directly, so this `Layer.mergeAll` call is redundant. effect(redundantLayerMergeAllInProvide) + +Layer.mergeAll +14:2 - 14:16 | 2 | `Effect.provide` accepts an array of Layers directly, so this `Layer.mergeAll` call is redundant. effect(redundantLayerMergeAllInProvide) + +Layer.mergeAll +18:17 - 18:31 | 2 | `Effect.provide` accepts an array of Layers directly, so this `Layer.mergeAll` call is redundant. effect(redundantLayerMergeAllInProvide) + +Layer.mergeAll +22:17 - 22:31 | 2 | `Effect.provide` accepts an array of Layers directly, so this `Layer.mergeAll` call is redundant. effect(redundantLayerMergeAllInProvide) \ No newline at end of file diff --git a/packages/harness-effect-v3/__snapshots__/diagnostics/redundantLayerMergeAllInProvide.ts.redundantLayerMergeAllInProvide_fix.from231to245.output b/packages/harness-effect-v3/__snapshots__/diagnostics/redundantLayerMergeAllInProvide.ts.redundantLayerMergeAllInProvide_fix.from231to245.output new file mode 100644 index 00000000..2f26aeb3 --- /dev/null +++ b/packages/harness-effect-v3/__snapshots__/diagnostics/redundantLayerMergeAllInProvide.ts.redundantLayerMergeAllInProvide_fix.from231to245.output @@ -0,0 +1,47 @@ +// code fix redundantLayerMergeAllInProvide_fix output for range 231 - 245 +import { Effect, Layer } from "effect" + +const first = Layer.empty +const second = Layer.empty +const third = Layer.empty +const layers = [first, second] as const + +export const shouldReportPipeable = Effect.void.pipe( + Effect.provide([first, second]) +) + +export const shouldReportDataFirst = Effect.provide( + Effect.void, + Layer.mergeAll(first, second) +) + +export const shouldReportSpread = Effect.void.pipe( + Effect.provide(Layer.mergeAll(...[first, second])) +) + +export const shouldReportTupleSpread = Effect.void.pipe( + Effect.provide(Layer.mergeAll(...layers)) +) + +export const shouldNotReportTransformed = Effect.void.pipe( + Effect.provide( + Layer.mergeAll(first, second).pipe(Layer.provide(third)) + ) +) + +const combined = Layer.mergeAll(first, second) + +export const shouldNotReportPrecomposed = Effect.void.pipe( + Effect.provide(combined) +) + +class Dependency extends Effect.Service()("Dependency", { + succeed: {} +}) {} +class Consumer extends Effect.Service()("Consumer", { + effect: Effect.as(Dependency, {}) +}) {} + +export const shouldNotReportWithDependencies = Effect.void.pipe( + Effect.provide(Layer.mergeAll(Dependency.Default, Consumer.Default)) +) diff --git a/packages/harness-effect-v3/__snapshots__/diagnostics/redundantLayerMergeAllInProvide.ts.redundantLayerMergeAllInProvide_fix.from335to349.output b/packages/harness-effect-v3/__snapshots__/diagnostics/redundantLayerMergeAllInProvide.ts.redundantLayerMergeAllInProvide_fix.from335to349.output new file mode 100644 index 00000000..0fe6ed5a --- /dev/null +++ b/packages/harness-effect-v3/__snapshots__/diagnostics/redundantLayerMergeAllInProvide.ts.redundantLayerMergeAllInProvide_fix.from335to349.output @@ -0,0 +1,47 @@ +// code fix redundantLayerMergeAllInProvide_fix output for range 335 - 349 +import { Effect, Layer } from "effect" + +const first = Layer.empty +const second = Layer.empty +const third = Layer.empty +const layers = [first, second] as const + +export const shouldReportPipeable = Effect.void.pipe( + Effect.provide(Layer.mergeAll(first, second)) +) + +export const shouldReportDataFirst = Effect.provide( + Effect.void, + [first, second] +) + +export const shouldReportSpread = Effect.void.pipe( + Effect.provide(Layer.mergeAll(...[first, second])) +) + +export const shouldReportTupleSpread = Effect.void.pipe( + Effect.provide(Layer.mergeAll(...layers)) +) + +export const shouldNotReportTransformed = Effect.void.pipe( + Effect.provide( + Layer.mergeAll(first, second).pipe(Layer.provide(third)) + ) +) + +const combined = Layer.mergeAll(first, second) + +export const shouldNotReportPrecomposed = Effect.void.pipe( + Effect.provide(combined) +) + +class Dependency extends Effect.Service()("Dependency", { + succeed: {} +}) {} +class Consumer extends Effect.Service()("Consumer", { + effect: Effect.as(Dependency, {}) +}) {} + +export const shouldNotReportWithDependencies = Effect.void.pipe( + Effect.provide(Layer.mergeAll(Dependency.Default, Consumer.Default)) +) diff --git a/packages/harness-effect-v3/__snapshots__/diagnostics/redundantLayerMergeAllInProvide.ts.redundantLayerMergeAllInProvide_fix.from437to451.output b/packages/harness-effect-v3/__snapshots__/diagnostics/redundantLayerMergeAllInProvide.ts.redundantLayerMergeAllInProvide_fix.from437to451.output new file mode 100644 index 00000000..d148bbeb --- /dev/null +++ b/packages/harness-effect-v3/__snapshots__/diagnostics/redundantLayerMergeAllInProvide.ts.redundantLayerMergeAllInProvide_fix.from437to451.output @@ -0,0 +1,47 @@ +// code fix redundantLayerMergeAllInProvide_fix output for range 437 - 451 +import { Effect, Layer } from "effect" + +const first = Layer.empty +const second = Layer.empty +const third = Layer.empty +const layers = [first, second] as const + +export const shouldReportPipeable = Effect.void.pipe( + Effect.provide(Layer.mergeAll(first, second)) +) + +export const shouldReportDataFirst = Effect.provide( + Effect.void, + Layer.mergeAll(first, second) +) + +export const shouldReportSpread = Effect.void.pipe( + Effect.provide([first, second]) +) + +export const shouldReportTupleSpread = Effect.void.pipe( + Effect.provide(Layer.mergeAll(...layers)) +) + +export const shouldNotReportTransformed = Effect.void.pipe( + Effect.provide( + Layer.mergeAll(first, second).pipe(Layer.provide(third)) + ) +) + +const combined = Layer.mergeAll(first, second) + +export const shouldNotReportPrecomposed = Effect.void.pipe( + Effect.provide(combined) +) + +class Dependency extends Effect.Service()("Dependency", { + succeed: {} +}) {} +class Consumer extends Effect.Service()("Consumer", { + effect: Effect.as(Dependency, {}) +}) {} + +export const shouldNotReportWithDependencies = Effect.void.pipe( + Effect.provide(Layer.mergeAll(Dependency.Default, Consumer.Default)) +) diff --git a/packages/harness-effect-v3/__snapshots__/diagnostics/redundantLayerMergeAllInProvide.ts.redundantLayerMergeAllInProvide_fix.from550to564.output b/packages/harness-effect-v3/__snapshots__/diagnostics/redundantLayerMergeAllInProvide.ts.redundantLayerMergeAllInProvide_fix.from550to564.output new file mode 100644 index 00000000..ff16080c --- /dev/null +++ b/packages/harness-effect-v3/__snapshots__/diagnostics/redundantLayerMergeAllInProvide.ts.redundantLayerMergeAllInProvide_fix.from550to564.output @@ -0,0 +1,47 @@ +// code fix redundantLayerMergeAllInProvide_fix output for range 550 - 564 +import { Effect, Layer } from "effect" + +const first = Layer.empty +const second = Layer.empty +const third = Layer.empty +const layers = [first, second] as const + +export const shouldReportPipeable = Effect.void.pipe( + Effect.provide(Layer.mergeAll(first, second)) +) + +export const shouldReportDataFirst = Effect.provide( + Effect.void, + Layer.mergeAll(first, second) +) + +export const shouldReportSpread = Effect.void.pipe( + Effect.provide(Layer.mergeAll(...[first, second])) +) + +export const shouldReportTupleSpread = Effect.void.pipe( + Effect.provide([layers[0], layers[1]]) +) + +export const shouldNotReportTransformed = Effect.void.pipe( + Effect.provide( + Layer.mergeAll(first, second).pipe(Layer.provide(third)) + ) +) + +const combined = Layer.mergeAll(first, second) + +export const shouldNotReportPrecomposed = Effect.void.pipe( + Effect.provide(combined) +) + +class Dependency extends Effect.Service()("Dependency", { + succeed: {} +}) {} +class Consumer extends Effect.Service()("Consumer", { + effect: Effect.as(Dependency, {}) +}) {} + +export const shouldNotReportWithDependencies = Effect.void.pipe( + Effect.provide(Layer.mergeAll(Dependency.Default, Consumer.Default)) +) diff --git a/packages/harness-effect-v3/__snapshots__/diagnostics/strictEffectProvide.ts.codefixes b/packages/harness-effect-v3/__snapshots__/diagnostics/strictEffectProvide.ts.codefixes index 598a2a25..e6f9971f 100644 --- a/packages/harness-effect-v3/__snapshots__/diagnostics/strictEffectProvide.ts.codefixes +++ b/packages/harness-effect-v3/__snapshots__/diagnostics/strictEffectProvide.ts.codefixes @@ -1,8 +1,10 @@ -strictEffectProvide_skipNextLine from 569 to 616 -strictEffectProvide_skipFile from 569 to 616 -strictEffectProvide_skipNextLine from 427 to 461 -strictEffectProvide_skipFile from 427 to 461 -strictEffectProvide_skipNextLine from 735 to 769 -strictEffectProvide_skipFile from 735 to 769 -strictEffectProvide_skipNextLine from 893 to 963 -strictEffectProvide_skipFile from 893 to 963 \ No newline at end of file +strictEffectProvide_skipNextLine from 692 to 739 +strictEffectProvide_skipFile from 692 to 739 +strictEffectProvide_skipNextLine from 550 to 584 +strictEffectProvide_skipFile from 550 to 584 +strictEffectProvide_skipNextLine from 858 to 892 +strictEffectProvide_skipFile from 858 to 892 +strictEffectProvide_skipNextLine from 1016 to 1086 +strictEffectProvide_skipFile from 1016 to 1086 +strictEffectProvide_skipNextLine from 1223 to 1249 +strictEffectProvide_skipFile from 1223 to 1249 \ No newline at end of file diff --git a/packages/harness-effect-v3/__snapshots__/diagnostics/strictEffectProvide.ts.output b/packages/harness-effect-v3/__snapshots__/diagnostics/strictEffectProvide.ts.output index b596f039..030bd078 100644 --- a/packages/harness-effect-v3/__snapshots__/diagnostics/strictEffectProvide.ts.output +++ b/packages/harness-effect-v3/__snapshots__/diagnostics/strictEffectProvide.ts.output @@ -1,11 +1,14 @@ Effect.provide(MyService1.Default) -15:2 - 15:36 | 0 | Effect.provide with a Layer should only be used at application entry points. If this is an entry point, you can safely disable this diagnostic. Otherwise, using Effect.provide may break scope lifetimes. Compose all layers at your entry point and provide them at once. effect(strictEffectProvide) +20:2 - 20:36 | 0 | Effect.provide with a Layer should only be used at application entry points. If this is an entry point, you can safely disable this diagnostic. Otherwise, using Effect.provide may break scope lifetimes. Compose all layers at your entry point and provide them at once. effect(strictEffectProvide) Effect.provide(Effect.void, MyService1.Default) -19:29 - 19:76 | 0 | Effect.provide with a Layer should only be used at application entry points. If this is an entry point, you can safely disable this diagnostic. Otherwise, using Effect.provide may break scope lifetimes. Compose all layers at your entry point and provide them at once. effect(strictEffectProvide) +24:29 - 24:76 | 0 | Effect.provide with a Layer should only be used at application entry points. If this is an entry point, you can safely disable this diagnostic. Otherwise, using Effect.provide may break scope lifetimes. Compose all layers at your entry point and provide them at once. effect(strictEffectProvide) Effect.provide(MyService1.Default) -24:2 - 24:36 | 0 | Effect.provide with a Layer should only be used at application entry points. If this is an entry point, you can safely disable this diagnostic. Otherwise, using Effect.provide may break scope lifetimes. Compose all layers at your entry point and provide them at once. effect(strictEffectProvide) +29:2 - 29:36 | 0 | Effect.provide with a Layer should only be used at application entry points. If this is an entry point, you can safely disable this diagnostic. Otherwise, using Effect.provide may break scope lifetimes. Compose all layers at your entry point and provide them at once. effect(strictEffectProvide) Effect.provide(Layer.mergeAll(MyService1.Default, MyService2.Default)) -30:2 - 30:72 | 0 | Effect.provide with a Layer should only be used at application entry points. If this is an entry point, you can safely disable this diagnostic. Otherwise, using Effect.provide may break scope lifetimes. Compose all layers at your entry point and provide them at once. effect(strictEffectProvide) \ No newline at end of file +35:2 - 35:72 | 0 | Effect.provide with a Layer should only be used at application entry points. If this is an entry point, you can safely disable this diagnostic. Otherwise, using Effect.provide may break scope lifetimes. Compose all layers at your entry point and provide them at once. effect(strictEffectProvide) + +Effect.provide(layerTuple) +40:2 - 40:28 | 0 | Effect.provide with a Layer should only be used at application entry points. If this is an entry point, you can safely disable this diagnostic. Otherwise, using Effect.provide may break scope lifetimes. Compose all layers at your entry point and provide them at once. effect(strictEffectProvide) \ No newline at end of file diff --git a/packages/harness-effect-v3/examples/diagnostics/multipleEffectProvide.ts b/packages/harness-effect-v3/examples/diagnostics/multipleEffectProvide.ts index e2d10fbf..e1432094 100644 --- a/packages/harness-effect-v3/examples/diagnostics/multipleEffectProvide.ts +++ b/packages/harness-effect-v3/examples/diagnostics/multipleEffectProvide.ts @@ -13,6 +13,11 @@ class MyService3 extends Effect.Service()("MyService3", { succeed: { value: 3 } }) {} +const layerTuple: [typeof MyService1.Default, typeof MyService2.Default] = [ + MyService1.Default, + MyService2.Default +] + export const shouldReport = Effect.void.pipe( Effect.provide(MyService1.Default), Effect.provide(MyService2.Default) @@ -31,3 +36,21 @@ export const shouldReportSingle = Effect.void.pipe( Effect.provide(MyService2.Default), Effect.provide(MyService3.Default) ) + +export const shouldReportAfterArrayRewrite = Effect.void.pipe( + Effect.provide([MyService1.Default, MyService2.Default]), + Effect.provide(MyService3.Default) +) + +export const shouldNotReportSingleArray = Effect.void.pipe( + Effect.provide([MyService1.Default, MyService2.Default]) +) + +export const shouldReportAfterTupleRewrite = Effect.void.pipe( + Effect.provide(layerTuple), + Effect.provide(MyService3.Default) +) + +export const shouldNotReportSingleTuple = Effect.void.pipe( + Effect.provide(layerTuple) +) diff --git a/packages/harness-effect-v3/examples/diagnostics/redundantLayerMergeAllInProvide.ts b/packages/harness-effect-v3/examples/diagnostics/redundantLayerMergeAllInProvide.ts new file mode 100644 index 00000000..db0a2e08 --- /dev/null +++ b/packages/harness-effect-v3/examples/diagnostics/redundantLayerMergeAllInProvide.ts @@ -0,0 +1,46 @@ +import { Effect, Layer } from "effect" + +const first = Layer.empty +const second = Layer.empty +const third = Layer.empty +const layers = [first, second] as const + +export const shouldReportPipeable = Effect.void.pipe( + Effect.provide(Layer.mergeAll(first, second)) +) + +export const shouldReportDataFirst = Effect.provide( + Effect.void, + Layer.mergeAll(first, second) +) + +export const shouldReportSpread = Effect.void.pipe( + Effect.provide(Layer.mergeAll(...[first, second])) +) + +export const shouldReportTupleSpread = Effect.void.pipe( + Effect.provide(Layer.mergeAll(...layers)) +) + +export const shouldNotReportTransformed = Effect.void.pipe( + Effect.provide( + Layer.mergeAll(first, second).pipe(Layer.provide(third)) + ) +) + +const combined = Layer.mergeAll(first, second) + +export const shouldNotReportPrecomposed = Effect.void.pipe( + Effect.provide(combined) +) + +class Dependency extends Effect.Service()("Dependency", { + succeed: {} +}) {} +class Consumer extends Effect.Service()("Consumer", { + effect: Effect.as(Dependency, {}) +}) {} + +export const shouldNotReportWithDependencies = Effect.void.pipe( + Effect.provide(Layer.mergeAll(Dependency.Default, Consumer.Default)) +) diff --git a/packages/harness-effect-v3/examples/diagnostics/strictEffectProvide.ts b/packages/harness-effect-v3/examples/diagnostics/strictEffectProvide.ts index a087c127..997b509d 100644 --- a/packages/harness-effect-v3/examples/diagnostics/strictEffectProvide.ts +++ b/packages/harness-effect-v3/examples/diagnostics/strictEffectProvide.ts @@ -10,6 +10,11 @@ class MyService2 extends Effect.Service()("MyService2", { succeed: { value: 2 } }) {} +const layerTuple: [typeof MyService1.Default, typeof MyService2.Default] = [ + MyService1.Default, + MyService2.Default +] + // Should report: Effect.provide with a Layer export const shouldReport1 = Effect.void.pipe( Effect.provide(MyService1.Default) @@ -30,6 +35,11 @@ export const shouldReport4 = Effect.void.pipe( Effect.provide(Layer.mergeAll(MyService1.Default, MyService2.Default)) ) +// Should report: non-empty tuples are Layer collections accepted by Effect.provide +export const shouldReport5 = Effect.void.pipe( + Effect.provide(layerTuple) +) + // Should NOT report: providing a plain service (not a layer) export const shouldNotReport1 = Effect.void.pipe( Effect.provideService(MyService1, new MyService1({ value: 1 })) diff --git a/packages/harness-effect-v4/__snapshots__/completions.test.ts.snap b/packages/harness-effect-v4/__snapshots__/completions.test.ts.snap index 6d9977c2..487094b3 100644 --- a/packages/harness-effect-v4/__snapshots__/completions.test.ts.snap +++ b/packages/harness-effect-v4/__snapshots__/completions.test.ts.snap @@ -109,7 +109,7 @@ exports[`Completion effectDataClasses > effectDataClasses.ts at 4:35 1`] = ` exports[`Completion effectDiagnosticsComment > effectDiagnosticsComment.ts at 2:5 1`] = ` [ { - "insertText": "@effect-diagnostics \${1|anyUnknownInErrorContext,asyncFunction,catchAllToMapError,catchUnfailableEffect,classSelfMismatch,cryptoRandomUUID,cryptoRandomUUIDInEffect,deterministicKeys,duplicatePackage,effectDoNotation,effectFnIife,effectFnImplicitAny,effectFnOpportunity,effectGenUsesAdapter,effectInFailure,effectInVoidSuccess,effectMapFlatten,effectMapVoid,effectSucceedWithVoid,extendsNativeError,flatMapToMap,floatingEffect,genericEffectServices,globalConsole,globalConsoleInEffect,globalDate,globalDateInEffect,globalErrorInEffectCatch,globalErrorInEffectFailure,globalFetch,globalFetchInEffect,globalRandom,globalRandomInEffect,globalTimers,globalTimersInEffect,importFromBarrel,instanceOfSchema,layerMergeAllWithDependencies,lazyPromiseInEffectSync,leakingRequirements,missedPipeableOpportunity,missingEffectContext,missingEffectError,missingEffectServiceDependency,missingLayerContext,missingReturnYieldStar,missingStarInYieldEffectGen,multipleEffectProvide,nestedEffectGenYield,newPromise,nodeBuiltinImport,nonObjectEffectServiceType,outdatedApi,outdatedEffectCodegen,overriddenSchemaConstructor,preferSchemaOverJson,processEnv,processEnvInEffect,redundantSchemaTagIdentifier,returnEffectInGen,runEffectInsideEffect,schemaStructWithTag,schemaSyncInEffect,schemaUnionOfLiterals,scopeInLayerEffect,serviceNotAsClass,strictBooleanExpressions,strictEffectProvide,tryCatchInEffectGen,unknownInEffectCatch,unnecessaryArrowBlock,unnecessaryEffectGen,unnecessaryFailYieldableError,unnecessaryPipe,unnecessaryPipeChain,unsafeEffectTypeAssertion,unsupportedServiceAccessors|}:\${2|off,warning,error,message,suggestion|}$0", + "insertText": "@effect-diagnostics \${1|anyUnknownInErrorContext,asyncFunction,catchAllToMapError,catchUnfailableEffect,classSelfMismatch,cryptoRandomUUID,cryptoRandomUUIDInEffect,deterministicKeys,duplicatePackage,effectDoNotation,effectFnIife,effectFnImplicitAny,effectFnOpportunity,effectGenUsesAdapter,effectInFailure,effectInVoidSuccess,effectMapFlatten,effectMapVoid,effectSucceedWithVoid,extendsNativeError,flatMapToMap,floatingEffect,genericEffectServices,globalConsole,globalConsoleInEffect,globalDate,globalDateInEffect,globalErrorInEffectCatch,globalErrorInEffectFailure,globalFetch,globalFetchInEffect,globalRandom,globalRandomInEffect,globalTimers,globalTimersInEffect,importFromBarrel,instanceOfSchema,layerMergeAllWithDependencies,lazyPromiseInEffectSync,leakingRequirements,missedPipeableOpportunity,missingEffectContext,missingEffectError,missingEffectServiceDependency,missingLayerContext,missingReturnYieldStar,missingStarInYieldEffectGen,multipleEffectProvide,nestedEffectGenYield,newPromise,nodeBuiltinImport,nonObjectEffectServiceType,outdatedApi,outdatedEffectCodegen,overriddenSchemaConstructor,preferSchemaOverJson,processEnv,processEnvInEffect,redundantLayerMergeAllInProvide,redundantSchemaTagIdentifier,returnEffectInGen,runEffectInsideEffect,schemaStructWithTag,schemaSyncInEffect,schemaUnionOfLiterals,scopeInLayerEffect,serviceNotAsClass,strictBooleanExpressions,strictEffectProvide,tryCatchInEffectGen,unknownInEffectCatch,unnecessaryArrowBlock,unnecessaryEffectGen,unnecessaryFailYieldableError,unnecessaryPipe,unnecessaryPipeChain,unsafeEffectTypeAssertion,unsupportedServiceAccessors|}:\${2|off,warning,error,message,suggestion|}$0", "isSnippet": true, "kind": "string", "name": "@effect-diagnostics", @@ -120,7 +120,7 @@ exports[`Completion effectDiagnosticsComment > effectDiagnosticsComment.ts at 2: "sortText": "11", }, { - "insertText": "@effect-diagnostics-next-line \${1|anyUnknownInErrorContext,asyncFunction,catchAllToMapError,catchUnfailableEffect,classSelfMismatch,cryptoRandomUUID,cryptoRandomUUIDInEffect,deterministicKeys,duplicatePackage,effectDoNotation,effectFnIife,effectFnImplicitAny,effectFnOpportunity,effectGenUsesAdapter,effectInFailure,effectInVoidSuccess,effectMapFlatten,effectMapVoid,effectSucceedWithVoid,extendsNativeError,flatMapToMap,floatingEffect,genericEffectServices,globalConsole,globalConsoleInEffect,globalDate,globalDateInEffect,globalErrorInEffectCatch,globalErrorInEffectFailure,globalFetch,globalFetchInEffect,globalRandom,globalRandomInEffect,globalTimers,globalTimersInEffect,importFromBarrel,instanceOfSchema,layerMergeAllWithDependencies,lazyPromiseInEffectSync,leakingRequirements,missedPipeableOpportunity,missingEffectContext,missingEffectError,missingEffectServiceDependency,missingLayerContext,missingReturnYieldStar,missingStarInYieldEffectGen,multipleEffectProvide,nestedEffectGenYield,newPromise,nodeBuiltinImport,nonObjectEffectServiceType,outdatedApi,outdatedEffectCodegen,overriddenSchemaConstructor,preferSchemaOverJson,processEnv,processEnvInEffect,redundantSchemaTagIdentifier,returnEffectInGen,runEffectInsideEffect,schemaStructWithTag,schemaSyncInEffect,schemaUnionOfLiterals,scopeInLayerEffect,serviceNotAsClass,strictBooleanExpressions,strictEffectProvide,tryCatchInEffectGen,unknownInEffectCatch,unnecessaryArrowBlock,unnecessaryEffectGen,unnecessaryFailYieldableError,unnecessaryPipe,unnecessaryPipeChain,unsafeEffectTypeAssertion,unsupportedServiceAccessors|}:\${2|off,warning,error,message,suggestion|}$0", + "insertText": "@effect-diagnostics-next-line \${1|anyUnknownInErrorContext,asyncFunction,catchAllToMapError,catchUnfailableEffect,classSelfMismatch,cryptoRandomUUID,cryptoRandomUUIDInEffect,deterministicKeys,duplicatePackage,effectDoNotation,effectFnIife,effectFnImplicitAny,effectFnOpportunity,effectGenUsesAdapter,effectInFailure,effectInVoidSuccess,effectMapFlatten,effectMapVoid,effectSucceedWithVoid,extendsNativeError,flatMapToMap,floatingEffect,genericEffectServices,globalConsole,globalConsoleInEffect,globalDate,globalDateInEffect,globalErrorInEffectCatch,globalErrorInEffectFailure,globalFetch,globalFetchInEffect,globalRandom,globalRandomInEffect,globalTimers,globalTimersInEffect,importFromBarrel,instanceOfSchema,layerMergeAllWithDependencies,lazyPromiseInEffectSync,leakingRequirements,missedPipeableOpportunity,missingEffectContext,missingEffectError,missingEffectServiceDependency,missingLayerContext,missingReturnYieldStar,missingStarInYieldEffectGen,multipleEffectProvide,nestedEffectGenYield,newPromise,nodeBuiltinImport,nonObjectEffectServiceType,outdatedApi,outdatedEffectCodegen,overriddenSchemaConstructor,preferSchemaOverJson,processEnv,processEnvInEffect,redundantLayerMergeAllInProvide,redundantSchemaTagIdentifier,returnEffectInGen,runEffectInsideEffect,schemaStructWithTag,schemaSyncInEffect,schemaUnionOfLiterals,scopeInLayerEffect,serviceNotAsClass,strictBooleanExpressions,strictEffectProvide,tryCatchInEffectGen,unknownInEffectCatch,unnecessaryArrowBlock,unnecessaryEffectGen,unnecessaryFailYieldableError,unnecessaryPipe,unnecessaryPipeChain,unsafeEffectTypeAssertion,unsupportedServiceAccessors|}:\${2|off,warning,error,message,suggestion|}$0", "isSnippet": true, "kind": "string", "name": "@effect-diagnostics-next-line", diff --git a/packages/harness-effect-v4/__snapshots__/diagnostics/multipleEffectProvide.ts.codefixes b/packages/harness-effect-v4/__snapshots__/diagnostics/multipleEffectProvide.ts.codefixes index 2cb4e6bb..45f9cbc0 100644 --- a/packages/harness-effect-v4/__snapshots__/diagnostics/multipleEffectProvide.ts.codefixes +++ b/packages/harness-effect-v4/__snapshots__/diagnostics/multipleEffectProvide.ts.codefixes @@ -1,15 +1,21 @@ -multipleEffectProvide_fix from 592 to 626 -multipleEffectProvide_skipNextLine from 592 to 626 -multipleEffectProvide_skipFile from 592 to 626 -multipleEffectProvide_fix from 726 to 760 -multipleEffectProvide_skipNextLine from 726 to 760 -multipleEffectProvide_skipFile from 726 to 760 -multipleEffectProvide_fix from 819 to 853 -multipleEffectProvide_skipNextLine from 819 to 853 -multipleEffectProvide_skipFile from 819 to 853 -multipleEffectProvide_fix from 949 to 983 -multipleEffectProvide_skipNextLine from 949 to 983 -multipleEffectProvide_skipFile from 949 to 983 -multipleEffectProvide_fix from 1339 to 1373 -multipleEffectProvide_skipNextLine from 1339 to 1373 -multipleEffectProvide_skipFile from 1339 to 1373 \ No newline at end of file +multipleEffectProvide_fix from 715 to 749 +multipleEffectProvide_skipNextLine from 715 to 749 +multipleEffectProvide_skipFile from 715 to 749 +multipleEffectProvide_fix from 849 to 883 +multipleEffectProvide_skipNextLine from 849 to 883 +multipleEffectProvide_skipFile from 849 to 883 +multipleEffectProvide_fix from 942 to 976 +multipleEffectProvide_skipNextLine from 942 to 976 +multipleEffectProvide_skipFile from 942 to 976 +multipleEffectProvide_fix from 1072 to 1106 +multipleEffectProvide_skipNextLine from 1072 to 1106 +multipleEffectProvide_skipFile from 1072 to 1106 +multipleEffectProvide_fix from 1462 to 1496 +multipleEffectProvide_skipNextLine from 1462 to 1496 +multipleEffectProvide_skipFile from 1462 to 1496 +multipleEffectProvide_fix from 1603 to 1659 +multipleEffectProvide_skipNextLine from 1603 to 1659 +multipleEffectProvide_skipFile from 1603 to 1659 +multipleEffectProvide_fix from 1888 to 1914 +multipleEffectProvide_skipNextLine from 1888 to 1914 +multipleEffectProvide_skipFile from 1888 to 1914 \ No newline at end of file diff --git a/packages/harness-effect-v4/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from1072to1106.output b/packages/harness-effect-v4/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from1072to1106.output new file mode 100644 index 00000000..da3db514 --- /dev/null +++ b/packages/harness-effect-v4/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from1072to1106.output @@ -0,0 +1,71 @@ +// code fix multipleEffectProvide_fix output for range 1072 - 1106 +import { Context, Effect, Layer } from "effect" + +class MyService1 extends Context.Service()("MyService1", { + make: Effect.succeed({ value: 1 }) +}) { + static Default = Layer.effect(this, this.make) +} + +class MyService2 extends Context.Service()("MyService2", { + make: Effect.succeed({ value: 2 }) +}) { + static Default = Layer.effect(this, this.make) +} + +class MyService3 extends Context.Service()("MyService3", { + make: Effect.succeed({ value: 3 }) +}) { + static Default = Layer.effect(this, this.make) +} + +const layerTuple: [typeof MyService1.Default, typeof MyService2.Default] = [ + MyService1.Default, + MyService2.Default +] + +export const shouldReport = Effect.void.pipe( + Effect.provide(MyService1.Default), + Effect.provide(MyService2.Default) +) + +export const shouldReportSeparately = Effect.void.pipe( + Effect.provide(MyService1.Default), + Effect.provide(MyService2.Default), + Effect.ignore, + Effect.provide(MyService1.Default), + Effect.provide(MyService2.Default) +) + +export const shouldReportSingle = Effect.void.pipe( + Effect.provide(Layer.mergeAll(MyService1.Default, MyService2.Default, MyService3.Default)) +) + +export const shouldNotReportLocalProvide = Effect.void.pipe( + Effect.provide(MyService1.Default), + Effect.provide(MyService2.Default, { local: true }) +) + +export const shouldReportAfterLocalProvide = Effect.void.pipe( + Effect.provide(MyService1.Default, { local: true }), + Effect.provide(MyService2.Default), + Effect.provide(MyService3.Default) +) + +export const shouldReportAfterArrayRewrite = Effect.void.pipe( + Effect.provide([MyService1.Default, MyService2.Default]), + Effect.provide(MyService3.Default) +) + +export const shouldNotReportSingleArray = Effect.void.pipe( + Effect.provide([MyService1.Default, MyService2.Default]) +) + +export const shouldReportAfterTupleRewrite = Effect.void.pipe( + Effect.provide(layerTuple), + Effect.provide(MyService3.Default) +) + +export const shouldNotReportSingleTuple = Effect.void.pipe( + Effect.provide(layerTuple) +) diff --git a/packages/harness-effect-v4/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from1339to1373.output b/packages/harness-effect-v4/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from1339to1373.output index 0459591a..8e045a2a 100644 --- a/packages/harness-effect-v4/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from1339to1373.output +++ b/packages/harness-effect-v4/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from1339to1373.output @@ -47,3 +47,12 @@ export const shouldReportAfterLocalProvide = Effect.void.pipe( Effect.provide(MyService1.Default, { local: true }), Effect.provide(Layer.mergeAll(MyService2.Default, MyService3.Default)) ) + +export const shouldReportAfterArrayRewrite = Effect.void.pipe( + Effect.provide([MyService1.Default, MyService2.Default]), + Effect.provide(MyService3.Default) +) + +export const shouldNotReportSingleArray = Effect.void.pipe( + Effect.provide([MyService1.Default, MyService2.Default]) +) diff --git a/packages/harness-effect-v4/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from1462to1496.output b/packages/harness-effect-v4/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from1462to1496.output new file mode 100644 index 00000000..9bd5fb72 --- /dev/null +++ b/packages/harness-effect-v4/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from1462to1496.output @@ -0,0 +1,72 @@ +// code fix multipleEffectProvide_fix output for range 1462 - 1496 +import { Context, Effect, Layer } from "effect" + +class MyService1 extends Context.Service()("MyService1", { + make: Effect.succeed({ value: 1 }) +}) { + static Default = Layer.effect(this, this.make) +} + +class MyService2 extends Context.Service()("MyService2", { + make: Effect.succeed({ value: 2 }) +}) { + static Default = Layer.effect(this, this.make) +} + +class MyService3 extends Context.Service()("MyService3", { + make: Effect.succeed({ value: 3 }) +}) { + static Default = Layer.effect(this, this.make) +} + +const layerTuple: [typeof MyService1.Default, typeof MyService2.Default] = [ + MyService1.Default, + MyService2.Default +] + +export const shouldReport = Effect.void.pipe( + Effect.provide(MyService1.Default), + Effect.provide(MyService2.Default) +) + +export const shouldReportSeparately = Effect.void.pipe( + Effect.provide(MyService1.Default), + Effect.provide(MyService2.Default), + Effect.ignore, + Effect.provide(MyService1.Default), + Effect.provide(MyService2.Default) +) + +export const shouldReportSingle = Effect.void.pipe( + Effect.provide(MyService1.Default), + Effect.provide(MyService2.Default), + Effect.provide(MyService3.Default) +) + +export const shouldNotReportLocalProvide = Effect.void.pipe( + Effect.provide(MyService1.Default), + Effect.provide(MyService2.Default, { local: true }) +) + +export const shouldReportAfterLocalProvide = Effect.void.pipe( + Effect.provide(MyService1.Default, { local: true }), + Effect.provide(Layer.mergeAll(MyService2.Default, MyService3.Default)) +) + +export const shouldReportAfterArrayRewrite = Effect.void.pipe( + Effect.provide([MyService1.Default, MyService2.Default]), + Effect.provide(MyService3.Default) +) + +export const shouldNotReportSingleArray = Effect.void.pipe( + Effect.provide([MyService1.Default, MyService2.Default]) +) + +export const shouldReportAfterTupleRewrite = Effect.void.pipe( + Effect.provide(layerTuple), + Effect.provide(MyService3.Default) +) + +export const shouldNotReportSingleTuple = Effect.void.pipe( + Effect.provide(layerTuple) +) diff --git a/packages/harness-effect-v4/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from1480to1536.output b/packages/harness-effect-v4/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from1480to1536.output new file mode 100644 index 00000000..ae46b590 --- /dev/null +++ b/packages/harness-effect-v4/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from1480to1536.output @@ -0,0 +1,58 @@ +// code fix multipleEffectProvide_fix output for range 1480 - 1536 +import { Context, Effect, Layer } from "effect" + +class MyService1 extends Context.Service()("MyService1", { + make: Effect.succeed({ value: 1 }) +}) { + static Default = Layer.effect(this, this.make) +} + +class MyService2 extends Context.Service()("MyService2", { + make: Effect.succeed({ value: 2 }) +}) { + static Default = Layer.effect(this, this.make) +} + +class MyService3 extends Context.Service()("MyService3", { + make: Effect.succeed({ value: 3 }) +}) { + static Default = Layer.effect(this, this.make) +} + +export const shouldReport = Effect.void.pipe( + Effect.provide(MyService1.Default), + Effect.provide(MyService2.Default) +) + +export const shouldReportSeparately = Effect.void.pipe( + Effect.provide(MyService1.Default), + Effect.provide(MyService2.Default), + Effect.ignore, + Effect.provide(MyService1.Default), + Effect.provide(MyService2.Default) +) + +export const shouldReportSingle = Effect.void.pipe( + Effect.provide(MyService1.Default), + Effect.provide(MyService2.Default), + Effect.provide(MyService3.Default) +) + +export const shouldNotReportLocalProvide = Effect.void.pipe( + Effect.provide(MyService1.Default), + Effect.provide(MyService2.Default, { local: true }) +) + +export const shouldReportAfterLocalProvide = Effect.void.pipe( + Effect.provide(MyService1.Default, { local: true }), + Effect.provide(MyService2.Default), + Effect.provide(MyService3.Default) +) + +export const shouldReportAfterArrayRewrite = Effect.void.pipe( + Effect.provide(Layer.mergeAll(MyService1.Default, MyService2.Default, MyService3.Default)) +) + +export const shouldNotReportSingleArray = Effect.void.pipe( + Effect.provide([MyService1.Default, MyService2.Default]) +) diff --git a/packages/harness-effect-v4/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from1603to1659.output b/packages/harness-effect-v4/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from1603to1659.output new file mode 100644 index 00000000..73f51aa5 --- /dev/null +++ b/packages/harness-effect-v4/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from1603to1659.output @@ -0,0 +1,72 @@ +// code fix multipleEffectProvide_fix output for range 1603 - 1659 +import { Context, Effect, Layer } from "effect" + +class MyService1 extends Context.Service()("MyService1", { + make: Effect.succeed({ value: 1 }) +}) { + static Default = Layer.effect(this, this.make) +} + +class MyService2 extends Context.Service()("MyService2", { + make: Effect.succeed({ value: 2 }) +}) { + static Default = Layer.effect(this, this.make) +} + +class MyService3 extends Context.Service()("MyService3", { + make: Effect.succeed({ value: 3 }) +}) { + static Default = Layer.effect(this, this.make) +} + +const layerTuple: [typeof MyService1.Default, typeof MyService2.Default] = [ + MyService1.Default, + MyService2.Default +] + +export const shouldReport = Effect.void.pipe( + Effect.provide(MyService1.Default), + Effect.provide(MyService2.Default) +) + +export const shouldReportSeparately = Effect.void.pipe( + Effect.provide(MyService1.Default), + Effect.provide(MyService2.Default), + Effect.ignore, + Effect.provide(MyService1.Default), + Effect.provide(MyService2.Default) +) + +export const shouldReportSingle = Effect.void.pipe( + Effect.provide(MyService1.Default), + Effect.provide(MyService2.Default), + Effect.provide(MyService3.Default) +) + +export const shouldNotReportLocalProvide = Effect.void.pipe( + Effect.provide(MyService1.Default), + Effect.provide(MyService2.Default, { local: true }) +) + +export const shouldReportAfterLocalProvide = Effect.void.pipe( + Effect.provide(MyService1.Default, { local: true }), + Effect.provide(MyService2.Default), + Effect.provide(MyService3.Default) +) + +export const shouldReportAfterArrayRewrite = Effect.void.pipe( + Effect.provide(Layer.mergeAll(MyService1.Default, MyService2.Default, MyService3.Default)) +) + +export const shouldNotReportSingleArray = Effect.void.pipe( + Effect.provide([MyService1.Default, MyService2.Default]) +) + +export const shouldReportAfterTupleRewrite = Effect.void.pipe( + Effect.provide(layerTuple), + Effect.provide(MyService3.Default) +) + +export const shouldNotReportSingleTuple = Effect.void.pipe( + Effect.provide(layerTuple) +) diff --git a/packages/harness-effect-v4/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from1888to1914.output b/packages/harness-effect-v4/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from1888to1914.output new file mode 100644 index 00000000..bd78b24b --- /dev/null +++ b/packages/harness-effect-v4/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from1888to1914.output @@ -0,0 +1,72 @@ +// code fix multipleEffectProvide_fix output for range 1888 - 1914 +import { Context, Effect, Layer } from "effect" + +class MyService1 extends Context.Service()("MyService1", { + make: Effect.succeed({ value: 1 }) +}) { + static Default = Layer.effect(this, this.make) +} + +class MyService2 extends Context.Service()("MyService2", { + make: Effect.succeed({ value: 2 }) +}) { + static Default = Layer.effect(this, this.make) +} + +class MyService3 extends Context.Service()("MyService3", { + make: Effect.succeed({ value: 3 }) +}) { + static Default = Layer.effect(this, this.make) +} + +const layerTuple: [typeof MyService1.Default, typeof MyService2.Default] = [ + MyService1.Default, + MyService2.Default +] + +export const shouldReport = Effect.void.pipe( + Effect.provide(MyService1.Default), + Effect.provide(MyService2.Default) +) + +export const shouldReportSeparately = Effect.void.pipe( + Effect.provide(MyService1.Default), + Effect.provide(MyService2.Default), + Effect.ignore, + Effect.provide(MyService1.Default), + Effect.provide(MyService2.Default) +) + +export const shouldReportSingle = Effect.void.pipe( + Effect.provide(MyService1.Default), + Effect.provide(MyService2.Default), + Effect.provide(MyService3.Default) +) + +export const shouldNotReportLocalProvide = Effect.void.pipe( + Effect.provide(MyService1.Default), + Effect.provide(MyService2.Default, { local: true }) +) + +export const shouldReportAfterLocalProvide = Effect.void.pipe( + Effect.provide(MyService1.Default, { local: true }), + Effect.provide(MyService2.Default), + Effect.provide(MyService3.Default) +) + +export const shouldReportAfterArrayRewrite = Effect.void.pipe( + Effect.provide([MyService1.Default, MyService2.Default]), + Effect.provide(MyService3.Default) +) + +export const shouldNotReportSingleArray = Effect.void.pipe( + Effect.provide([MyService1.Default, MyService2.Default]) +) + +export const shouldReportAfterTupleRewrite = Effect.void.pipe( + Effect.provide(Layer.mergeAll(...layerTuple, MyService3.Default)) +) + +export const shouldNotReportSingleTuple = Effect.void.pipe( + Effect.provide(layerTuple) +) diff --git a/packages/harness-effect-v4/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from592to626.output b/packages/harness-effect-v4/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from592to626.output index 28d35e40..0ca38f8b 100644 --- a/packages/harness-effect-v4/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from592to626.output +++ b/packages/harness-effect-v4/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from592to626.output @@ -47,3 +47,12 @@ export const shouldReportAfterLocalProvide = Effect.void.pipe( Effect.provide(MyService2.Default), Effect.provide(MyService3.Default) ) + +export const shouldReportAfterArrayRewrite = Effect.void.pipe( + Effect.provide([MyService1.Default, MyService2.Default]), + Effect.provide(MyService3.Default) +) + +export const shouldNotReportSingleArray = Effect.void.pipe( + Effect.provide([MyService1.Default, MyService2.Default]) +) diff --git a/packages/harness-effect-v4/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from715to749.output b/packages/harness-effect-v4/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from715to749.output new file mode 100644 index 00000000..9cccb1e5 --- /dev/null +++ b/packages/harness-effect-v4/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from715to749.output @@ -0,0 +1,72 @@ +// code fix multipleEffectProvide_fix output for range 715 - 749 +import { Context, Effect, Layer } from "effect" + +class MyService1 extends Context.Service()("MyService1", { + make: Effect.succeed({ value: 1 }) +}) { + static Default = Layer.effect(this, this.make) +} + +class MyService2 extends Context.Service()("MyService2", { + make: Effect.succeed({ value: 2 }) +}) { + static Default = Layer.effect(this, this.make) +} + +class MyService3 extends Context.Service()("MyService3", { + make: Effect.succeed({ value: 3 }) +}) { + static Default = Layer.effect(this, this.make) +} + +const layerTuple: [typeof MyService1.Default, typeof MyService2.Default] = [ + MyService1.Default, + MyService2.Default +] + +export const shouldReport = Effect.void.pipe( + Effect.provide(Layer.mergeAll(MyService1.Default, MyService2.Default)) +) + +export const shouldReportSeparately = Effect.void.pipe( + Effect.provide(MyService1.Default), + Effect.provide(MyService2.Default), + Effect.ignore, + Effect.provide(MyService1.Default), + Effect.provide(MyService2.Default) +) + +export const shouldReportSingle = Effect.void.pipe( + Effect.provide(MyService1.Default), + Effect.provide(MyService2.Default), + Effect.provide(MyService3.Default) +) + +export const shouldNotReportLocalProvide = Effect.void.pipe( + Effect.provide(MyService1.Default), + Effect.provide(MyService2.Default, { local: true }) +) + +export const shouldReportAfterLocalProvide = Effect.void.pipe( + Effect.provide(MyService1.Default, { local: true }), + Effect.provide(MyService2.Default), + Effect.provide(MyService3.Default) +) + +export const shouldReportAfterArrayRewrite = Effect.void.pipe( + Effect.provide([MyService1.Default, MyService2.Default]), + Effect.provide(MyService3.Default) +) + +export const shouldNotReportSingleArray = Effect.void.pipe( + Effect.provide([MyService1.Default, MyService2.Default]) +) + +export const shouldReportAfterTupleRewrite = Effect.void.pipe( + Effect.provide(layerTuple), + Effect.provide(MyService3.Default) +) + +export const shouldNotReportSingleTuple = Effect.void.pipe( + Effect.provide(layerTuple) +) diff --git a/packages/harness-effect-v4/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from726to760.output b/packages/harness-effect-v4/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from726to760.output index dbefcace..1110f9a0 100644 --- a/packages/harness-effect-v4/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from726to760.output +++ b/packages/harness-effect-v4/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from726to760.output @@ -47,3 +47,12 @@ export const shouldReportAfterLocalProvide = Effect.void.pipe( Effect.provide(MyService2.Default), Effect.provide(MyService3.Default) ) + +export const shouldReportAfterArrayRewrite = Effect.void.pipe( + Effect.provide([MyService1.Default, MyService2.Default]), + Effect.provide(MyService3.Default) +) + +export const shouldNotReportSingleArray = Effect.void.pipe( + Effect.provide([MyService1.Default, MyService2.Default]) +) diff --git a/packages/harness-effect-v4/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from819to853.output b/packages/harness-effect-v4/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from819to853.output index feef817a..611627eb 100644 --- a/packages/harness-effect-v4/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from819to853.output +++ b/packages/harness-effect-v4/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from819to853.output @@ -47,3 +47,12 @@ export const shouldReportAfterLocalProvide = Effect.void.pipe( Effect.provide(MyService2.Default), Effect.provide(MyService3.Default) ) + +export const shouldReportAfterArrayRewrite = Effect.void.pipe( + Effect.provide([MyService1.Default, MyService2.Default]), + Effect.provide(MyService3.Default) +) + +export const shouldNotReportSingleArray = Effect.void.pipe( + Effect.provide([MyService1.Default, MyService2.Default]) +) diff --git a/packages/harness-effect-v4/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from849to883.output b/packages/harness-effect-v4/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from849to883.output new file mode 100644 index 00000000..53c02baf --- /dev/null +++ b/packages/harness-effect-v4/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from849to883.output @@ -0,0 +1,72 @@ +// code fix multipleEffectProvide_fix output for range 849 - 883 +import { Context, Effect, Layer } from "effect" + +class MyService1 extends Context.Service()("MyService1", { + make: Effect.succeed({ value: 1 }) +}) { + static Default = Layer.effect(this, this.make) +} + +class MyService2 extends Context.Service()("MyService2", { + make: Effect.succeed({ value: 2 }) +}) { + static Default = Layer.effect(this, this.make) +} + +class MyService3 extends Context.Service()("MyService3", { + make: Effect.succeed({ value: 3 }) +}) { + static Default = Layer.effect(this, this.make) +} + +const layerTuple: [typeof MyService1.Default, typeof MyService2.Default] = [ + MyService1.Default, + MyService2.Default +] + +export const shouldReport = Effect.void.pipe( + Effect.provide(MyService1.Default), + Effect.provide(MyService2.Default) +) + +export const shouldReportSeparately = Effect.void.pipe( + Effect.provide(Layer.mergeAll(MyService1.Default, MyService2.Default)), + Effect.ignore, + Effect.provide(MyService1.Default), + Effect.provide(MyService2.Default) +) + +export const shouldReportSingle = Effect.void.pipe( + Effect.provide(MyService1.Default), + Effect.provide(MyService2.Default), + Effect.provide(MyService3.Default) +) + +export const shouldNotReportLocalProvide = Effect.void.pipe( + Effect.provide(MyService1.Default), + Effect.provide(MyService2.Default, { local: true }) +) + +export const shouldReportAfterLocalProvide = Effect.void.pipe( + Effect.provide(MyService1.Default, { local: true }), + Effect.provide(MyService2.Default), + Effect.provide(MyService3.Default) +) + +export const shouldReportAfterArrayRewrite = Effect.void.pipe( + Effect.provide([MyService1.Default, MyService2.Default]), + Effect.provide(MyService3.Default) +) + +export const shouldNotReportSingleArray = Effect.void.pipe( + Effect.provide([MyService1.Default, MyService2.Default]) +) + +export const shouldReportAfterTupleRewrite = Effect.void.pipe( + Effect.provide(layerTuple), + Effect.provide(MyService3.Default) +) + +export const shouldNotReportSingleTuple = Effect.void.pipe( + Effect.provide(layerTuple) +) diff --git a/packages/harness-effect-v4/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from942to976.output b/packages/harness-effect-v4/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from942to976.output new file mode 100644 index 00000000..cff0623b --- /dev/null +++ b/packages/harness-effect-v4/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from942to976.output @@ -0,0 +1,72 @@ +// code fix multipleEffectProvide_fix output for range 942 - 976 +import { Context, Effect, Layer } from "effect" + +class MyService1 extends Context.Service()("MyService1", { + make: Effect.succeed({ value: 1 }) +}) { + static Default = Layer.effect(this, this.make) +} + +class MyService2 extends Context.Service()("MyService2", { + make: Effect.succeed({ value: 2 }) +}) { + static Default = Layer.effect(this, this.make) +} + +class MyService3 extends Context.Service()("MyService3", { + make: Effect.succeed({ value: 3 }) +}) { + static Default = Layer.effect(this, this.make) +} + +const layerTuple: [typeof MyService1.Default, typeof MyService2.Default] = [ + MyService1.Default, + MyService2.Default +] + +export const shouldReport = Effect.void.pipe( + Effect.provide(MyService1.Default), + Effect.provide(MyService2.Default) +) + +export const shouldReportSeparately = Effect.void.pipe( + Effect.provide(MyService1.Default), + Effect.provide(MyService2.Default), + Effect.ignore, + Effect.provide(Layer.mergeAll(MyService1.Default, MyService2.Default)) +) + +export const shouldReportSingle = Effect.void.pipe( + Effect.provide(MyService1.Default), + Effect.provide(MyService2.Default), + Effect.provide(MyService3.Default) +) + +export const shouldNotReportLocalProvide = Effect.void.pipe( + Effect.provide(MyService1.Default), + Effect.provide(MyService2.Default, { local: true }) +) + +export const shouldReportAfterLocalProvide = Effect.void.pipe( + Effect.provide(MyService1.Default, { local: true }), + Effect.provide(MyService2.Default), + Effect.provide(MyService3.Default) +) + +export const shouldReportAfterArrayRewrite = Effect.void.pipe( + Effect.provide([MyService1.Default, MyService2.Default]), + Effect.provide(MyService3.Default) +) + +export const shouldNotReportSingleArray = Effect.void.pipe( + Effect.provide([MyService1.Default, MyService2.Default]) +) + +export const shouldReportAfterTupleRewrite = Effect.void.pipe( + Effect.provide(layerTuple), + Effect.provide(MyService3.Default) +) + +export const shouldNotReportSingleTuple = Effect.void.pipe( + Effect.provide(layerTuple) +) diff --git a/packages/harness-effect-v4/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from949to983.output b/packages/harness-effect-v4/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from949to983.output index fa879812..d768cc21 100644 --- a/packages/harness-effect-v4/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from949to983.output +++ b/packages/harness-effect-v4/__snapshots__/diagnostics/multipleEffectProvide.ts.multipleEffectProvide_fix.from949to983.output @@ -46,3 +46,12 @@ export const shouldReportAfterLocalProvide = Effect.void.pipe( Effect.provide(MyService2.Default), Effect.provide(MyService3.Default) ) + +export const shouldReportAfterArrayRewrite = Effect.void.pipe( + Effect.provide([MyService1.Default, MyService2.Default]), + Effect.provide(MyService3.Default) +) + +export const shouldNotReportSingleArray = Effect.void.pipe( + Effect.provide([MyService1.Default, MyService2.Default]) +) diff --git a/packages/harness-effect-v4/__snapshots__/diagnostics/multipleEffectProvide.ts.output b/packages/harness-effect-v4/__snapshots__/diagnostics/multipleEffectProvide.ts.output index c1b2c826..d0393aee 100644 --- a/packages/harness-effect-v4/__snapshots__/diagnostics/multipleEffectProvide.ts.output +++ b/packages/harness-effect-v4/__snapshots__/diagnostics/multipleEffectProvide.ts.output @@ -1,14 +1,20 @@ -Effect.provide(MyService1.Default) -22:2 - 22:36 | 0 | This expression chains multiple `Effect.provide` calls. Providing Layers in multiple calls in a chain can break service lifecycle behavior compared with a single combined provide with merged layers. effect(multipleEffectProvide) - Effect.provide(MyService1.Default) 27:2 - 27:36 | 0 | This expression chains multiple `Effect.provide` calls. Providing Layers in multiple calls in a chain can break service lifecycle behavior compared with a single combined provide with merged layers. effect(multipleEffectProvide) Effect.provide(MyService1.Default) -30:2 - 30:36 | 0 | This expression chains multiple `Effect.provide` calls. Providing Layers in multiple calls in a chain can break service lifecycle behavior compared with a single combined provide with merged layers. effect(multipleEffectProvide) +32:2 - 32:36 | 0 | This expression chains multiple `Effect.provide` calls. Providing Layers in multiple calls in a chain can break service lifecycle behavior compared with a single combined provide with merged layers. effect(multipleEffectProvide) Effect.provide(MyService1.Default) 35:2 - 35:36 | 0 | This expression chains multiple `Effect.provide` calls. Providing Layers in multiple calls in a chain can break service lifecycle behavior compared with a single combined provide with merged layers. effect(multipleEffectProvide) +Effect.provide(MyService1.Default) +40:2 - 40:36 | 0 | This expression chains multiple `Effect.provide` calls. Providing Layers in multiple calls in a chain can break service lifecycle behavior compared with a single combined provide with merged layers. effect(multipleEffectProvide) + Effect.provide(MyService2.Default) -47:2 - 47:36 | 0 | This expression chains multiple `Effect.provide` calls. Providing Layers in multiple calls in a chain can break service lifecycle behavior compared with a single combined provide with merged layers. effect(multipleEffectProvide) \ No newline at end of file +52:2 - 52:36 | 0 | This expression chains multiple `Effect.provide` calls. Providing Layers in multiple calls in a chain can break service lifecycle behavior compared with a single combined provide with merged layers. effect(multipleEffectProvide) + +Effect.provide([MyService1.Default, MyService2.Default]) +57:2 - 57:58 | 0 | This expression chains multiple `Effect.provide` calls. Providing Layers in multiple calls in a chain can break service lifecycle behavior compared with a single combined provide with merged layers. effect(multipleEffectProvide) + +Effect.provide(layerTuple) +66:2 - 66:28 | 0 | This expression chains multiple `Effect.provide` calls. Providing Layers in multiple calls in a chain can break service lifecycle behavior compared with a single combined provide with merged layers. effect(multipleEffectProvide) \ No newline at end of file diff --git a/packages/harness-effect-v4/__snapshots__/diagnostics/redundantLayerMergeAllInProvide.ts.codefixes b/packages/harness-effect-v4/__snapshots__/diagnostics/redundantLayerMergeAllInProvide.ts.codefixes new file mode 100644 index 00000000..89c0325f --- /dev/null +++ b/packages/harness-effect-v4/__snapshots__/diagnostics/redundantLayerMergeAllInProvide.ts.codefixes @@ -0,0 +1,15 @@ +redundantLayerMergeAllInProvide_fix from 469 to 483 +redundantLayerMergeAllInProvide_skipNextLine from 469 to 483 +redundantLayerMergeAllInProvide_skipFile from 469 to 483 +redundantLayerMergeAllInProvide_fix from 240 to 254 +redundantLayerMergeAllInProvide_skipNextLine from 240 to 254 +redundantLayerMergeAllInProvide_skipFile from 240 to 254 +redundantLayerMergeAllInProvide_fix from 348 to 362 +redundantLayerMergeAllInProvide_skipNextLine from 348 to 362 +redundantLayerMergeAllInProvide_skipFile from 348 to 362 +redundantLayerMergeAllInProvide_fix from 571 to 585 +redundantLayerMergeAllInProvide_skipNextLine from 571 to 585 +redundantLayerMergeAllInProvide_skipFile from 571 to 585 +redundantLayerMergeAllInProvide_fix from 684 to 698 +redundantLayerMergeAllInProvide_skipNextLine from 684 to 698 +redundantLayerMergeAllInProvide_skipFile from 684 to 698 \ No newline at end of file diff --git a/packages/harness-effect-v4/__snapshots__/diagnostics/redundantLayerMergeAllInProvide.ts.output b/packages/harness-effect-v4/__snapshots__/diagnostics/redundantLayerMergeAllInProvide.ts.output new file mode 100644 index 00000000..9c078c58 --- /dev/null +++ b/packages/harness-effect-v4/__snapshots__/diagnostics/redundantLayerMergeAllInProvide.ts.output @@ -0,0 +1,14 @@ +Layer.mergeAll +9:17 - 9:31 | 2 | `Effect.provide` accepts an array of Layers directly, so this `Layer.mergeAll` call is redundant. effect(redundantLayerMergeAllInProvide) + +Layer.mergeAll +13:17 - 13:31 | 2 | `Effect.provide` accepts an array of Layers directly, so this `Layer.mergeAll` call is redundant. effect(redundantLayerMergeAllInProvide) + +Layer.mergeAll +18:2 - 18:16 | 2 | `Effect.provide` accepts an array of Layers directly, so this `Layer.mergeAll` call is redundant. effect(redundantLayerMergeAllInProvide) + +Layer.mergeAll +22:17 - 22:31 | 2 | `Effect.provide` accepts an array of Layers directly, so this `Layer.mergeAll` call is redundant. effect(redundantLayerMergeAllInProvide) + +Layer.mergeAll +26:17 - 26:31 | 2 | `Effect.provide` accepts an array of Layers directly, so this `Layer.mergeAll` call is redundant. effect(redundantLayerMergeAllInProvide) \ No newline at end of file diff --git a/packages/harness-effect-v4/__snapshots__/diagnostics/redundantLayerMergeAllInProvide.ts.redundantLayerMergeAllInProvide_fix.from240to254.output b/packages/harness-effect-v4/__snapshots__/diagnostics/redundantLayerMergeAllInProvide.ts.redundantLayerMergeAllInProvide_fix.from240to254.output new file mode 100644 index 00000000..a491db04 --- /dev/null +++ b/packages/harness-effect-v4/__snapshots__/diagnostics/redundantLayerMergeAllInProvide.ts.redundantLayerMergeAllInProvide_fix.from240to254.output @@ -0,0 +1,55 @@ +// code fix redundantLayerMergeAllInProvide_fix output for range 240 - 254 +import { Context, Effect, Layer } from "effect" + +const first = Layer.empty +const second = Layer.empty +const third = Layer.empty +const layers = [first, second] as const + +export const shouldReportPipeable = Effect.void.pipe( + Effect.provide([first, second]) +) + +export const shouldReportWithOptions = Effect.void.pipe( + Effect.provide(Layer.mergeAll(first, second), { local: true }) +) + +export const shouldReportDataFirst = Effect.provide( + Effect.void, + Layer.mergeAll(first, second) +) + +export const shouldReportSpread = Effect.void.pipe( + Effect.provide(Layer.mergeAll(...[first, second])) +) + +export const shouldReportTupleSpread = Effect.void.pipe( + Effect.provide(Layer.mergeAll(...layers)) +) + +export const shouldNotReportTransformed = Effect.void.pipe( + Effect.provide( + Layer.mergeAll(first, second).pipe(Layer.provide(third)) + ) +) + +const combined = Layer.mergeAll(first, second) + +export const shouldNotReportPrecomposed = Effect.void.pipe( + Effect.provide(combined) +) + +class Dependency extends Context.Service()("Dependency", { + make: Effect.succeed({}) +}) { + static Default = Layer.effect(this, this.make) +} +class Consumer extends Context.Service()("Consumer", { + make: Effect.as(Dependency, {}) +}) { + static Default = Layer.effect(this, this.make) +} + +export const shouldNotReportWithDependencies = Effect.void.pipe( + Effect.provide(Layer.mergeAll(Dependency.Default, Consumer.Default)) +) diff --git a/packages/harness-effect-v4/__snapshots__/diagnostics/redundantLayerMergeAllInProvide.ts.redundantLayerMergeAllInProvide_fix.from348to362.output b/packages/harness-effect-v4/__snapshots__/diagnostics/redundantLayerMergeAllInProvide.ts.redundantLayerMergeAllInProvide_fix.from348to362.output new file mode 100644 index 00000000..346389a3 --- /dev/null +++ b/packages/harness-effect-v4/__snapshots__/diagnostics/redundantLayerMergeAllInProvide.ts.redundantLayerMergeAllInProvide_fix.from348to362.output @@ -0,0 +1,55 @@ +// code fix redundantLayerMergeAllInProvide_fix output for range 348 - 362 +import { Context, Effect, Layer } from "effect" + +const first = Layer.empty +const second = Layer.empty +const third = Layer.empty +const layers = [first, second] as const + +export const shouldReportPipeable = Effect.void.pipe( + Effect.provide(Layer.mergeAll(first, second)) +) + +export const shouldReportWithOptions = Effect.void.pipe( + Effect.provide([first, second], { local: true }) +) + +export const shouldReportDataFirst = Effect.provide( + Effect.void, + Layer.mergeAll(first, second) +) + +export const shouldReportSpread = Effect.void.pipe( + Effect.provide(Layer.mergeAll(...[first, second])) +) + +export const shouldReportTupleSpread = Effect.void.pipe( + Effect.provide(Layer.mergeAll(...layers)) +) + +export const shouldNotReportTransformed = Effect.void.pipe( + Effect.provide( + Layer.mergeAll(first, second).pipe(Layer.provide(third)) + ) +) + +const combined = Layer.mergeAll(first, second) + +export const shouldNotReportPrecomposed = Effect.void.pipe( + Effect.provide(combined) +) + +class Dependency extends Context.Service()("Dependency", { + make: Effect.succeed({}) +}) { + static Default = Layer.effect(this, this.make) +} +class Consumer extends Context.Service()("Consumer", { + make: Effect.as(Dependency, {}) +}) { + static Default = Layer.effect(this, this.make) +} + +export const shouldNotReportWithDependencies = Effect.void.pipe( + Effect.provide(Layer.mergeAll(Dependency.Default, Consumer.Default)) +) diff --git a/packages/harness-effect-v4/__snapshots__/diagnostics/redundantLayerMergeAllInProvide.ts.redundantLayerMergeAllInProvide_fix.from469to483.output b/packages/harness-effect-v4/__snapshots__/diagnostics/redundantLayerMergeAllInProvide.ts.redundantLayerMergeAllInProvide_fix.from469to483.output new file mode 100644 index 00000000..020c7741 --- /dev/null +++ b/packages/harness-effect-v4/__snapshots__/diagnostics/redundantLayerMergeAllInProvide.ts.redundantLayerMergeAllInProvide_fix.from469to483.output @@ -0,0 +1,55 @@ +// code fix redundantLayerMergeAllInProvide_fix output for range 469 - 483 +import { Context, Effect, Layer } from "effect" + +const first = Layer.empty +const second = Layer.empty +const third = Layer.empty +const layers = [first, second] as const + +export const shouldReportPipeable = Effect.void.pipe( + Effect.provide(Layer.mergeAll(first, second)) +) + +export const shouldReportWithOptions = Effect.void.pipe( + Effect.provide(Layer.mergeAll(first, second), { local: true }) +) + +export const shouldReportDataFirst = Effect.provide( + Effect.void, + [first, second] +) + +export const shouldReportSpread = Effect.void.pipe( + Effect.provide(Layer.mergeAll(...[first, second])) +) + +export const shouldReportTupleSpread = Effect.void.pipe( + Effect.provide(Layer.mergeAll(...layers)) +) + +export const shouldNotReportTransformed = Effect.void.pipe( + Effect.provide( + Layer.mergeAll(first, second).pipe(Layer.provide(third)) + ) +) + +const combined = Layer.mergeAll(first, second) + +export const shouldNotReportPrecomposed = Effect.void.pipe( + Effect.provide(combined) +) + +class Dependency extends Context.Service()("Dependency", { + make: Effect.succeed({}) +}) { + static Default = Layer.effect(this, this.make) +} +class Consumer extends Context.Service()("Consumer", { + make: Effect.as(Dependency, {}) +}) { + static Default = Layer.effect(this, this.make) +} + +export const shouldNotReportWithDependencies = Effect.void.pipe( + Effect.provide(Layer.mergeAll(Dependency.Default, Consumer.Default)) +) diff --git a/packages/harness-effect-v4/__snapshots__/diagnostics/redundantLayerMergeAllInProvide.ts.redundantLayerMergeAllInProvide_fix.from571to585.output b/packages/harness-effect-v4/__snapshots__/diagnostics/redundantLayerMergeAllInProvide.ts.redundantLayerMergeAllInProvide_fix.from571to585.output new file mode 100644 index 00000000..1a03900b --- /dev/null +++ b/packages/harness-effect-v4/__snapshots__/diagnostics/redundantLayerMergeAllInProvide.ts.redundantLayerMergeAllInProvide_fix.from571to585.output @@ -0,0 +1,55 @@ +// code fix redundantLayerMergeAllInProvide_fix output for range 571 - 585 +import { Context, Effect, Layer } from "effect" + +const first = Layer.empty +const second = Layer.empty +const third = Layer.empty +const layers = [first, second] as const + +export const shouldReportPipeable = Effect.void.pipe( + Effect.provide(Layer.mergeAll(first, second)) +) + +export const shouldReportWithOptions = Effect.void.pipe( + Effect.provide(Layer.mergeAll(first, second), { local: true }) +) + +export const shouldReportDataFirst = Effect.provide( + Effect.void, + Layer.mergeAll(first, second) +) + +export const shouldReportSpread = Effect.void.pipe( + Effect.provide([first, second]) +) + +export const shouldReportTupleSpread = Effect.void.pipe( + Effect.provide(Layer.mergeAll(...layers)) +) + +export const shouldNotReportTransformed = Effect.void.pipe( + Effect.provide( + Layer.mergeAll(first, second).pipe(Layer.provide(third)) + ) +) + +const combined = Layer.mergeAll(first, second) + +export const shouldNotReportPrecomposed = Effect.void.pipe( + Effect.provide(combined) +) + +class Dependency extends Context.Service()("Dependency", { + make: Effect.succeed({}) +}) { + static Default = Layer.effect(this, this.make) +} +class Consumer extends Context.Service()("Consumer", { + make: Effect.as(Dependency, {}) +}) { + static Default = Layer.effect(this, this.make) +} + +export const shouldNotReportWithDependencies = Effect.void.pipe( + Effect.provide(Layer.mergeAll(Dependency.Default, Consumer.Default)) +) diff --git a/packages/harness-effect-v4/__snapshots__/diagnostics/redundantLayerMergeAllInProvide.ts.redundantLayerMergeAllInProvide_fix.from684to698.output b/packages/harness-effect-v4/__snapshots__/diagnostics/redundantLayerMergeAllInProvide.ts.redundantLayerMergeAllInProvide_fix.from684to698.output new file mode 100644 index 00000000..f52053cf --- /dev/null +++ b/packages/harness-effect-v4/__snapshots__/diagnostics/redundantLayerMergeAllInProvide.ts.redundantLayerMergeAllInProvide_fix.from684to698.output @@ -0,0 +1,55 @@ +// code fix redundantLayerMergeAllInProvide_fix output for range 684 - 698 +import { Context, Effect, Layer } from "effect" + +const first = Layer.empty +const second = Layer.empty +const third = Layer.empty +const layers = [first, second] as const + +export const shouldReportPipeable = Effect.void.pipe( + Effect.provide(Layer.mergeAll(first, second)) +) + +export const shouldReportWithOptions = Effect.void.pipe( + Effect.provide(Layer.mergeAll(first, second), { local: true }) +) + +export const shouldReportDataFirst = Effect.provide( + Effect.void, + Layer.mergeAll(first, second) +) + +export const shouldReportSpread = Effect.void.pipe( + Effect.provide(Layer.mergeAll(...[first, second])) +) + +export const shouldReportTupleSpread = Effect.void.pipe( + Effect.provide([layers[0], layers[1]]) +) + +export const shouldNotReportTransformed = Effect.void.pipe( + Effect.provide( + Layer.mergeAll(first, second).pipe(Layer.provide(third)) + ) +) + +const combined = Layer.mergeAll(first, second) + +export const shouldNotReportPrecomposed = Effect.void.pipe( + Effect.provide(combined) +) + +class Dependency extends Context.Service()("Dependency", { + make: Effect.succeed({}) +}) { + static Default = Layer.effect(this, this.make) +} +class Consumer extends Context.Service()("Consumer", { + make: Effect.as(Dependency, {}) +}) { + static Default = Layer.effect(this, this.make) +} + +export const shouldNotReportWithDependencies = Effect.void.pipe( + Effect.provide(Layer.mergeAll(Dependency.Default, Consumer.Default)) +) diff --git a/packages/harness-effect-v4/__snapshots__/diagnostics/redundantLayerMergeAllInProvide_preview.ts.codefixes b/packages/harness-effect-v4/__snapshots__/diagnostics/redundantLayerMergeAllInProvide_preview.ts.codefixes new file mode 100644 index 00000000..a3c5192b --- /dev/null +++ b/packages/harness-effect-v4/__snapshots__/diagnostics/redundantLayerMergeAllInProvide_preview.ts.codefixes @@ -0,0 +1,3 @@ +redundantLayerMergeAllInProvide_fix from 193 to 207 +redundantLayerMergeAllInProvide_skipNextLine from 193 to 207 +redundantLayerMergeAllInProvide_skipFile from 193 to 207 \ No newline at end of file diff --git a/packages/harness-effect-v4/__snapshots__/diagnostics/redundantLayerMergeAllInProvide_preview.ts.output b/packages/harness-effect-v4/__snapshots__/diagnostics/redundantLayerMergeAllInProvide_preview.ts.output new file mode 100644 index 00000000..4106509b --- /dev/null +++ b/packages/harness-effect-v4/__snapshots__/diagnostics/redundantLayerMergeAllInProvide_preview.ts.output @@ -0,0 +1,2 @@ +Layer.mergeAll +6:17 - 6:31 | 2 | `Effect.provide` accepts an array of Layers directly, so this `Layer.mergeAll` call is redundant. effect(redundantLayerMergeAllInProvide) \ No newline at end of file diff --git a/packages/harness-effect-v4/__snapshots__/diagnostics/redundantLayerMergeAllInProvide_preview.ts.redundantLayerMergeAllInProvide_fix.from193to207.output b/packages/harness-effect-v4/__snapshots__/diagnostics/redundantLayerMergeAllInProvide_preview.ts.redundantLayerMergeAllInProvide_fix.from193to207.output new file mode 100644 index 00000000..f6521863 --- /dev/null +++ b/packages/harness-effect-v4/__snapshots__/diagnostics/redundantLayerMergeAllInProvide_preview.ts.redundantLayerMergeAllInProvide_fix.from193to207.output @@ -0,0 +1,8 @@ +// code fix redundantLayerMergeAllInProvide_fix output for range 193 - 207 +// @effect-diagnostics *:off +// @effect-diagnostics redundantLayerMergeAllInProvide:suggestion +import { Effect, Layer } from "effect" + +export const preview = Effect.void.pipe( + Effect.provide([Layer.empty, Layer.empty]) +) diff --git a/packages/harness-effect-v4/__snapshots__/diagnostics/strictEffectProvide.ts.codefixes b/packages/harness-effect-v4/__snapshots__/diagnostics/strictEffectProvide.ts.codefixes index 19490e13..e9bcb334 100644 --- a/packages/harness-effect-v4/__snapshots__/diagnostics/strictEffectProvide.ts.codefixes +++ b/packages/harness-effect-v4/__snapshots__/diagnostics/strictEffectProvide.ts.codefixes @@ -1,8 +1,10 @@ -strictEffectProvide_skipNextLine from 667 to 714 -strictEffectProvide_skipFile from 667 to 714 -strictEffectProvide_skipNextLine from 525 to 559 -strictEffectProvide_skipFile from 525 to 559 -strictEffectProvide_skipNextLine from 833 to 867 -strictEffectProvide_skipFile from 833 to 867 -strictEffectProvide_skipNextLine from 991 to 1061 -strictEffectProvide_skipFile from 991 to 1061 \ No newline at end of file +strictEffectProvide_skipNextLine from 790 to 837 +strictEffectProvide_skipFile from 790 to 837 +strictEffectProvide_skipNextLine from 648 to 682 +strictEffectProvide_skipFile from 648 to 682 +strictEffectProvide_skipNextLine from 956 to 990 +strictEffectProvide_skipFile from 956 to 990 +strictEffectProvide_skipNextLine from 1114 to 1184 +strictEffectProvide_skipFile from 1114 to 1184 +strictEffectProvide_skipNextLine from 1321 to 1347 +strictEffectProvide_skipFile from 1321 to 1347 \ No newline at end of file diff --git a/packages/harness-effect-v4/__snapshots__/diagnostics/strictEffectProvide.ts.output b/packages/harness-effect-v4/__snapshots__/diagnostics/strictEffectProvide.ts.output index 200650dc..eac2ba2b 100644 --- a/packages/harness-effect-v4/__snapshots__/diagnostics/strictEffectProvide.ts.output +++ b/packages/harness-effect-v4/__snapshots__/diagnostics/strictEffectProvide.ts.output @@ -1,11 +1,14 @@ Effect.provide(MyService1.Default) -18:2 - 18:36 | 0 | Effect.provide with a Layer should only be used at application entry points. If this is an entry point, you can safely disable this diagnostic. Otherwise, using Effect.provide may break scope lifetimes. Compose all layers at your entry point and provide them at once. effect(strictEffectProvide) +23:2 - 23:36 | 0 | Effect.provide with a Layer should only be used at application entry points. If this is an entry point, you can safely disable this diagnostic. Otherwise, using Effect.provide may break scope lifetimes. Compose all layers at your entry point and provide them at once. effect(strictEffectProvide) Effect.provide(Effect.void, MyService1.Default) -22:29 - 22:76 | 0 | Effect.provide with a Layer should only be used at application entry points. If this is an entry point, you can safely disable this diagnostic. Otherwise, using Effect.provide may break scope lifetimes. Compose all layers at your entry point and provide them at once. effect(strictEffectProvide) +27:29 - 27:76 | 0 | Effect.provide with a Layer should only be used at application entry points. If this is an entry point, you can safely disable this diagnostic. Otherwise, using Effect.provide may break scope lifetimes. Compose all layers at your entry point and provide them at once. effect(strictEffectProvide) Effect.provide(MyService1.Default) -27:2 - 27:36 | 0 | Effect.provide with a Layer should only be used at application entry points. If this is an entry point, you can safely disable this diagnostic. Otherwise, using Effect.provide may break scope lifetimes. Compose all layers at your entry point and provide them at once. effect(strictEffectProvide) +32:2 - 32:36 | 0 | Effect.provide with a Layer should only be used at application entry points. If this is an entry point, you can safely disable this diagnostic. Otherwise, using Effect.provide may break scope lifetimes. Compose all layers at your entry point and provide them at once. effect(strictEffectProvide) Effect.provide(Layer.mergeAll(MyService1.Default, MyService2.Default)) -33:2 - 33:72 | 0 | Effect.provide with a Layer should only be used at application entry points. If this is an entry point, you can safely disable this diagnostic. Otherwise, using Effect.provide may break scope lifetimes. Compose all layers at your entry point and provide them at once. effect(strictEffectProvide) \ No newline at end of file +38:2 - 38:72 | 0 | Effect.provide with a Layer should only be used at application entry points. If this is an entry point, you can safely disable this diagnostic. Otherwise, using Effect.provide may break scope lifetimes. Compose all layers at your entry point and provide them at once. effect(strictEffectProvide) + +Effect.provide(layerTuple) +43:2 - 43:28 | 0 | Effect.provide with a Layer should only be used at application entry points. If this is an entry point, you can safely disable this diagnostic. Otherwise, using Effect.provide may break scope lifetimes. Compose all layers at your entry point and provide them at once. effect(strictEffectProvide) \ No newline at end of file diff --git a/packages/harness-effect-v4/examples/diagnostics/multipleEffectProvide.ts b/packages/harness-effect-v4/examples/diagnostics/multipleEffectProvide.ts index 76a00ff3..e4e8bb17 100644 --- a/packages/harness-effect-v4/examples/diagnostics/multipleEffectProvide.ts +++ b/packages/harness-effect-v4/examples/diagnostics/multipleEffectProvide.ts @@ -18,6 +18,11 @@ class MyService3 extends Context.Service()("MyService3", { static Default = Layer.effect(this, this.make) } +const layerTuple: [typeof MyService1.Default, typeof MyService2.Default] = [ + MyService1.Default, + MyService2.Default +] + export const shouldReport = Effect.void.pipe( Effect.provide(MyService1.Default), Effect.provide(MyService2.Default) @@ -47,3 +52,21 @@ export const shouldReportAfterLocalProvide = Effect.void.pipe( Effect.provide(MyService2.Default), Effect.provide(MyService3.Default) ) + +export const shouldReportAfterArrayRewrite = Effect.void.pipe( + Effect.provide([MyService1.Default, MyService2.Default]), + Effect.provide(MyService3.Default) +) + +export const shouldNotReportSingleArray = Effect.void.pipe( + Effect.provide([MyService1.Default, MyService2.Default]) +) + +export const shouldReportAfterTupleRewrite = Effect.void.pipe( + Effect.provide(layerTuple), + Effect.provide(MyService3.Default) +) + +export const shouldNotReportSingleTuple = Effect.void.pipe( + Effect.provide(layerTuple) +) diff --git a/packages/harness-effect-v4/examples/diagnostics/redundantLayerMergeAllInProvide.ts b/packages/harness-effect-v4/examples/diagnostics/redundantLayerMergeAllInProvide.ts new file mode 100644 index 00000000..aa2bf3b3 --- /dev/null +++ b/packages/harness-effect-v4/examples/diagnostics/redundantLayerMergeAllInProvide.ts @@ -0,0 +1,54 @@ +import { Context, Effect, Layer } from "effect" + +const first = Layer.empty +const second = Layer.empty +const third = Layer.empty +const layers = [first, second] as const + +export const shouldReportPipeable = Effect.void.pipe( + Effect.provide(Layer.mergeAll(first, second)) +) + +export const shouldReportWithOptions = Effect.void.pipe( + Effect.provide(Layer.mergeAll(first, second), { local: true }) +) + +export const shouldReportDataFirst = Effect.provide( + Effect.void, + Layer.mergeAll(first, second) +) + +export const shouldReportSpread = Effect.void.pipe( + Effect.provide(Layer.mergeAll(...[first, second])) +) + +export const shouldReportTupleSpread = Effect.void.pipe( + Effect.provide(Layer.mergeAll(...layers)) +) + +export const shouldNotReportTransformed = Effect.void.pipe( + Effect.provide( + Layer.mergeAll(first, second).pipe(Layer.provide(third)) + ) +) + +const combined = Layer.mergeAll(first, second) + +export const shouldNotReportPrecomposed = Effect.void.pipe( + Effect.provide(combined) +) + +class Dependency extends Context.Service()("Dependency", { + make: Effect.succeed({}) +}) { + static Default = Layer.effect(this, this.make) +} +class Consumer extends Context.Service()("Consumer", { + make: Effect.as(Dependency, {}) +}) { + static Default = Layer.effect(this, this.make) +} + +export const shouldNotReportWithDependencies = Effect.void.pipe( + Effect.provide(Layer.mergeAll(Dependency.Default, Consumer.Default)) +) diff --git a/packages/harness-effect-v4/examples/diagnostics/redundantLayerMergeAllInProvide_preview.ts b/packages/harness-effect-v4/examples/diagnostics/redundantLayerMergeAllInProvide_preview.ts new file mode 100644 index 00000000..fc7807c0 --- /dev/null +++ b/packages/harness-effect-v4/examples/diagnostics/redundantLayerMergeAllInProvide_preview.ts @@ -0,0 +1,7 @@ +// @effect-diagnostics *:off +// @effect-diagnostics redundantLayerMergeAllInProvide:suggestion +import { Effect, Layer } from "effect" + +export const preview = Effect.void.pipe( + Effect.provide(Layer.mergeAll(Layer.empty, Layer.empty)) +) diff --git a/packages/harness-effect-v4/examples/diagnostics/strictEffectProvide.ts b/packages/harness-effect-v4/examples/diagnostics/strictEffectProvide.ts index ce522876..fb806b58 100644 --- a/packages/harness-effect-v4/examples/diagnostics/strictEffectProvide.ts +++ b/packages/harness-effect-v4/examples/diagnostics/strictEffectProvide.ts @@ -13,6 +13,11 @@ class MyService2 extends Context.Service()("MyService2", { static Default = Layer.effect(this, this.make) } +const layerTuple: [typeof MyService1.Default, typeof MyService2.Default] = [ + MyService1.Default, + MyService2.Default +] + // Should report: Effect.provide with a Layer export const shouldReport1 = Effect.void.pipe( Effect.provide(MyService1.Default) @@ -33,6 +38,11 @@ export const shouldReport4 = Effect.void.pipe( Effect.provide(Layer.mergeAll(MyService1.Default, MyService2.Default)) ) +// Should report: non-empty tuples are Layer collections accepted by Effect.provide +export const shouldReport5 = Effect.void.pipe( + Effect.provide(layerTuple) +) + // Should NOT report: providing a plain service (not a layer) export const shouldNotReport1 = Effect.void.pipe( Effect.provideService(MyService1, MyService1.of({ value: 1 })) diff --git a/packages/language-service/src/diagnostics.ts b/packages/language-service/src/diagnostics.ts index 28aaaa8c..fd80a91d 100644 --- a/packages/language-service/src/diagnostics.ts +++ b/packages/language-service/src/diagnostics.ts @@ -56,6 +56,7 @@ import { overriddenSchemaConstructor } from "./diagnostics/overriddenSchemaConst import { preferSchemaOverJson } from "./diagnostics/preferSchemaOverJson.js" import { processEnv } from "./diagnostics/processEnv.js" import { processEnvInEffect } from "./diagnostics/processEnvInEffect.js" +import { redundantLayerMergeAllInProvide } from "./diagnostics/redundantLayerMergeAllInProvide.js" import { redundantSchemaTagIdentifier } from "./diagnostics/redundantSchemaTagIdentifier.js" import { returnEffectInGen } from "./diagnostics/returnEffectInGen.js" import { runEffectInsideEffect } from "./diagnostics/runEffectInsideEffect.js" @@ -140,6 +141,7 @@ export const diagnostics = [ effectSucceedWithVoid, effectFnIife, effectFnOpportunity, + redundantLayerMergeAllInProvide, redundantSchemaTagIdentifier, schemaSyncInEffect, preferSchemaOverJson, diff --git a/packages/language-service/src/diagnostics/effectProvideLayerArgument.ts b/packages/language-service/src/diagnostics/effectProvideLayerArgument.ts new file mode 100644 index 00000000..330cc4e2 --- /dev/null +++ b/packages/language-service/src/diagnostics/effectProvideLayerArgument.ts @@ -0,0 +1,58 @@ +import { pipe } from "effect/Function" +import * as Option from "effect/Option" +import type ts from "typescript" +import * as Nano from "../core/Nano.js" +import * as TypeCheckerApi from "../core/TypeCheckerApi.js" +import * as TypeCheckerUtils from "../core/TypeCheckerUtils.js" +import * as TypeParser from "../core/TypeParser.js" +import * as TypeScriptApi from "../core/TypeScriptApi.js" + +export const parseEffectProvideLayerArgument = Nano.fn( + "effectProvideLayerArgument.parse" +)(function*(argument: ts.Expression) { + const ts = yield* Nano.service(TypeScriptApi.TypeScriptApi) + const typeChecker = yield* Nano.service(TypeCheckerApi.TypeCheckerApi) + const typeCheckerUtils = yield* Nano.service(TypeCheckerUtils.TypeCheckerUtils) + const typeParser = yield* Nano.service(TypeParser.TypeParser) + + if (ts.isArrayLiteralExpression(argument)) { + if (argument.elements.length === 0) { + return yield* TypeParser.typeParserIssue("Layer array is empty", undefined, argument) + } + for (const element of argument.elements) { + const type = typeCheckerUtils.getTypeAtLocation(element) + if (!type) { + return yield* TypeParser.typeParserIssue("Could not get Layer array element type", undefined, element) + } + yield* typeParser.layerType(type, element) + } + return Array.from(argument.elements) + } + + const argumentType = typeCheckerUtils.getTypeAtLocation(argument) + if (!argumentType) { + return yield* TypeParser.typeParserIssue("Could not get Effect.provide argument type", undefined, argument) + } + + const directLayer = yield* pipe( + typeParser.layerType(argumentType, argument), + Nano.option + ) + if (Option.isSome(directLayer)) { + return [argument] + } + + if (!typeChecker.isTupleType(argumentType)) { + return yield* TypeParser.typeParserIssue("Effect.provide argument is not a Layer tuple", argumentType, argument) + } + + const elementTypes = typeChecker.getTypeArguments(argumentType as ts.TypeReference) + if (elementTypes.length === 0) { + return yield* TypeParser.typeParserIssue("Layer tuple is empty", argumentType, argument) + } + for (const elementType of elementTypes) { + yield* typeParser.layerType(elementType, argument) + } + + return [ts.factory.createSpreadElement(argument)] +}) diff --git a/packages/language-service/src/diagnostics/layerMergeAllDependencies.ts b/packages/language-service/src/diagnostics/layerMergeAllDependencies.ts new file mode 100644 index 00000000..0404e095 --- /dev/null +++ b/packages/language-service/src/diagnostics/layerMergeAllDependencies.ts @@ -0,0 +1,69 @@ +import * as Option from "effect/Option" +import type ts from "typescript" +import * as Nano from "../core/Nano.js" +import * as TypeCheckerApi from "../core/TypeCheckerApi.js" +import * as TypeCheckerUtils from "../core/TypeCheckerUtils.js" +import * as TypeParser from "../core/TypeParser.js" +import * as TypeScriptApi from "../core/TypeScriptApi.js" + +export interface LayerMergeAllDependency { + readonly provider: ts.Expression + readonly providedTypes: string +} + +export const findLayerMergeAllDependencies = Nano.fn("findLayerMergeAllDependencies")(function*( + layerArgs: ReadonlyArray +) { + if (layerArgs.length <= 1) return [] + + const ts = yield* Nano.service(TypeScriptApi.TypeScriptApi) + const typeChecker = yield* Nano.service(TypeCheckerApi.TypeCheckerApi) + const typeCheckerUtils = yield* Nano.service(TypeCheckerUtils.TypeCheckerUtils) + const typeParser = yield* Nano.service(TypeParser.TypeParser) + const layerInfos: Array<{ + arg: ts.Expression + requirementsType: ts.Type + }> = [] + const actuallyProvidedMap = new Map() + + for (const arg of layerArgs) { + const argType = typeCheckerUtils.getTypeAtLocation(arg) + if (!argType) continue + + const layerTypeParsedOption = yield* Nano.option(typeParser.layerType(argType, arg)) + if (Option.isNone(layerTypeParsedOption)) continue + + const layerTypeParsed = layerTypeParsedOption.value + const providedMembers = typeCheckerUtils.unrollUnionMembers(layerTypeParsed.ROut) + + for (const providedType of providedMembers) { + if (providedType.flags & ts.TypeFlags.Never) continue + const isPassThrough = typeChecker.isTypeAssignableTo(providedType, layerTypeParsed.RIn) + if (!isPassThrough) { + actuallyProvidedMap.set(providedType, arg) + } + } + + layerInfos.push({ + arg, + requirementsType: layerTypeParsed.RIn + }) + } + + const providerToTypes = new Map>() + for (const layer of layerInfos) { + for (const [providedType, provider] of actuallyProvidedMap) { + if (provider === layer.arg) continue + if (!typeChecker.isTypeAssignableTo(providedType, layer.requirementsType)) continue + + const providedTypes = providerToTypes.get(provider) ?? new Set() + providedTypes.add(typeChecker.typeToString(providedType)) + providerToTypes.set(provider, providedTypes) + } + } + + return Array.from(providerToTypes, ([provider, providedTypes]) => ({ + provider, + providedTypes: Array.from(providedTypes).join(", ") + })) +}) diff --git a/packages/language-service/src/diagnostics/layerMergeAllWithDependencies.ts b/packages/language-service/src/diagnostics/layerMergeAllWithDependencies.ts index 0959b429..2cdae97e 100644 --- a/packages/language-service/src/diagnostics/layerMergeAllWithDependencies.ts +++ b/packages/language-service/src/diagnostics/layerMergeAllWithDependencies.ts @@ -1,13 +1,11 @@ import { pipe } from "effect/Function" -import * as Option from "effect/Option" import type ts from "typescript" import * as LSP from "../core/LSP.js" import * as Nano from "../core/Nano.js" -import * as TypeCheckerApi from "../core/TypeCheckerApi.js" -import * as TypeCheckerUtils from "../core/TypeCheckerUtils.js" import * as TypeParser from "../core/TypeParser.js" import * as TypeScriptApi from "../core/TypeScriptApi.js" import * as TypeScriptUtils from "../core/TypeScriptUtils.js" +import { findLayerMergeAllDependencies } from "./layerMergeAllDependencies.js" export const layerMergeAllWithDependencies = LSP.createDiagnostic({ name: "layerMergeAllWithDependencies", @@ -20,8 +18,6 @@ export const layerMergeAllWithDependencies = LSP.createDiagnostic({ supportedEffect: ["v3", "v4"], apply: Nano.fn("layerMergeAllWithDependencies.apply")(function*(sourceFile, report) { const ts = yield* Nano.service(TypeScriptApi.TypeScriptApi) - const typeChecker = yield* Nano.service(TypeCheckerApi.TypeCheckerApi) - const typeCheckerUtils = yield* Nano.service(TypeCheckerUtils.TypeCheckerUtils) const typeParser = yield* Nano.service(TypeParser.TypeParser) const tsUtils = yield* Nano.service(TypeScriptUtils.TypeScriptUtils) @@ -49,123 +45,59 @@ export const layerMergeAllWithDependencies = LSP.createDiagnostic({ ) if (checkLayerMergeAll) { - // Get all layer arguments const layerArgs = node.arguments - - if (layerArgs.length > 1) { - // Parse all layers to extract their ROut (provided) and RIn (required) types - const layerInfos: Array<{ - arg: ts.Expression - requirementsType: ts.Type - }> = [] - - // Map of actually provided types -> layer argument that provides it - const actuallyProvidedMap = new Map() - - for (const arg of layerArgs) { - const argType = typeCheckerUtils.getTypeAtLocation(arg) - if (!argType) continue - - const layerTypeParsedOption = yield* Nano.option(typeParser.layerType(argType, arg)) - if (Option.isNone(layerTypeParsedOption)) continue - - const layerTypeParsed = layerTypeParsedOption.value - - // Unroll union members for provided types (ROut) - const providedMembers = typeCheckerUtils.unrollUnionMembers(layerTypeParsed.ROut) - - // Filter out pass-through types: types that are both provided and required - // Add only actually provided types to the map - for (const providedType of providedMembers) { - if (providedType.flags & ts.TypeFlags.Never) continue - const isPassThrough = typeChecker.isTypeAssignableTo(providedType, layerTypeParsed.RIn) - if (!isPassThrough) { - actuallyProvidedMap.set(providedType, arg) - } - } - - layerInfos.push({ - arg, - requirementsType: layerTypeParsed.RIn - }) - } - - // Check for interdependencies - // Build a map of provider -> layers that depend on it - const providerToConsumers = new Map< - ts.Expression, - Array<{ consumer: ts.Expression; providedType: ts.Type }> - >() - - for (const layer of layerInfos) { - // Check if any of the actually provided types satisfy this layer's requirements - for (const [providedType, providerArg] of actuallyProvidedMap) { - // Skip if this is the same layer - if (providerArg === layer.arg) continue - - // Check if this provided type is assignable to the layer's requirements type - if (typeChecker.isTypeAssignableTo(providedType, layer.requirementsType)) { - const consumers = providerToConsumers.get(providerArg) || [] - consumers.push({ consumer: layer.arg, providedType }) - providerToConsumers.set(providerArg, consumers) - } - } - } - - // Report on providers that have consumers - for (const [providerArg, consumers] of providerToConsumers) { - const providedTypes = Array.from(new Set(consumers.map((c) => typeChecker.typeToString(c.providedType)))) - .join(", ") - - report({ - location: providerArg, - messageText: - `This layer provides ${providedTypes} which is required by another layer in the same Layer.mergeAll call. Layer.mergeAll creates layers in parallel, so dependencies between layers will not be satisfied. Consider moving this layer into a Layer.provideMerge after the Layer.mergeAll.`, - fixes: [{ - fixName: "layerMergeAllWithDependencies_fix", - description: "Move layer to Layer.provideMerge", - apply: Nano.gen(function*() { - const changeTracker = yield* Nano.service(TypeScriptApi.ChangeTracker) - - // Find the index of the provider argument - const providerIndex = layerArgs.indexOf(providerArg as ts.Expression) - if (providerIndex === -1) return - - // Step 1: Delete the provider argument from Layer.mergeAll - // Need to handle commas correctly - const providerArgNode = providerArg as ts.Expression - if (providerIndex === 0 && layerArgs.length > 1) { - // First argument - delete including the trailing comma - changeTracker.deleteRange(sourceFile, { - pos: providerArgNode.pos, - end: layerArgs[1].pos - }) - } else if (providerIndex > 0) { - // Not first argument - delete including the preceding comma - changeTracker.deleteRange(sourceFile, { - pos: layerArgs[providerIndex - 1].end, - end: providerArgNode.end - }) - } - - // Step 2: Insert .pipe(Layer.provideMerge(providerArg)) at the end - const provideMergeCall = ts.factory.createCallExpression( - ts.factory.createPropertyAccessExpression( - ts.factory.createIdentifier(layerModuleIdentifier), - ts.factory.createIdentifier("provideMerge") - ), - undefined, - [providerArgNode] - ) - - changeTracker.insertNodeAt(sourceFile, node.end, provideMergeCall, { - prefix: ".pipe(" + const dependencies = yield* findLayerMergeAllDependencies(layerArgs) + for (const dependency of dependencies) { + const providerArg = dependency.provider + const providedTypes = dependency.providedTypes + report({ + location: providerArg, + messageText: + `This layer provides ${providedTypes} which is required by another layer in the same Layer.mergeAll call. Layer.mergeAll creates layers in parallel, so dependencies between layers will not be satisfied. Consider moving this layer into a Layer.provideMerge after the Layer.mergeAll.`, + fixes: [{ + fixName: "layerMergeAllWithDependencies_fix", + description: "Move layer to Layer.provideMerge", + apply: Nano.gen(function*() { + const changeTracker = yield* Nano.service(TypeScriptApi.ChangeTracker) + + // Find the index of the provider argument + const providerIndex = layerArgs.indexOf(providerArg as ts.Expression) + if (providerIndex === -1) return + + // Step 1: Delete the provider argument from Layer.mergeAll + // Need to handle commas correctly + const providerArgNode = providerArg as ts.Expression + if (providerIndex === 0 && layerArgs.length > 1) { + // First argument - delete including the trailing comma + changeTracker.deleteRange(sourceFile, { + pos: providerArgNode.pos, + end: layerArgs[1].pos + }) + } else if (providerIndex > 0) { + // Not first argument - delete including the preceding comma + changeTracker.deleteRange(sourceFile, { + pos: layerArgs[providerIndex - 1].end, + end: providerArgNode.end }) - changeTracker.insertText(sourceFile, node.end, ")") + } + + // Step 2: Insert .pipe(Layer.provideMerge(providerArg)) at the end + const provideMergeCall = ts.factory.createCallExpression( + ts.factory.createPropertyAccessExpression( + ts.factory.createIdentifier(layerModuleIdentifier), + ts.factory.createIdentifier("provideMerge") + ), + undefined, + [providerArgNode] + ) + + changeTracker.insertNodeAt(sourceFile, node.end, provideMergeCall, { + prefix: ".pipe(" }) - }] - }) - } + changeTracker.insertText(sourceFile, node.end, ")") + }) + }] + }) } } } diff --git a/packages/language-service/src/diagnostics/multipleEffectProvide.ts b/packages/language-service/src/diagnostics/multipleEffectProvide.ts index 26855b6e..8255da2e 100644 --- a/packages/language-service/src/diagnostics/multipleEffectProvide.ts +++ b/packages/language-service/src/diagnostics/multipleEffectProvide.ts @@ -3,10 +3,10 @@ import * as Option from "effect/Option" import type ts from "typescript" import * as LSP from "../core/LSP.js" import * as Nano from "../core/Nano.js" -import * as TypeCheckerUtils from "../core/TypeCheckerUtils.js" import * as TypeParser from "../core/TypeParser.js" import * as TypeScriptApi from "../core/TypeScriptApi.js" import * as TypeScriptUtils from "../core/TypeScriptUtils.js" +import { parseEffectProvideLayerArgument } from "./effectProvideLayerArgument.js" export const multipleEffectProvide = LSP.createDiagnostic({ name: "multipleEffectProvide", @@ -19,7 +19,6 @@ export const multipleEffectProvide = LSP.createDiagnostic({ apply: Nano.fn("multipleEffectProvide.apply")(function*(sourceFile, report) { const ts = yield* Nano.service(TypeScriptApi.TypeScriptApi) const tsUtils = yield* Nano.service(TypeScriptUtils.TypeScriptUtils) - const typeCheckerUtils = yield* Nano.service(TypeCheckerUtils.TypeCheckerUtils) const typeParser = yield* Nano.service(TypeParser.TypeParser) const supportedEffect = typeParser.supportedEffect() @@ -40,7 +39,12 @@ export const multipleEffectProvide = LSP.createDiagnostic({ for (const flow of flows) { let currentChunk = 0 - const previousLayers: Array> = [[]] + const previousLayers: Array< + Array<{ + layers: ReadonlyArray + node: ts.CallExpression + }> + > = [[]] // Look for consecutive Effect.provide transformations in the flow for (const transformation of flow.transformations) { @@ -77,19 +81,14 @@ export const multipleEffectProvide = LSP.createDiagnostic({ } const layer = transformation.args[0] - const type = typeCheckerUtils.getTypeAtLocation(layer) const node = ts.findAncestor(transformation.callee, ts.isCallExpression) + const layers = yield* pipe( + parseEffectProvideLayerArgument(layer), + Nano.option + ) - // Check if the argument is a Layer type and we found the call expression - const isLayerType = type - ? yield* pipe( - typeParser.layerType(type, layer), - Nano.option - ) - : Option.none() - - if (Option.isSome(isLayerType) && node) { - previousLayers[currentChunk].push({ layer, node }) + if (Option.isSome(layers) && node) { + previousLayers[currentChunk].push({ layers: layers.value, node }) } else { // Not a layer, breaks the chain currentChunk++ @@ -130,7 +129,7 @@ export const multipleEffectProvide = LSP.createDiagnostic({ ts.factory.createIdentifier("mergeAll") ), undefined, - chunk.map((c) => c.layer) + chunk.flatMap((c) => c.layers) )] ) changeTracker.insertNodeAt(sourceFile, ts.getTokenPosOfNode(chunk[0].node, sourceFile), newNode) diff --git a/packages/language-service/src/diagnostics/redundantLayerMergeAllInProvide.ts b/packages/language-service/src/diagnostics/redundantLayerMergeAllInProvide.ts new file mode 100644 index 00000000..1cf74905 --- /dev/null +++ b/packages/language-service/src/diagnostics/redundantLayerMergeAllInProvide.ts @@ -0,0 +1,115 @@ +import { pipe } from "effect/Function" +import type ts from "typescript" +import * as LSP from "../core/LSP.js" +import * as Nano from "../core/Nano.js" +import * as TypeCheckerApi from "../core/TypeCheckerApi.js" +import * as TypeCheckerUtils from "../core/TypeCheckerUtils.js" +import * as TypeParser from "../core/TypeParser.js" +import * as TypeScriptApi from "../core/TypeScriptApi.js" +import { findLayerMergeAllDependencies } from "./layerMergeAllDependencies.js" + +export const redundantLayerMergeAllInProvide = LSP.createDiagnostic({ + name: "redundantLayerMergeAllInProvide", + code: 77, + description: "Replaces direct Layer.mergeAll arguments to Effect.provide with an array of layers", + group: "style", + severity: "suggestion", + fixable: true, + supportedEffect: ["v3", "v4"], + apply: Nano.fn("redundantLayerMergeAllInProvide.apply")(function*(sourceFile, report) { + const ts = yield* Nano.service(TypeScriptApi.TypeScriptApi) + const typeChecker = yield* Nano.service(TypeCheckerApi.TypeCheckerApi) + const typeCheckerUtils = yield* Nano.service(TypeCheckerUtils.TypeCheckerUtils) + const typeParser = yield* Nano.service(TypeParser.TypeParser) + + const nodesToVisit: Array = [] + const appendNodeToVisit = (node: ts.Node) => { + nodesToVisit.push(node) + return undefined + } + ts.forEachChild(sourceFile, appendNodeToVisit) + + while (nodesToVisit.length > 0) { + const node = nodesToVisit.shift()! + ts.forEachChild(node, appendNodeToVisit) + + if (!ts.isCallExpression(node)) continue + + const isEffectProvide = yield* pipe( + typeParser.isNodeReferenceToEffectModuleApi("provide")(node.expression), + Nano.orUndefined + ) + if (!isEffectProvide) continue + + for (const argument of node.arguments) { + if (!ts.isCallExpression(argument)) continue + + const isLayerMergeAll = yield* pipe( + typeParser.isNodeReferenceToEffectLayerModuleApi("mergeAll")(argument.expression), + Nano.orUndefined + ) + if (!isLayerMergeAll) continue + if ((yield* findLayerMergeAllDependencies(argument.arguments)).length > 0) continue + + const startLine = sourceFile.getLineAndCharacterOfPosition(ts.getTokenPosOfNode(argument, sourceFile)).line + const endLine = sourceFile.getLineAndCharacterOfPosition(argument.end).line + const onlyArgument = argument.arguments.length === 1 + ? argument.arguments[0] + : undefined + let replacement: ts.Expression + if (onlyArgument && ts.isSpreadElement(onlyArgument)) { + const spreadExpression = onlyArgument.expression + const spreadType = typeCheckerUtils.getTypeAtLocation(spreadExpression) + if ( + spreadType && + typeChecker.isTupleType(spreadType) && + (spreadType as ts.TupleTypeReference).target.readonly + ) { + const tupleTarget = (spreadType as ts.TupleTypeReference).target + const elementTypes = typeChecker.getTypeArguments(spreadType as ts.TypeReference) + if ( + !ts.isIdentifier(spreadExpression) || + elementTypes.length === 0 || + tupleTarget.elementFlags.some((flag) => flag !== ts.ElementFlags.Required) + ) { + continue + } + replacement = ts.factory.createArrayLiteralExpression( + elementTypes.map((_, index) => + ts.factory.createElementAccessExpression( + ts.factory.createIdentifier(spreadExpression.text), + index + ) + ) + ) + } else { + replacement = spreadExpression + } + } else { + replacement = ts.factory.createArrayLiteralExpression( + argument.arguments, + startLine !== endLine + ) + } + + report({ + location: argument.expression, + messageText: + "`Effect.provide` accepts an array of Layers directly, so this `Layer.mergeAll` call is redundant.", + fixes: [{ + fixName: "redundantLayerMergeAllInProvide_fix", + description: "Replace Layer.mergeAll with an array", + apply: Nano.gen(function*() { + const changeTracker = yield* Nano.service(TypeScriptApi.ChangeTracker) + changeTracker.replaceNode( + sourceFile, + argument, + replacement + ) + }) + }] + }) + } + } + }) +}) diff --git a/packages/language-service/src/diagnostics/strictEffectProvide.ts b/packages/language-service/src/diagnostics/strictEffectProvide.ts index 4e4eeba6..2feb11e5 100644 --- a/packages/language-service/src/diagnostics/strictEffectProvide.ts +++ b/packages/language-service/src/diagnostics/strictEffectProvide.ts @@ -3,9 +3,9 @@ import * as Option from "effect/Option" import type ts from "typescript" import * as LSP from "../core/LSP.js" import * as Nano from "../core/Nano.js" -import * as TypeCheckerUtils from "../core/TypeCheckerUtils.js" import * as TypeParser from "../core/TypeParser.js" import * as TypeScriptApi from "../core/TypeScriptApi.js" +import { parseEffectProvideLayerArgument } from "./effectProvideLayerArgument.js" export const strictEffectProvide = LSP.createDiagnostic({ name: "strictEffectProvide", @@ -17,7 +17,6 @@ export const strictEffectProvide = LSP.createDiagnostic({ supportedEffect: ["v3", "v4"], apply: Nano.fn("strictEffectProvide.apply")(function*(sourceFile, report) { const ts = yield* Nano.service(TypeScriptApi.TypeScriptApi) - const typeCheckerUtils = yield* Nano.service(TypeCheckerUtils.TypeCheckerUtils) const typeParser = yield* Nano.service(TypeParser.TypeParser) const parseEffectProvideWithLayerGen = Nano.fn("strictEffectProvide.parseEffectProvideWithLayer")( @@ -27,11 +26,7 @@ export const strictEffectProvide = LSP.createDiagnostic({ // Check if any argument is a Layer using firstSuccessOf return yield* Nano.firstSuccessOf( - node.arguments.map((arg) => { - const argType = typeCheckerUtils.getTypeAtLocation(arg) - if (!argType) return TypeParser.typeParserIssue("Could not get argument type") - return typeParser.layerType(argType, arg) - }) + node.arguments.map(parseEffectProvideLayerArgument) ) } ) diff --git a/packages/language-service/src/metadata.json b/packages/language-service/src/metadata.json index 69f19809..1208a32e 100644 --- a/packages/language-service/src/metadata.json +++ b/packages/language-service/src/metadata.json @@ -1470,6 +1470,27 @@ ] } }, + { + "name": "redundantLayerMergeAllInProvide", + "group": "style", + "description": "Replaces direct Layer.mergeAll arguments to Effect.provide with an array of layers", + "defaultSeverity": "suggestion", + "fixable": true, + "supportedEffect": [ + "v3", + "v4" + ], + "preview": { + "sourceText": "import { Effect, Layer } from \"effect\"\n\nexport const preview = Effect.void.pipe(\n Effect.provide(Layer.mergeAll(Layer.empty, Layer.empty))\n)\n", + "diagnostics": [ + { + "start": 98, + "end": 112, + "text": "`Effect.provide` accepts an array of Layers directly, so this `Layer.mergeAll` call is redundant. effect(redundantLayerMergeAllInProvide)" + } + ] + } + }, { "name": "redundantSchemaTagIdentifier", "group": "style", diff --git a/schema.json b/schema.json index 5f63b532..322cc958 100644 --- a/schema.json +++ b/schema.json @@ -2996,6 +2996,18 @@ "default": "off", "description": "Warns when reading process.env inside Effect generators instead of using Effect Config Default severity: off." }, + "redundantLayerMergeAllInProvide": { + "type": "string", + "enum": [ + "off", + "error", + "warning", + "message", + "suggestion" + ], + "default": "suggestion", + "description": "Replaces direct Layer.mergeAll arguments to Effect.provide with an array of layers Default severity: suggestion." + }, "redundantSchemaTagIdentifier": { "type": "string", "enum": [