@effect/language-service 0.81.0 → 0.82.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 +4 -1
- package/cli.js +135 -5
- package/cli.js.map +1 -1
- package/effect-lsp-patch-utils.js +110 -3
- package/effect-lsp-patch-utils.js.map +1 -1
- package/index.js +110 -3
- package/index.js.map +1 -1
- package/package.json +1 -1
- package/schema.json +2828 -0
- package/transform.js +110 -3
- package/transform.js.map +1 -1
package/transform.js
CHANGED
|
@@ -5545,6 +5545,7 @@ var classSelfMismatch = createDiagnostic({
|
|
|
5545
5545
|
if (ts.isClassDeclaration(node) && node.name && node.heritageClauses) {
|
|
5546
5546
|
const result = yield* pipe(
|
|
5547
5547
|
typeParser.extendsEffectService(node),
|
|
5548
|
+
orElse2(() => typeParser.extendsServiceMapService(node)),
|
|
5548
5549
|
orElse2(() => typeParser.extendsContextTag(node)),
|
|
5549
5550
|
orElse2(() => typeParser.extendsEffectTag(node)),
|
|
5550
5551
|
orElse2(() => typeParser.extendsSchemaClass(node)),
|
|
@@ -5920,6 +5921,89 @@ var effectFnIife = createDiagnostic({
|
|
|
5920
5921
|
})
|
|
5921
5922
|
});
|
|
5922
5923
|
|
|
5924
|
+
// src/diagnostics/effectFnImplicitAny.ts
|
|
5925
|
+
var getParameterName = (typescript, name) => {
|
|
5926
|
+
if (typescript.isIdentifier(name)) {
|
|
5927
|
+
return typescript.idText(name);
|
|
5928
|
+
}
|
|
5929
|
+
return "parameter";
|
|
5930
|
+
};
|
|
5931
|
+
var hasOuterContextualFunctionType = (typescript, typeChecker, node) => {
|
|
5932
|
+
const contextualType = typeChecker.getContextualType(node);
|
|
5933
|
+
if (!contextualType) {
|
|
5934
|
+
return false;
|
|
5935
|
+
}
|
|
5936
|
+
return typeChecker.getSignaturesOfType(contextualType, typescript.SignatureKind.Call).length > 0;
|
|
5937
|
+
};
|
|
5938
|
+
var effectFnImplicitAny = createDiagnostic({
|
|
5939
|
+
name: "effectFnImplicitAny",
|
|
5940
|
+
code: 54,
|
|
5941
|
+
description: "Mirrors noImplicitAny for unannotated Effect.fn and Effect.fnUntraced callback parameters when no outer contextual function type exists",
|
|
5942
|
+
group: "correctness",
|
|
5943
|
+
severity: "error",
|
|
5944
|
+
fixable: false,
|
|
5945
|
+
supportedEffect: ["v3", "v4"],
|
|
5946
|
+
apply: fn("effectFnImplicitAny.apply")(function* (sourceFile, report) {
|
|
5947
|
+
const ts = yield* service(TypeScriptApi);
|
|
5948
|
+
const program = yield* service(TypeScriptProgram);
|
|
5949
|
+
const typeChecker = yield* service(TypeCheckerApi);
|
|
5950
|
+
const typeParser = yield* service(TypeParser);
|
|
5951
|
+
const noImplicitAny = program.getCompilerOptions().noImplicitAny ?? program.getCompilerOptions().strict ?? false;
|
|
5952
|
+
if (!noImplicitAny) {
|
|
5953
|
+
return;
|
|
5954
|
+
}
|
|
5955
|
+
const nodeToVisit = [sourceFile];
|
|
5956
|
+
const appendNodeToVisit = (node) => {
|
|
5957
|
+
nodeToVisit.push(node);
|
|
5958
|
+
return void 0;
|
|
5959
|
+
};
|
|
5960
|
+
while (nodeToVisit.length > 0) {
|
|
5961
|
+
const node = nodeToVisit.pop();
|
|
5962
|
+
ts.forEachChild(node, appendNodeToVisit);
|
|
5963
|
+
const parsed = yield* pipe(
|
|
5964
|
+
typeParser.effectFn(node),
|
|
5965
|
+
map4((result) => ({
|
|
5966
|
+
call: result.node,
|
|
5967
|
+
fn: result.regularFunction
|
|
5968
|
+
})),
|
|
5969
|
+
orElse2(
|
|
5970
|
+
() => pipe(
|
|
5971
|
+
typeParser.effectFnGen(node),
|
|
5972
|
+
map4((result) => ({
|
|
5973
|
+
call: result.node,
|
|
5974
|
+
fn: result.generatorFunction
|
|
5975
|
+
}))
|
|
5976
|
+
)
|
|
5977
|
+
),
|
|
5978
|
+
orElse2(
|
|
5979
|
+
() => pipe(
|
|
5980
|
+
typeParser.effectFnUntracedGen(node),
|
|
5981
|
+
map4((result) => ({
|
|
5982
|
+
call: result.node,
|
|
5983
|
+
fn: result.generatorFunction
|
|
5984
|
+
}))
|
|
5985
|
+
)
|
|
5986
|
+
),
|
|
5987
|
+
orUndefined
|
|
5988
|
+
);
|
|
5989
|
+
if (!parsed || hasOuterContextualFunctionType(ts, typeChecker, parsed.call)) {
|
|
5990
|
+
continue;
|
|
5991
|
+
}
|
|
5992
|
+
for (const parameter of parsed.fn.parameters) {
|
|
5993
|
+
if (parameter.type || parameter.initializer) {
|
|
5994
|
+
continue;
|
|
5995
|
+
}
|
|
5996
|
+
const parameterName = getParameterName(ts, parameter.name);
|
|
5997
|
+
report({
|
|
5998
|
+
location: parameter.name,
|
|
5999
|
+
messageText: `Parameter '${parameterName}' implicitly has an 'any' type in Effect.fn/Effect.fnUntraced. Add an explicit type annotation or provide a contextual function type.`,
|
|
6000
|
+
fixes: []
|
|
6001
|
+
});
|
|
6002
|
+
}
|
|
6003
|
+
}
|
|
6004
|
+
})
|
|
6005
|
+
});
|
|
6006
|
+
|
|
5923
6007
|
// src/diagnostics/effectFnOpportunity.ts
|
|
5924
6008
|
var effectFnOpportunity = createDiagnostic({
|
|
5925
6009
|
name: "effectFnOpportunity",
|
|
@@ -7454,6 +7538,24 @@ var leakingRequirements = createDiagnostic({
|
|
|
7454
7538
|
const typeCheckerUtils = yield* service(TypeCheckerUtils);
|
|
7455
7539
|
const typeParser = yield* service(TypeParser);
|
|
7456
7540
|
const tsUtils = yield* service(TypeScriptUtils);
|
|
7541
|
+
const isExpectedLeakingServiceSuppressed = (leakedServiceName, startNode) => {
|
|
7542
|
+
const sourceFile2 = tsUtils.getSourceFileOfNode(startNode);
|
|
7543
|
+
if (!sourceFile2) return false;
|
|
7544
|
+
return !!ts.findAncestor(startNode, (current) => {
|
|
7545
|
+
const ranges = ts.getLeadingCommentRanges(sourceFile2.text, current.pos) ?? [];
|
|
7546
|
+
const isSuppressed = ranges.some((range) => {
|
|
7547
|
+
const commentText = sourceFile2.text.slice(range.pos, range.end);
|
|
7548
|
+
return commentText.split("\n").filter((line) => line.includes("@effect-expect-leaking")).some((line) => {
|
|
7549
|
+
const markerIndex = line.indexOf("@effect-expect-leaking");
|
|
7550
|
+
return markerIndex !== -1 && line.slice(markerIndex + "@effect-expect-leaking".length).includes(leakedServiceName);
|
|
7551
|
+
});
|
|
7552
|
+
});
|
|
7553
|
+
if (isSuppressed) {
|
|
7554
|
+
return true;
|
|
7555
|
+
}
|
|
7556
|
+
return ts.isClassDeclaration(current) || ts.isVariableStatement(current) || ts.isExpressionStatement(current) || ts.isStatement(current) ? "quit" : false;
|
|
7557
|
+
});
|
|
7558
|
+
};
|
|
7457
7559
|
const parseLeakedRequirements = cachedBy(
|
|
7458
7560
|
fn("leakingServices.checkServiceLeaking")(
|
|
7459
7561
|
function* (service2, atLocation) {
|
|
@@ -7535,8 +7637,12 @@ var leakingRequirements = createDiagnostic({
|
|
|
7535
7637
|
(_, service2) => service2
|
|
7536
7638
|
);
|
|
7537
7639
|
function reportLeakingRequirements(node, requirements) {
|
|
7538
|
-
|
|
7539
|
-
|
|
7640
|
+
const filteredRequirements = requirements.filter(
|
|
7641
|
+
(requirement) => !isExpectedLeakingServiceSuppressed(typeChecker.typeToString(requirement), node)
|
|
7642
|
+
);
|
|
7643
|
+
if (filteredRequirements.length === 0) return;
|
|
7644
|
+
const requirementsStr = filteredRequirements.map((_) => typeChecker.typeToString(_)).join(" | ");
|
|
7645
|
+
const firstStr = typeChecker.typeToString(filteredRequirements[0]);
|
|
7540
7646
|
report({
|
|
7541
7647
|
location: node,
|
|
7542
7648
|
messageText: `Methods of this Service require \`${requirementsStr}\` from every caller.
|
|
@@ -7545,7 +7651,7 @@ This leaks implementation details into the service's public type \u2014 callers
|
|
|
7545
7651
|
|
|
7546
7652
|
Resolve these dependencies at Layer creation and provide them to each method, so the service's type reflects its purpose, not its implementation.
|
|
7547
7653
|
|
|
7548
|
-
To suppress this diagnostic for specific dependency types that are intentionally passed through (e.g., HttpServerRequest), add \`@effect-leakable-service\` JSDoc to their interface declarations (e.g., the \`${
|
|
7654
|
+
To suppress this diagnostic for specific dependency types that are intentionally passed through (e.g., HttpServerRequest), add \`@effect-leakable-service\` JSDoc to their interface declarations (e.g., the \`${firstStr}\` interface), or to this service by adding a \`@effect-expect-leaking ${firstStr}\` JSDoc.
|
|
7549
7655
|
|
|
7550
7656
|
More info and examples at https://effect.website/docs/requirements-management/layers/#avoiding-requirement-leakage`,
|
|
7551
7657
|
fixes: []
|
|
@@ -11866,6 +11972,7 @@ var diagnostics = [
|
|
|
11866
11972
|
catchUnfailableEffect,
|
|
11867
11973
|
classSelfMismatch,
|
|
11868
11974
|
duplicatePackage,
|
|
11975
|
+
effectFnImplicitAny,
|
|
11869
11976
|
effectGenUsesAdapter,
|
|
11870
11977
|
missingEffectContext,
|
|
11871
11978
|
missingEffectError,
|