@effect/language-service 0.56.0 → 0.57.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -0
- package/cli.js +283 -13
- package/cli.js.map +1 -1
- package/effect-lsp-patch-utils.js +287 -13
- package/effect-lsp-patch-utils.js.map +1 -1
- package/index.js +283 -13
- package/index.js.map +1 -1
- package/package.json +1 -1
- package/transform.js +287 -13
- package/transform.js.map +1 -1
package/index.js
CHANGED
|
@@ -2799,6 +2799,51 @@ function make3(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
|
|
|
2799
2799
|
if (!symbol3) return typeParserIssue("Node has no symbol", void 0, givenNode);
|
|
2800
2800
|
return isSymbolExportOfPackageModule(symbol3, packageName, memberName, isCorrectSourceFile);
|
|
2801
2801
|
};
|
|
2802
|
+
const findSymbolsMatchingPackageAndExportedName = (packageName, exportedSymbolName) => cachedBy(
|
|
2803
|
+
fn("TypeParser.findSymbolsMatchingPackageAndExportedName")(function* (_fromSourceFile) {
|
|
2804
|
+
const result = [];
|
|
2805
|
+
for (const sourceFile of program.getSourceFiles()) {
|
|
2806
|
+
const moduleSymbol = typeChecker.getSymbolAtLocation(sourceFile);
|
|
2807
|
+
if (!moduleSymbol) continue;
|
|
2808
|
+
const symbol3 = typeChecker.tryGetMemberInModuleExports(exportedSymbolName, moduleSymbol);
|
|
2809
|
+
if (!symbol3) continue;
|
|
2810
|
+
const packageInfo = yield* getSourceFilePackageInfo(sourceFile);
|
|
2811
|
+
if (!packageInfo || packageInfo.name.toLowerCase() !== packageName.toLowerCase()) continue;
|
|
2812
|
+
result.push([symbol3, sourceFile]);
|
|
2813
|
+
}
|
|
2814
|
+
return result;
|
|
2815
|
+
}),
|
|
2816
|
+
`TypeParser.findSymbolsMatchingPackageAndExportedName(${packageName}, ${exportedSymbolName})`,
|
|
2817
|
+
(sourceFile) => sourceFile
|
|
2818
|
+
);
|
|
2819
|
+
const isCauseTypeSourceFile = cachedBy(
|
|
2820
|
+
fn("TypeParser.isCauseTypeSourceFile")(function* (sourceFile) {
|
|
2821
|
+
const moduleSymbol = typeChecker.getSymbolAtLocation(sourceFile);
|
|
2822
|
+
if (!moduleSymbol) return yield* typeParserIssue("Node has no symbol", void 0, sourceFile);
|
|
2823
|
+
const causeTypeSymbol = typeChecker.tryGetMemberInModuleExports("Cause", moduleSymbol);
|
|
2824
|
+
if (!causeTypeSymbol) return yield* typeParserIssue("Cause type not found", void 0, sourceFile);
|
|
2825
|
+
const type = typeChecker.getDeclaredTypeOfSymbol(causeTypeSymbol);
|
|
2826
|
+
yield* pipeableType(type, sourceFile);
|
|
2827
|
+
return sourceFile;
|
|
2828
|
+
}),
|
|
2829
|
+
"TypeParser.isCauseTypeSourceFile",
|
|
2830
|
+
(sourceFile) => sourceFile
|
|
2831
|
+
);
|
|
2832
|
+
const effectCauseYieldableErrorTypes = cachedBy(
|
|
2833
|
+
fn("TypeParser.effectCauseYieldableErrorTypes")(function* (fromSourceFile) {
|
|
2834
|
+
const symbols = yield* findSymbolsMatchingPackageAndExportedName("effect", "YieldableError")(fromSourceFile);
|
|
2835
|
+
const result = [];
|
|
2836
|
+
for (const [symbol3, sourceFile] of symbols) {
|
|
2837
|
+
const causeFile = yield* isCauseTypeSourceFile(sourceFile);
|
|
2838
|
+
if (!causeFile) continue;
|
|
2839
|
+
const type = typeChecker.getDeclaredTypeOfSymbol(symbol3);
|
|
2840
|
+
result.push(type);
|
|
2841
|
+
}
|
|
2842
|
+
return result;
|
|
2843
|
+
}),
|
|
2844
|
+
"TypeParser.effectCauseYieldableErrorTypes",
|
|
2845
|
+
(fromSourceFile) => fromSourceFile
|
|
2846
|
+
);
|
|
2802
2847
|
function covariantTypeArgument(type) {
|
|
2803
2848
|
const signatures = typeChecker.getSignaturesOfType(type, ts.SignatureKind.Call);
|
|
2804
2849
|
if (signatures.length !== 1) {
|
|
@@ -3788,6 +3833,7 @@ function make3(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
|
|
|
3788
3833
|
effectGen,
|
|
3789
3834
|
effectFnUntracedGen,
|
|
3790
3835
|
effectFnGen,
|
|
3836
|
+
effectCauseYieldableErrorTypes,
|
|
3791
3837
|
unnecessaryEffectGen: unnecessaryEffectGen2,
|
|
3792
3838
|
effectSchemaType,
|
|
3793
3839
|
contextTag,
|
|
@@ -4098,8 +4144,93 @@ var accessors = createCodegen({
|
|
|
4098
4144
|
})
|
|
4099
4145
|
});
|
|
4100
4146
|
|
|
4147
|
+
// src/codegens/annotate.ts
|
|
4148
|
+
var annotate = createCodegen({
|
|
4149
|
+
name: "annotate",
|
|
4150
|
+
apply: fn("annotate.apply")(function* (sourceFile, textRange) {
|
|
4151
|
+
const ts = yield* service(TypeScriptApi);
|
|
4152
|
+
const tsUtils = yield* service(TypeScriptUtils);
|
|
4153
|
+
const typeChecker = yield* service(TypeCheckerApi);
|
|
4154
|
+
const typeCheckerUtils = yield* service(TypeCheckerUtils);
|
|
4155
|
+
const parse3 = (node) => gen(function* () {
|
|
4156
|
+
let variableDeclarations = [];
|
|
4157
|
+
const result = [];
|
|
4158
|
+
if (ts.isVariableStatement(node)) {
|
|
4159
|
+
variableDeclarations = [...variableDeclarations, ...node.declarationList.declarations];
|
|
4160
|
+
} else if (ts.isVariableDeclarationList(node)) {
|
|
4161
|
+
variableDeclarations = [...variableDeclarations, ...node.declarations];
|
|
4162
|
+
} else if (ts.isVariableDeclaration(node)) {
|
|
4163
|
+
variableDeclarations = [...variableDeclarations, node];
|
|
4164
|
+
}
|
|
4165
|
+
if (variableDeclarations.length === 0) {
|
|
4166
|
+
return yield* fail(new CodegenNotApplicableError("not a variable declaration"));
|
|
4167
|
+
}
|
|
4168
|
+
for (const variableDeclaration of variableDeclarations) {
|
|
4169
|
+
if (!variableDeclaration.initializer) continue;
|
|
4170
|
+
const initializerType = typeChecker.getTypeAtLocation(variableDeclaration.initializer);
|
|
4171
|
+
const initializerTypeNode = fromNullable(typeCheckerUtils.typeToSimplifiedTypeNode(
|
|
4172
|
+
initializerType,
|
|
4173
|
+
node,
|
|
4174
|
+
ts.NodeBuilderFlags.NoTruncation
|
|
4175
|
+
)).pipe(
|
|
4176
|
+
orElse(
|
|
4177
|
+
() => fromNullable(typeCheckerUtils.typeToSimplifiedTypeNode(
|
|
4178
|
+
initializerType,
|
|
4179
|
+
void 0,
|
|
4180
|
+
ts.NodeBuilderFlags.NoTruncation
|
|
4181
|
+
))
|
|
4182
|
+
),
|
|
4183
|
+
getOrUndefined
|
|
4184
|
+
);
|
|
4185
|
+
if (!initializerTypeNode) continue;
|
|
4186
|
+
const typeNodeString = typeChecker.typeToString(initializerType, void 0, ts.TypeFormatFlags.NoTruncation);
|
|
4187
|
+
const hash3 = cyrb53(typeNodeString);
|
|
4188
|
+
result.push({ variableDeclaration, initializerTypeNode, hash: hash3 });
|
|
4189
|
+
}
|
|
4190
|
+
if (result.length === 0) {
|
|
4191
|
+
return yield* fail(new CodegenNotApplicableError("no variable declarations with initializers"));
|
|
4192
|
+
}
|
|
4193
|
+
const hash2 = cyrb53(result.map((_) => _.hash).join("/"));
|
|
4194
|
+
return {
|
|
4195
|
+
hash: hash2,
|
|
4196
|
+
result
|
|
4197
|
+
};
|
|
4198
|
+
});
|
|
4199
|
+
const nodeAndCommentRange = tsUtils.findNodeWithLeadingCommentAtPosition(sourceFile, textRange.pos);
|
|
4200
|
+
if (!nodeAndCommentRange) return yield* fail(new CodegenNotApplicableError("no node and comment range"));
|
|
4201
|
+
return yield* pipe(
|
|
4202
|
+
parse3(nodeAndCommentRange.node),
|
|
4203
|
+
map5(
|
|
4204
|
+
(_) => ({
|
|
4205
|
+
hash: _.hash,
|
|
4206
|
+
description: "Annotate with type",
|
|
4207
|
+
apply: gen(function* () {
|
|
4208
|
+
const changeTracker = yield* service(ChangeTracker);
|
|
4209
|
+
for (const { initializerTypeNode, variableDeclaration } of _.result) {
|
|
4210
|
+
if (variableDeclaration.type) {
|
|
4211
|
+
changeTracker.deleteRange(sourceFile, {
|
|
4212
|
+
pos: variableDeclaration.name.end,
|
|
4213
|
+
end: variableDeclaration.type.end
|
|
4214
|
+
});
|
|
4215
|
+
}
|
|
4216
|
+
changeTracker.insertNodeAt(
|
|
4217
|
+
sourceFile,
|
|
4218
|
+
variableDeclaration.name.end,
|
|
4219
|
+
initializerTypeNode,
|
|
4220
|
+
{
|
|
4221
|
+
prefix: ": "
|
|
4222
|
+
}
|
|
4223
|
+
);
|
|
4224
|
+
}
|
|
4225
|
+
})
|
|
4226
|
+
})
|
|
4227
|
+
)
|
|
4228
|
+
);
|
|
4229
|
+
})
|
|
4230
|
+
});
|
|
4231
|
+
|
|
4101
4232
|
// src/codegens.ts
|
|
4102
|
-
var codegens = [accessors];
|
|
4233
|
+
var codegens = [accessors, annotate];
|
|
4103
4234
|
|
|
4104
4235
|
// src/completions/effectCodegensComment.ts
|
|
4105
4236
|
var effectCodegensComment = createCompletion({
|
|
@@ -8236,6 +8367,11 @@ var runEffectInsideEffect = createDiagnostic({
|
|
|
8236
8367
|
apply: fn("runEffectInsideEffect.apply")(function* (sourceFile, report) {
|
|
8237
8368
|
const ts = yield* service(TypeScriptApi);
|
|
8238
8369
|
const typeParser = yield* service(TypeParser);
|
|
8370
|
+
const tsUtils = yield* service(TypeScriptUtils);
|
|
8371
|
+
const parseEffectMethod = (node, methodName) => pipe(
|
|
8372
|
+
typeParser.isNodeReferenceToEffectModuleApi(methodName)(node),
|
|
8373
|
+
map5(() => ({ node, methodName }))
|
|
8374
|
+
);
|
|
8239
8375
|
const nodeToVisit = [];
|
|
8240
8376
|
const appendNodeToVisit = (node) => {
|
|
8241
8377
|
nodeToVisit.push(node);
|
|
@@ -8246,11 +8382,12 @@ var runEffectInsideEffect = createDiagnostic({
|
|
|
8246
8382
|
const node = nodeToVisit.shift();
|
|
8247
8383
|
ts.forEachChild(node, appendNodeToVisit);
|
|
8248
8384
|
if (!ts.isCallExpression(node)) continue;
|
|
8385
|
+
if (node.arguments.length === 0) continue;
|
|
8249
8386
|
const isEffectRunCall = yield* pipe(
|
|
8250
|
-
|
|
8251
|
-
orElse2(() =>
|
|
8252
|
-
orElse2(() =>
|
|
8253
|
-
orElse2(() =>
|
|
8387
|
+
parseEffectMethod(node.expression, "runPromise"),
|
|
8388
|
+
orElse2(() => parseEffectMethod(node.expression, "runSync")),
|
|
8389
|
+
orElse2(() => parseEffectMethod(node.expression, "runFork")),
|
|
8390
|
+
orElse2(() => parseEffectMethod(node.expression, "runCallback")),
|
|
8254
8391
|
option
|
|
8255
8392
|
);
|
|
8256
8393
|
if (isNone2(isEffectRunCall)) continue;
|
|
@@ -8270,18 +8407,90 @@ var runEffectInsideEffect = createDiagnostic({
|
|
|
8270
8407
|
orElse2(() => typeParser.effectFnGen(possiblyEffectGen)),
|
|
8271
8408
|
option
|
|
8272
8409
|
);
|
|
8273
|
-
if (isSome2(isInEffectGen)) {
|
|
8410
|
+
if (isSome2(isInEffectGen) && isInEffectGen.value.body.statements.length > 0) {
|
|
8274
8411
|
const nodeText = sourceFile.text.substring(
|
|
8275
8412
|
ts.getTokenPosOfNode(node.expression, sourceFile),
|
|
8276
8413
|
node.expression.end
|
|
8277
8414
|
);
|
|
8278
|
-
|
|
8279
|
-
|
|
8280
|
-
|
|
8281
|
-
|
|
8282
|
-
|
|
8283
|
-
|
|
8284
|
-
|
|
8415
|
+
if (nodeIntroduceScope && nodeIntroduceScope !== isInEffectGen.value.generatorFunction) {
|
|
8416
|
+
const fixAddRuntime = gen(function* () {
|
|
8417
|
+
const changeTracker = yield* service(ChangeTracker);
|
|
8418
|
+
const runtimeModuleIdentifier = tsUtils.findImportedModuleIdentifierByPackageAndNameOrBarrel(sourceFile, "effect", "Runtime") || "Runtime";
|
|
8419
|
+
const effectModuleIdentifier = tsUtils.findImportedModuleIdentifierByPackageAndNameOrBarrel(sourceFile, "effect", "Effect") || "Effect";
|
|
8420
|
+
let runtimeIdentifier = void 0;
|
|
8421
|
+
for (const statement of isInEffectGen.value.generatorFunction.body.statements) {
|
|
8422
|
+
if (ts.isVariableStatement(statement) && statement.declarationList.declarations.length === 1) {
|
|
8423
|
+
const declaration = statement.declarationList.declarations[0];
|
|
8424
|
+
if (declaration.initializer && ts.isYieldExpression(declaration.initializer) && declaration.initializer.asteriskToken && declaration.initializer.expression) {
|
|
8425
|
+
const yieldedExpression = declaration.initializer.expression;
|
|
8426
|
+
if (ts.isCallExpression(yieldedExpression)) {
|
|
8427
|
+
const maybeEffectRuntime = yield* pipe(
|
|
8428
|
+
typeParser.isNodeReferenceToEffectModuleApi("runtime")(yieldedExpression.expression),
|
|
8429
|
+
option
|
|
8430
|
+
);
|
|
8431
|
+
if (isSome2(maybeEffectRuntime) && ts.isIdentifier(declaration.name)) {
|
|
8432
|
+
runtimeIdentifier = ts.idText(declaration.name);
|
|
8433
|
+
}
|
|
8434
|
+
}
|
|
8435
|
+
}
|
|
8436
|
+
}
|
|
8437
|
+
}
|
|
8438
|
+
if (!runtimeIdentifier) {
|
|
8439
|
+
changeTracker.insertNodeAt(
|
|
8440
|
+
sourceFile,
|
|
8441
|
+
isInEffectGen.value.body.statements[0].pos,
|
|
8442
|
+
ts.factory.createVariableStatement(
|
|
8443
|
+
void 0,
|
|
8444
|
+
ts.factory.createVariableDeclarationList([ts.factory.createVariableDeclaration(
|
|
8445
|
+
"effectRuntime",
|
|
8446
|
+
void 0,
|
|
8447
|
+
void 0,
|
|
8448
|
+
ts.factory.createYieldExpression(
|
|
8449
|
+
ts.factory.createToken(ts.SyntaxKind.AsteriskToken),
|
|
8450
|
+
ts.factory.createCallExpression(
|
|
8451
|
+
ts.factory.createPropertyAccessExpression(
|
|
8452
|
+
ts.factory.createIdentifier(effectModuleIdentifier),
|
|
8453
|
+
"runtime"
|
|
8454
|
+
),
|
|
8455
|
+
[ts.factory.createTypeReferenceNode("never")],
|
|
8456
|
+
[]
|
|
8457
|
+
)
|
|
8458
|
+
)
|
|
8459
|
+
)], ts.NodeFlags.Const)
|
|
8460
|
+
),
|
|
8461
|
+
{
|
|
8462
|
+
prefix: "\n",
|
|
8463
|
+
suffix: "\n"
|
|
8464
|
+
}
|
|
8465
|
+
);
|
|
8466
|
+
}
|
|
8467
|
+
changeTracker.deleteRange(sourceFile, {
|
|
8468
|
+
pos: ts.getTokenPosOfNode(node.expression, sourceFile),
|
|
8469
|
+
end: node.arguments[0].pos
|
|
8470
|
+
});
|
|
8471
|
+
changeTracker.insertText(
|
|
8472
|
+
sourceFile,
|
|
8473
|
+
node.arguments[0].pos,
|
|
8474
|
+
`${runtimeModuleIdentifier}.${isEffectRunCall.value.methodName}(${runtimeIdentifier || "effectRuntime"}, `
|
|
8475
|
+
);
|
|
8476
|
+
});
|
|
8477
|
+
report({
|
|
8478
|
+
location: node.expression,
|
|
8479
|
+
messageText: `Using ${nodeText} inside an Effect is not recommended. The same runtime should generally be used instead to run child effects.
|
|
8480
|
+
Consider extracting the Runtime by using for example Effect.runtime and then use Runtime.${isEffectRunCall.value.methodName} with the extracted runtime instead.`,
|
|
8481
|
+
fixes: [{
|
|
8482
|
+
fixName: "runEffectInsideEffect_fix",
|
|
8483
|
+
description: "Use a runtime to run the Effect",
|
|
8484
|
+
apply: fixAddRuntime
|
|
8485
|
+
}]
|
|
8486
|
+
});
|
|
8487
|
+
} else {
|
|
8488
|
+
report({
|
|
8489
|
+
location: node.expression,
|
|
8490
|
+
messageText: `Using ${nodeText} inside an Effect is not recommended. Effects inside generators can usually just be yielded.`,
|
|
8491
|
+
fixes: []
|
|
8492
|
+
});
|
|
8493
|
+
}
|
|
8285
8494
|
}
|
|
8286
8495
|
currentParent = currentParent.parent;
|
|
8287
8496
|
}
|
|
@@ -8711,6 +8920,66 @@ var unnecessaryEffectGen = createDiagnostic({
|
|
|
8711
8920
|
})
|
|
8712
8921
|
});
|
|
8713
8922
|
|
|
8923
|
+
// src/diagnostics/unnecessaryFailYieldableError.ts
|
|
8924
|
+
var unnecessaryFailYieldableError = createDiagnostic({
|
|
8925
|
+
name: "unnecessaryFailYieldableError",
|
|
8926
|
+
code: 29,
|
|
8927
|
+
severity: "suggestion",
|
|
8928
|
+
apply: fn("unnecessaryFailYieldableError.apply")(function* (sourceFile, report) {
|
|
8929
|
+
const ts = yield* service(TypeScriptApi);
|
|
8930
|
+
const typeParser = yield* service(TypeParser);
|
|
8931
|
+
const typeChecker = yield* service(TypeCheckerApi);
|
|
8932
|
+
const yieldableErrorTypes = yield* pipe(
|
|
8933
|
+
typeParser.effectCauseYieldableErrorTypes(sourceFile),
|
|
8934
|
+
orElse2(() => succeed([]))
|
|
8935
|
+
);
|
|
8936
|
+
const nodeToVisit = [];
|
|
8937
|
+
const appendNodeToVisit = (node) => {
|
|
8938
|
+
nodeToVisit.push(node);
|
|
8939
|
+
return void 0;
|
|
8940
|
+
};
|
|
8941
|
+
ts.forEachChild(sourceFile, appendNodeToVisit);
|
|
8942
|
+
while (nodeToVisit.length > 0) {
|
|
8943
|
+
const node = nodeToVisit.shift();
|
|
8944
|
+
ts.forEachChild(node, appendNodeToVisit);
|
|
8945
|
+
if (ts.isYieldExpression(node) && node.asteriskToken && node.expression && ts.isCallExpression(node.expression)) {
|
|
8946
|
+
const callExpression = node.expression;
|
|
8947
|
+
yield* pipe(
|
|
8948
|
+
typeParser.isNodeReferenceToEffectModuleApi("fail")(callExpression.expression),
|
|
8949
|
+
map5(() => {
|
|
8950
|
+
if (callExpression.arguments.length > 0) {
|
|
8951
|
+
const failArgument = callExpression.arguments[0];
|
|
8952
|
+
const argumentType = typeChecker.getTypeAtLocation(failArgument);
|
|
8953
|
+
const isYieldableError = yieldableErrorTypes.some(
|
|
8954
|
+
(yieldableType) => typeChecker.isTypeAssignableTo(argumentType, yieldableType)
|
|
8955
|
+
);
|
|
8956
|
+
if (isYieldableError) {
|
|
8957
|
+
report({
|
|
8958
|
+
location: node,
|
|
8959
|
+
messageText: `This Effect.fail call uses a yieldable error type as argument. You can yield* the error directly instead.`,
|
|
8960
|
+
fixes: [{
|
|
8961
|
+
fixName: "unnecessaryFailYieldableError_fix",
|
|
8962
|
+
description: "Replace yield* Effect.fail with yield*",
|
|
8963
|
+
apply: gen(function* () {
|
|
8964
|
+
const changeTracker = yield* service(ChangeTracker);
|
|
8965
|
+
changeTracker.replaceNode(
|
|
8966
|
+
sourceFile,
|
|
8967
|
+
callExpression,
|
|
8968
|
+
failArgument
|
|
8969
|
+
);
|
|
8970
|
+
})
|
|
8971
|
+
}]
|
|
8972
|
+
});
|
|
8973
|
+
}
|
|
8974
|
+
}
|
|
8975
|
+
}),
|
|
8976
|
+
ignore
|
|
8977
|
+
);
|
|
8978
|
+
}
|
|
8979
|
+
}
|
|
8980
|
+
})
|
|
8981
|
+
});
|
|
8982
|
+
|
|
8714
8983
|
// src/diagnostics/unnecessaryPipe.ts
|
|
8715
8984
|
var unnecessaryPipe = createDiagnostic({
|
|
8716
8985
|
name: "unnecessaryPipe",
|
|
@@ -8898,6 +9167,7 @@ var diagnostics = [
|
|
|
8898
9167
|
floatingEffect,
|
|
8899
9168
|
missingStarInYieldEffectGen,
|
|
8900
9169
|
unnecessaryEffectGen,
|
|
9170
|
+
unnecessaryFailYieldableError,
|
|
8901
9171
|
missingReturnYieldStar,
|
|
8902
9172
|
leakingRequirements,
|
|
8903
9173
|
unnecessaryPipe,
|