@effect/language-service 0.56.0 → 0.57.0
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 +193 -1
- package/cli.js.map +1 -1
- package/effect-lsp-patch-utils.js +197 -1
- package/effect-lsp-patch-utils.js.map +1 -1
- package/index.js +193 -1
- package/index.js.map +1 -1
- package/package.json +1 -1
- package/transform.js +197 -1
- package/transform.js.map +1 -1
package/README.md
CHANGED
|
@@ -72,6 +72,7 @@ And you're done! You'll now be able to use a set of refactors and diagnostics th
|
|
|
72
72
|
- Warn when catch callbacks in `Effect.tryPromise`, `Effect.tryMap`, or `Effect.tryMapPromise` return `unknown` or `any` types
|
|
73
73
|
- Warn when using `Effect.runSync`, `Effect.runPromise`, `Effect.runFork`, or `Effect.runCallback` inside an Effect
|
|
74
74
|
- Warn when using `Schema.Union` with multiple `Schema.Literal` calls that can be simplified to a single `Schema.Literal` call
|
|
75
|
+
- Warn when using `yield* Effect.fail()` with yieldable error types that can be yielded directly
|
|
75
76
|
|
|
76
77
|
### Completions
|
|
77
78
|
|
|
@@ -99,6 +100,11 @@ And you're done! You'll now be able to use a set of refactors and diagnostics th
|
|
|
99
100
|
- Toggle between pipe styles `X.pipe(Y)` and `pipe(X, Y)`
|
|
100
101
|
- Layer Magic: Automatically compose and build layers based on service dependencies
|
|
101
102
|
|
|
103
|
+
### Codegens
|
|
104
|
+
|
|
105
|
+
- Automatically adds type annotations to exported constants based on their initializer types using `// @effect-codegens annotate`
|
|
106
|
+
- Automatically implements service accessors in `Effect.Service`, `Context.Tag` or `Effect.Tag` declarations using `// @effect-codegens accessors`
|
|
107
|
+
|
|
102
108
|
### Miscellaneous
|
|
103
109
|
- Renaming a class name, will rename the identifier as well for TaggedError, TaggedClass, etc...
|
|
104
110
|
- "Go to definition" for RpcClient will resolve to the Rpc definition
|
package/cli.js
CHANGED
|
@@ -32083,6 +32083,51 @@ function make64(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
|
|
|
32083
32083
|
if (!symbol3) return typeParserIssue("Node has no symbol", void 0, givenNode);
|
|
32084
32084
|
return isSymbolExportOfPackageModule(symbol3, packageName, memberName, isCorrectSourceFile);
|
|
32085
32085
|
};
|
|
32086
|
+
const findSymbolsMatchingPackageAndExportedName = (packageName, exportedSymbolName) => cachedBy(
|
|
32087
|
+
fn2("TypeParser.findSymbolsMatchingPackageAndExportedName")(function* (_fromSourceFile) {
|
|
32088
|
+
const result = [];
|
|
32089
|
+
for (const sourceFile of program.getSourceFiles()) {
|
|
32090
|
+
const moduleSymbol = typeChecker.getSymbolAtLocation(sourceFile);
|
|
32091
|
+
if (!moduleSymbol) continue;
|
|
32092
|
+
const symbol3 = typeChecker.tryGetMemberInModuleExports(exportedSymbolName, moduleSymbol);
|
|
32093
|
+
if (!symbol3) continue;
|
|
32094
|
+
const packageInfo = yield* getSourceFilePackageInfo(sourceFile);
|
|
32095
|
+
if (!packageInfo || packageInfo.name.toLowerCase() !== packageName.toLowerCase()) continue;
|
|
32096
|
+
result.push([symbol3, sourceFile]);
|
|
32097
|
+
}
|
|
32098
|
+
return result;
|
|
32099
|
+
}),
|
|
32100
|
+
`TypeParser.findSymbolsMatchingPackageAndExportedName(${packageName}, ${exportedSymbolName})`,
|
|
32101
|
+
(sourceFile) => sourceFile
|
|
32102
|
+
);
|
|
32103
|
+
const isCauseTypeSourceFile = cachedBy(
|
|
32104
|
+
fn2("TypeParser.isCauseTypeSourceFile")(function* (sourceFile) {
|
|
32105
|
+
const moduleSymbol = typeChecker.getSymbolAtLocation(sourceFile);
|
|
32106
|
+
if (!moduleSymbol) return yield* typeParserIssue("Node has no symbol", void 0, sourceFile);
|
|
32107
|
+
const causeTypeSymbol = typeChecker.tryGetMemberInModuleExports("Cause", moduleSymbol);
|
|
32108
|
+
if (!causeTypeSymbol) return yield* typeParserIssue("Cause type not found", void 0, sourceFile);
|
|
32109
|
+
const type2 = typeChecker.getDeclaredTypeOfSymbol(causeTypeSymbol);
|
|
32110
|
+
yield* pipeableType(type2, sourceFile);
|
|
32111
|
+
return sourceFile;
|
|
32112
|
+
}),
|
|
32113
|
+
"TypeParser.isCauseTypeSourceFile",
|
|
32114
|
+
(sourceFile) => sourceFile
|
|
32115
|
+
);
|
|
32116
|
+
const effectCauseYieldableErrorTypes = cachedBy(
|
|
32117
|
+
fn2("TypeParser.effectCauseYieldableErrorTypes")(function* (fromSourceFile) {
|
|
32118
|
+
const symbols = yield* findSymbolsMatchingPackageAndExportedName("effect", "YieldableError")(fromSourceFile);
|
|
32119
|
+
const result = [];
|
|
32120
|
+
for (const [symbol3, sourceFile] of symbols) {
|
|
32121
|
+
const causeFile = yield* isCauseTypeSourceFile(sourceFile);
|
|
32122
|
+
if (!causeFile) continue;
|
|
32123
|
+
const type2 = typeChecker.getDeclaredTypeOfSymbol(symbol3);
|
|
32124
|
+
result.push(type2);
|
|
32125
|
+
}
|
|
32126
|
+
return result;
|
|
32127
|
+
}),
|
|
32128
|
+
"TypeParser.effectCauseYieldableErrorTypes",
|
|
32129
|
+
(fromSourceFile) => fromSourceFile
|
|
32130
|
+
);
|
|
32086
32131
|
function covariantTypeArgument(type2) {
|
|
32087
32132
|
const signatures = typeChecker.getSignaturesOfType(type2, ts.SignatureKind.Call);
|
|
32088
32133
|
if (signatures.length !== 1) {
|
|
@@ -33072,6 +33117,7 @@ function make64(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
|
|
|
33072
33117
|
effectGen,
|
|
33073
33118
|
effectFnUntracedGen,
|
|
33074
33119
|
effectFnGen,
|
|
33120
|
+
effectCauseYieldableErrorTypes,
|
|
33075
33121
|
unnecessaryEffectGen: unnecessaryEffectGen2,
|
|
33076
33122
|
effectSchemaType,
|
|
33077
33123
|
contextTag,
|
|
@@ -33382,8 +33428,93 @@ var accessors = createCodegen({
|
|
|
33382
33428
|
})
|
|
33383
33429
|
});
|
|
33384
33430
|
|
|
33431
|
+
// src/codegens/annotate.ts
|
|
33432
|
+
var annotate3 = createCodegen({
|
|
33433
|
+
name: "annotate",
|
|
33434
|
+
apply: fn2("annotate.apply")(function* (sourceFile, textRange) {
|
|
33435
|
+
const ts = yield* service2(TypeScriptApi);
|
|
33436
|
+
const tsUtils = yield* service2(TypeScriptUtils);
|
|
33437
|
+
const typeChecker = yield* service2(TypeCheckerApi);
|
|
33438
|
+
const typeCheckerUtils = yield* service2(TypeCheckerUtils);
|
|
33439
|
+
const parse6 = (node) => gen3(function* () {
|
|
33440
|
+
let variableDeclarations = [];
|
|
33441
|
+
const result = [];
|
|
33442
|
+
if (ts.isVariableStatement(node)) {
|
|
33443
|
+
variableDeclarations = [...variableDeclarations, ...node.declarationList.declarations];
|
|
33444
|
+
} else if (ts.isVariableDeclarationList(node)) {
|
|
33445
|
+
variableDeclarations = [...variableDeclarations, ...node.declarations];
|
|
33446
|
+
} else if (ts.isVariableDeclaration(node)) {
|
|
33447
|
+
variableDeclarations = [...variableDeclarations, node];
|
|
33448
|
+
}
|
|
33449
|
+
if (variableDeclarations.length === 0) {
|
|
33450
|
+
return yield* fail18(new CodegenNotApplicableError("not a variable declaration"));
|
|
33451
|
+
}
|
|
33452
|
+
for (const variableDeclaration of variableDeclarations) {
|
|
33453
|
+
if (!variableDeclaration.initializer) continue;
|
|
33454
|
+
const initializerType = typeChecker.getTypeAtLocation(variableDeclaration.initializer);
|
|
33455
|
+
const initializerTypeNode = fromNullable(typeCheckerUtils.typeToSimplifiedTypeNode(
|
|
33456
|
+
initializerType,
|
|
33457
|
+
node,
|
|
33458
|
+
ts.NodeBuilderFlags.NoTruncation
|
|
33459
|
+
)).pipe(
|
|
33460
|
+
orElse(
|
|
33461
|
+
() => fromNullable(typeCheckerUtils.typeToSimplifiedTypeNode(
|
|
33462
|
+
initializerType,
|
|
33463
|
+
void 0,
|
|
33464
|
+
ts.NodeBuilderFlags.NoTruncation
|
|
33465
|
+
))
|
|
33466
|
+
),
|
|
33467
|
+
getOrUndefined
|
|
33468
|
+
);
|
|
33469
|
+
if (!initializerTypeNode) continue;
|
|
33470
|
+
const typeNodeString = typeChecker.typeToString(initializerType, void 0, ts.TypeFormatFlags.NoTruncation);
|
|
33471
|
+
const hash3 = cyrb53(typeNodeString);
|
|
33472
|
+
result.push({ variableDeclaration, initializerTypeNode, hash: hash3 });
|
|
33473
|
+
}
|
|
33474
|
+
if (result.length === 0) {
|
|
33475
|
+
return yield* fail18(new CodegenNotApplicableError("no variable declarations with initializers"));
|
|
33476
|
+
}
|
|
33477
|
+
const hash2 = cyrb53(result.map((_) => _.hash).join("/"));
|
|
33478
|
+
return {
|
|
33479
|
+
hash: hash2,
|
|
33480
|
+
result
|
|
33481
|
+
};
|
|
33482
|
+
});
|
|
33483
|
+
const nodeAndCommentRange = tsUtils.findNodeWithLeadingCommentAtPosition(sourceFile, textRange.pos);
|
|
33484
|
+
if (!nodeAndCommentRange) return yield* fail18(new CodegenNotApplicableError("no node and comment range"));
|
|
33485
|
+
return yield* pipe(
|
|
33486
|
+
parse6(nodeAndCommentRange.node),
|
|
33487
|
+
map34(
|
|
33488
|
+
(_) => ({
|
|
33489
|
+
hash: _.hash,
|
|
33490
|
+
description: "Annotate with type",
|
|
33491
|
+
apply: gen3(function* () {
|
|
33492
|
+
const changeTracker = yield* service2(ChangeTracker);
|
|
33493
|
+
for (const { initializerTypeNode, variableDeclaration } of _.result) {
|
|
33494
|
+
if (variableDeclaration.type) {
|
|
33495
|
+
changeTracker.deleteRange(sourceFile, {
|
|
33496
|
+
pos: variableDeclaration.name.end,
|
|
33497
|
+
end: variableDeclaration.type.end
|
|
33498
|
+
});
|
|
33499
|
+
}
|
|
33500
|
+
changeTracker.insertNodeAt(
|
|
33501
|
+
sourceFile,
|
|
33502
|
+
variableDeclaration.name.end,
|
|
33503
|
+
initializerTypeNode,
|
|
33504
|
+
{
|
|
33505
|
+
prefix: ": "
|
|
33506
|
+
}
|
|
33507
|
+
);
|
|
33508
|
+
}
|
|
33509
|
+
})
|
|
33510
|
+
})
|
|
33511
|
+
)
|
|
33512
|
+
);
|
|
33513
|
+
})
|
|
33514
|
+
});
|
|
33515
|
+
|
|
33385
33516
|
// src/codegens.ts
|
|
33386
|
-
var codegens = [accessors];
|
|
33517
|
+
var codegens = [accessors, annotate3];
|
|
33387
33518
|
|
|
33388
33519
|
// src/cli/codegen.ts
|
|
33389
33520
|
var NoFilesToCodegenError = class extends TaggedError("NoFilesToCodegenError") {
|
|
@@ -35872,6 +36003,66 @@ var unnecessaryEffectGen = createDiagnostic({
|
|
|
35872
36003
|
})
|
|
35873
36004
|
});
|
|
35874
36005
|
|
|
36006
|
+
// src/diagnostics/unnecessaryFailYieldableError.ts
|
|
36007
|
+
var unnecessaryFailYieldableError = createDiagnostic({
|
|
36008
|
+
name: "unnecessaryFailYieldableError",
|
|
36009
|
+
code: 29,
|
|
36010
|
+
severity: "suggestion",
|
|
36011
|
+
apply: fn2("unnecessaryFailYieldableError.apply")(function* (sourceFile, report) {
|
|
36012
|
+
const ts = yield* service2(TypeScriptApi);
|
|
36013
|
+
const typeParser = yield* service2(TypeParser);
|
|
36014
|
+
const typeChecker = yield* service2(TypeCheckerApi);
|
|
36015
|
+
const yieldableErrorTypes = yield* pipe(
|
|
36016
|
+
typeParser.effectCauseYieldableErrorTypes(sourceFile),
|
|
36017
|
+
orElse14(() => succeed17([]))
|
|
36018
|
+
);
|
|
36019
|
+
const nodeToVisit = [];
|
|
36020
|
+
const appendNodeToVisit = (node) => {
|
|
36021
|
+
nodeToVisit.push(node);
|
|
36022
|
+
return void 0;
|
|
36023
|
+
};
|
|
36024
|
+
ts.forEachChild(sourceFile, appendNodeToVisit);
|
|
36025
|
+
while (nodeToVisit.length > 0) {
|
|
36026
|
+
const node = nodeToVisit.shift();
|
|
36027
|
+
ts.forEachChild(node, appendNodeToVisit);
|
|
36028
|
+
if (ts.isYieldExpression(node) && node.asteriskToken && node.expression && ts.isCallExpression(node.expression)) {
|
|
36029
|
+
const callExpression = node.expression;
|
|
36030
|
+
yield* pipe(
|
|
36031
|
+
typeParser.isNodeReferenceToEffectModuleApi("fail")(callExpression.expression),
|
|
36032
|
+
map34(() => {
|
|
36033
|
+
if (callExpression.arguments.length > 0) {
|
|
36034
|
+
const failArgument = callExpression.arguments[0];
|
|
36035
|
+
const argumentType = typeChecker.getTypeAtLocation(failArgument);
|
|
36036
|
+
const isYieldableError = yieldableErrorTypes.some(
|
|
36037
|
+
(yieldableType) => typeChecker.isTypeAssignableTo(argumentType, yieldableType)
|
|
36038
|
+
);
|
|
36039
|
+
if (isYieldableError) {
|
|
36040
|
+
report({
|
|
36041
|
+
location: node,
|
|
36042
|
+
messageText: `This Effect.fail call uses a yieldable error type as argument. You can yield* the error directly instead.`,
|
|
36043
|
+
fixes: [{
|
|
36044
|
+
fixName: "unnecessaryFailYieldableError_fix",
|
|
36045
|
+
description: "Replace yield* Effect.fail with yield*",
|
|
36046
|
+
apply: gen3(function* () {
|
|
36047
|
+
const changeTracker = yield* service2(ChangeTracker);
|
|
36048
|
+
changeTracker.replaceNode(
|
|
36049
|
+
sourceFile,
|
|
36050
|
+
callExpression,
|
|
36051
|
+
failArgument
|
|
36052
|
+
);
|
|
36053
|
+
})
|
|
36054
|
+
}]
|
|
36055
|
+
});
|
|
36056
|
+
}
|
|
36057
|
+
}
|
|
36058
|
+
}),
|
|
36059
|
+
ignore3
|
|
36060
|
+
);
|
|
36061
|
+
}
|
|
36062
|
+
}
|
|
36063
|
+
})
|
|
36064
|
+
});
|
|
36065
|
+
|
|
35875
36066
|
// src/diagnostics/unnecessaryPipe.ts
|
|
35876
36067
|
var unnecessaryPipe = createDiagnostic({
|
|
35877
36068
|
name: "unnecessaryPipe",
|
|
@@ -36059,6 +36250,7 @@ var diagnostics = [
|
|
|
36059
36250
|
floatingEffect,
|
|
36060
36251
|
missingStarInYieldEffectGen,
|
|
36061
36252
|
unnecessaryEffectGen,
|
|
36253
|
+
unnecessaryFailYieldableError,
|
|
36062
36254
|
missingReturnYieldStar,
|
|
36063
36255
|
leakingRequirements,
|
|
36064
36256
|
unnecessaryPipe,
|