@effect/language-service 0.84.0 → 0.84.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/cli.js +91 -16
- package/cli.js.map +1 -1
- package/effect-lsp-patch-utils.js +86 -12
- package/effect-lsp-patch-utils.js.map +1 -1
- package/index.js +86 -12
- package/index.js.map +1 -1
- package/package.json +1 -1
- package/schema.json +1 -1
- package/transform.js +86 -12
- package/transform.js.map +1 -1
package/README.md
CHANGED
|
@@ -81,7 +81,7 @@ Some diagnostics are off by default or have a default severity of suggestion, bu
|
|
|
81
81
|
<tr><td><code>leakingRequirements</code></td><td>💡</td><td></td><td>Detects implementation services leaked in service methods</td><td>✓</td><td>✓</td></tr>
|
|
82
82
|
<tr><td><code>multipleEffectProvide</code></td><td>⚠️</td><td>🔧</td><td>Warns against chaining Effect.provide calls which can cause service lifecycle issues</td><td>✓</td><td>✓</td></tr>
|
|
83
83
|
<tr><td><code>returnEffectInGen</code></td><td>💡</td><td>🔧</td><td>Warns when returning an Effect in a generator causes nested Effect<Effect<...>></td><td>✓</td><td>✓</td></tr>
|
|
84
|
-
<tr><td><code>runEffectInsideEffect</code></td><td>💡</td><td>🔧</td><td>Suggests using Runtime methods instead of Effect.run* inside Effect contexts</td><td>✓</td><td
|
|
84
|
+
<tr><td><code>runEffectInsideEffect</code></td><td>💡</td><td>🔧</td><td>Suggests using Runtime or Effect.run*With methods instead of Effect.run* inside Effect contexts</td><td>✓</td><td>✓</td></tr>
|
|
85
85
|
<tr><td><code>schemaSyncInEffect</code></td><td>💡</td><td></td><td>Suggests using Effect-based Schema methods instead of sync methods inside Effect generators</td><td>✓</td><td></td></tr>
|
|
86
86
|
<tr><td><code>scopeInLayerEffect</code></td><td>⚠️</td><td>🔧</td><td>Suggests using Layer.scoped instead of Layer.effect when Scope is in requirements</td><td>✓</td><td></td></tr>
|
|
87
87
|
<tr><td><code>strictEffectProvide</code></td><td>➖</td><td></td><td>Warns when using Effect.provide with layers outside of application entry points</td><td>✓</td><td>✓</td></tr>
|
package/cli.js
CHANGED
|
@@ -25942,7 +25942,7 @@ var runWith2 = (command, config2) => {
|
|
|
25942
25942
|
// package.json
|
|
25943
25943
|
var package_default = {
|
|
25944
25944
|
name: "@effect/language-service",
|
|
25945
|
-
version: "0.84.
|
|
25945
|
+
version: "0.84.2",
|
|
25946
25946
|
publishConfig: {
|
|
25947
25947
|
access: "public",
|
|
25948
25948
|
directory: "dist"
|
|
@@ -28665,10 +28665,42 @@ function makeTypeCheckerUtils(ts, typeChecker, tsUtils) {
|
|
|
28665
28665
|
if (node.parent && ts.isJsxOpeningElement(node.parent) && node.parent.tagName === node) return;
|
|
28666
28666
|
if (node.parent && ts.isJsxClosingElement(node.parent) && node.parent.tagName === node) return;
|
|
28667
28667
|
if (node.parent && ts.isJsxAttribute(node.parent) && node.parent.name === node) return;
|
|
28668
|
+
if (isInsideTypeOnlyHeritageExpression(node)) return;
|
|
28668
28669
|
if (ts.isExpression(node) || ts.isTypeNode(node)) {
|
|
28669
28670
|
return typeChecker.getTypeAtLocation(node);
|
|
28670
28671
|
}
|
|
28671
28672
|
}
|
|
28673
|
+
function isInsideTypeOnlyHeritageExpression(node) {
|
|
28674
|
+
if (ts.isExpressionWithTypeArguments(node)) {
|
|
28675
|
+
return isTypeOnlyHeritageClause(node.parent);
|
|
28676
|
+
}
|
|
28677
|
+
if (!ts.isIdentifier(node) && !ts.isPropertyAccessExpression(node)) {
|
|
28678
|
+
return false;
|
|
28679
|
+
}
|
|
28680
|
+
for (let current = node.parent; current; current = current.parent) {
|
|
28681
|
+
if (ts.isPropertyAccessExpression(current)) {
|
|
28682
|
+
continue;
|
|
28683
|
+
}
|
|
28684
|
+
if (ts.isExpressionWithTypeArguments(current)) {
|
|
28685
|
+
return isTypeOnlyHeritageClause(current.parent);
|
|
28686
|
+
}
|
|
28687
|
+
return false;
|
|
28688
|
+
}
|
|
28689
|
+
return false;
|
|
28690
|
+
}
|
|
28691
|
+
function isTypeOnlyHeritageClause(node) {
|
|
28692
|
+
if (!node || !ts.isHeritageClause(node)) {
|
|
28693
|
+
return false;
|
|
28694
|
+
}
|
|
28695
|
+
const container = node.parent;
|
|
28696
|
+
if (!container) {
|
|
28697
|
+
return false;
|
|
28698
|
+
}
|
|
28699
|
+
if (ts.isInterfaceDeclaration(container)) {
|
|
28700
|
+
return true;
|
|
28701
|
+
}
|
|
28702
|
+
return ts.isClassLike(container) && node.token === ts.SyntaxKind.ImplementsKeyword;
|
|
28703
|
+
}
|
|
28672
28704
|
function resolveToGlobalSymbol(symbol4) {
|
|
28673
28705
|
if (symbol4.flags & ts.SymbolFlags.Alias) {
|
|
28674
28706
|
symbol4 = typeChecker.getAliasedSymbol(symbol4);
|
|
@@ -33935,12 +33967,14 @@ var getParameterName = (typescript, name) => {
|
|
|
33935
33967
|
}
|
|
33936
33968
|
return "parameter";
|
|
33937
33969
|
};
|
|
33938
|
-
var hasOuterContextualFunctionType = (typescript, typeChecker, node) => {
|
|
33970
|
+
var hasOuterContextualFunctionType = (typescript, typeChecker, typeCheckerUtils, node) => {
|
|
33939
33971
|
const contextualType = typeChecker.getContextualType(node);
|
|
33940
33972
|
if (!contextualType) {
|
|
33941
33973
|
return false;
|
|
33942
33974
|
}
|
|
33943
|
-
return
|
|
33975
|
+
return typeCheckerUtils.unrollUnionMembers(contextualType).some(
|
|
33976
|
+
(type) => typeChecker.getSignaturesOfType(type, typescript.SignatureKind.Call).length > 0
|
|
33977
|
+
);
|
|
33944
33978
|
};
|
|
33945
33979
|
var effectFnImplicitAny = createDiagnostic({
|
|
33946
33980
|
name: "effectFnImplicitAny",
|
|
@@ -33954,6 +33988,7 @@ var effectFnImplicitAny = createDiagnostic({
|
|
|
33954
33988
|
const ts = yield* service2(TypeScriptApi);
|
|
33955
33989
|
const program = yield* service2(TypeScriptProgram);
|
|
33956
33990
|
const typeChecker = yield* service2(TypeCheckerApi);
|
|
33991
|
+
const typeCheckerUtils = yield* service2(TypeCheckerUtils);
|
|
33957
33992
|
const typeParser = yield* service2(TypeParser);
|
|
33958
33993
|
const noImplicitAny = program.getCompilerOptions().noImplicitAny ?? program.getCompilerOptions().strict ?? false;
|
|
33959
33994
|
if (!noImplicitAny) {
|
|
@@ -33993,7 +34028,7 @@ var effectFnImplicitAny = createDiagnostic({
|
|
|
33993
34028
|
),
|
|
33994
34029
|
orUndefined
|
|
33995
34030
|
);
|
|
33996
|
-
if (!parsed || hasOuterContextualFunctionType(ts, typeChecker, parsed.call)) {
|
|
34031
|
+
if (!parsed || hasOuterContextualFunctionType(ts, typeChecker, typeCheckerUtils, parsed.call)) {
|
|
33997
34032
|
continue;
|
|
33998
34033
|
}
|
|
33999
34034
|
for (const parameter of parsed.fn.parameters) {
|
|
@@ -38088,16 +38123,16 @@ Nested Effect-able types may be intended if you plan to later manually flatten o
|
|
|
38088
38123
|
var runEffectInsideEffect = createDiagnostic({
|
|
38089
38124
|
name: "runEffectInsideEffect",
|
|
38090
38125
|
code: 32,
|
|
38091
|
-
description: "Suggests using Runtime methods instead of Effect.run* inside Effect contexts",
|
|
38126
|
+
description: "Suggests using Runtime or Effect.run*With methods instead of Effect.run* inside Effect contexts",
|
|
38092
38127
|
group: "antipattern",
|
|
38093
38128
|
severity: "suggestion",
|
|
38094
38129
|
fixable: true,
|
|
38095
|
-
supportedEffect: ["v3"],
|
|
38130
|
+
supportedEffect: ["v3", "v4"],
|
|
38096
38131
|
apply: fn3("runEffectInsideEffect.apply")(function* (sourceFile, report) {
|
|
38097
38132
|
const ts = yield* service2(TypeScriptApi);
|
|
38098
38133
|
const typeParser = yield* service2(TypeParser);
|
|
38099
38134
|
const tsUtils = yield* service2(TypeScriptUtils);
|
|
38100
|
-
|
|
38135
|
+
const supportedEffect = typeParser.supportedEffect();
|
|
38101
38136
|
const parseEffectMethod = (node, methodName) => pipe(
|
|
38102
38137
|
typeParser.isNodeReferenceToEffectModuleApi(methodName)(node),
|
|
38103
38138
|
map12(() => ({ node, methodName }))
|
|
@@ -38130,9 +38165,10 @@ var runEffectInsideEffect = createDiagnostic({
|
|
|
38130
38165
|
if (scopeNode && scopeNode !== effectGen.generatorFunction) {
|
|
38131
38166
|
const fixAddRuntime = gen3(function* () {
|
|
38132
38167
|
const changeTracker = yield* service2(ChangeTracker);
|
|
38133
|
-
const runtimeModuleIdentifier = tsUtils.findImportedModuleIdentifierByPackageAndNameOrBarrel(sourceFile, "effect", "Runtime") || "Runtime";
|
|
38134
38168
|
const effectModuleIdentifier = tsUtils.findImportedModuleIdentifierByPackageAndNameOrBarrel(sourceFile, "effect", "Effect") || "Effect";
|
|
38169
|
+
const runtimeModuleIdentifier = tsUtils.findImportedModuleIdentifierByPackageAndNameOrBarrel(sourceFile, "effect", "Runtime") || "Runtime";
|
|
38135
38170
|
let runtimeIdentifier = void 0;
|
|
38171
|
+
let servicesIdentifier = void 0;
|
|
38136
38172
|
for (const statement of effectGen.generatorFunction.body.statements) {
|
|
38137
38173
|
if (ts.isVariableStatement(statement) && statement.declarationList.declarations.length === 1) {
|
|
38138
38174
|
const declaration = statement.declarationList.declarations[0];
|
|
@@ -38146,11 +38182,46 @@ var runEffectInsideEffect = createDiagnostic({
|
|
|
38146
38182
|
if (isSome2(maybeEffectRuntime) && ts.isIdentifier(declaration.name)) {
|
|
38147
38183
|
runtimeIdentifier = ts.idText(declaration.name);
|
|
38148
38184
|
}
|
|
38185
|
+
const maybeEffectServices = yield* pipe(
|
|
38186
|
+
typeParser.isNodeReferenceToEffectModuleApi("services")(yieldedExpression.expression),
|
|
38187
|
+
option4
|
|
38188
|
+
);
|
|
38189
|
+
if (isSome2(maybeEffectServices) && ts.isIdentifier(declaration.name)) {
|
|
38190
|
+
servicesIdentifier = ts.idText(declaration.name);
|
|
38191
|
+
}
|
|
38149
38192
|
}
|
|
38150
38193
|
}
|
|
38151
38194
|
}
|
|
38152
38195
|
}
|
|
38153
|
-
if (!
|
|
38196
|
+
if (supportedEffect === "v4" && !servicesIdentifier) {
|
|
38197
|
+
changeTracker.insertNodeAt(
|
|
38198
|
+
sourceFile,
|
|
38199
|
+
effectGen.body.statements[0].pos,
|
|
38200
|
+
ts.factory.createVariableStatement(
|
|
38201
|
+
void 0,
|
|
38202
|
+
ts.factory.createVariableDeclarationList([ts.factory.createVariableDeclaration(
|
|
38203
|
+
"effectServices",
|
|
38204
|
+
void 0,
|
|
38205
|
+
void 0,
|
|
38206
|
+
ts.factory.createYieldExpression(
|
|
38207
|
+
ts.factory.createToken(ts.SyntaxKind.AsteriskToken),
|
|
38208
|
+
ts.factory.createCallExpression(
|
|
38209
|
+
ts.factory.createPropertyAccessExpression(
|
|
38210
|
+
ts.factory.createIdentifier(effectModuleIdentifier),
|
|
38211
|
+
"services"
|
|
38212
|
+
),
|
|
38213
|
+
[ts.factory.createKeywordTypeNode(ts.SyntaxKind.NeverKeyword)],
|
|
38214
|
+
[]
|
|
38215
|
+
)
|
|
38216
|
+
)
|
|
38217
|
+
)], ts.NodeFlags.Const)
|
|
38218
|
+
),
|
|
38219
|
+
{
|
|
38220
|
+
prefix: "\n",
|
|
38221
|
+
suffix: "\n"
|
|
38222
|
+
}
|
|
38223
|
+
);
|
|
38224
|
+
} else if (supportedEffect === "v3" && !runtimeIdentifier) {
|
|
38154
38225
|
changeTracker.insertNodeAt(
|
|
38155
38226
|
sourceFile,
|
|
38156
38227
|
effectGen.body.statements[0].pos,
|
|
@@ -38186,16 +38257,19 @@ var runEffectInsideEffect = createDiagnostic({
|
|
|
38186
38257
|
changeTracker.insertText(
|
|
38187
38258
|
sourceFile,
|
|
38188
38259
|
node.arguments[0].pos,
|
|
38189
|
-
`${runtimeModuleIdentifier}.${isEffectRunCall.value.methodName}(${runtimeIdentifier || "effectRuntime"}, `
|
|
38260
|
+
supportedEffect === "v4" ? `${effectModuleIdentifier}.${isEffectRunCall.value.methodName}With(${servicesIdentifier || "effectServices"})(` : `${runtimeModuleIdentifier}.${isEffectRunCall.value.methodName}(${runtimeIdentifier || "effectRuntime"}, `
|
|
38190
38261
|
);
|
|
38191
38262
|
});
|
|
38263
|
+
const v4MethodName = `${isEffectRunCall.value.methodName}With`;
|
|
38264
|
+
const messageText = supportedEffect === "v4" ? `Using ${nodeText} inside an Effect is not recommended. The same services should generally be used instead to run child effects.
|
|
38265
|
+
Consider extracting the current services by using for example Effect.services and then use Effect.${v4MethodName} with the extracted services instead.` : `Using ${nodeText} inside an Effect is not recommended. The same runtime should generally be used instead to run child effects.
|
|
38266
|
+
Consider extracting the Runtime by using for example Effect.runtime and then use Runtime.${isEffectRunCall.value.methodName} with the extracted runtime instead.`;
|
|
38192
38267
|
report({
|
|
38193
38268
|
location: node.expression,
|
|
38194
|
-
messageText
|
|
38195
|
-
Consider extracting the Runtime by using for example Effect.runtime and then use Runtime.${isEffectRunCall.value.methodName} with the extracted runtime instead.`,
|
|
38269
|
+
messageText,
|
|
38196
38270
|
fixes: [{
|
|
38197
38271
|
fixName: "runEffectInsideEffect_fix",
|
|
38198
|
-
description: "Use a runtime to run the Effect",
|
|
38272
|
+
description: supportedEffect === "v4" ? "Use the current services to run the Effect" : "Use a runtime to run the Effect",
|
|
38199
38273
|
apply: fixAddRuntime
|
|
38200
38274
|
}]
|
|
38201
38275
|
});
|
|
@@ -39861,11 +39935,12 @@ var metadata_default = {
|
|
|
39861
39935
|
{
|
|
39862
39936
|
name: "runEffectInsideEffect",
|
|
39863
39937
|
group: "antipattern",
|
|
39864
|
-
description: "Suggests using Runtime methods instead of Effect.run* inside Effect contexts",
|
|
39938
|
+
description: "Suggests using Runtime or Effect.run*With methods instead of Effect.run* inside Effect contexts",
|
|
39865
39939
|
defaultSeverity: "suggestion",
|
|
39866
39940
|
fixable: true,
|
|
39867
39941
|
supportedEffect: [
|
|
39868
|
-
"v3"
|
|
39942
|
+
"v3",
|
|
39943
|
+
"v4"
|
|
39869
39944
|
],
|
|
39870
39945
|
preview: {
|
|
39871
39946
|
sourceText: 'import { Effect } from "effect"\n\nexport const preview = Effect.gen(function*() {\n const run = () => Effect.runSync(Effect.succeed(1))\n return run()\n})\n',
|
|
@@ -39873,7 +39948,7 @@ var metadata_default = {
|
|
|
39873
39948
|
{
|
|
39874
39949
|
start: 101,
|
|
39875
39950
|
end: 115,
|
|
39876
|
-
text: "Using Effect.runSync inside an Effect is not recommended. The same
|
|
39951
|
+
text: "Using Effect.runSync inside an Effect is not recommended. The same services should generally be used instead to run child effects.\nConsider extracting the current services by using for example Effect.services and then use Effect.runSyncWith with the extracted services instead. effect(runEffectInsideEffect)"
|
|
39877
39952
|
}
|
|
39878
39953
|
]
|
|
39879
39954
|
}
|