@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
|
@@ -5549,6 +5549,7 @@ var classSelfMismatch = createDiagnostic({
|
|
|
5549
5549
|
if (ts.isClassDeclaration(node) && node.name && node.heritageClauses) {
|
|
5550
5550
|
const result = yield* pipe(
|
|
5551
5551
|
typeParser.extendsEffectService(node),
|
|
5552
|
+
orElse2(() => typeParser.extendsServiceMapService(node)),
|
|
5552
5553
|
orElse2(() => typeParser.extendsContextTag(node)),
|
|
5553
5554
|
orElse2(() => typeParser.extendsEffectTag(node)),
|
|
5554
5555
|
orElse2(() => typeParser.extendsSchemaClass(node)),
|
|
@@ -5924,6 +5925,89 @@ var effectFnIife = createDiagnostic({
|
|
|
5924
5925
|
})
|
|
5925
5926
|
});
|
|
5926
5927
|
|
|
5928
|
+
// src/diagnostics/effectFnImplicitAny.ts
|
|
5929
|
+
var getParameterName = (typescript, name) => {
|
|
5930
|
+
if (typescript.isIdentifier(name)) {
|
|
5931
|
+
return typescript.idText(name);
|
|
5932
|
+
}
|
|
5933
|
+
return "parameter";
|
|
5934
|
+
};
|
|
5935
|
+
var hasOuterContextualFunctionType = (typescript, typeChecker, node) => {
|
|
5936
|
+
const contextualType = typeChecker.getContextualType(node);
|
|
5937
|
+
if (!contextualType) {
|
|
5938
|
+
return false;
|
|
5939
|
+
}
|
|
5940
|
+
return typeChecker.getSignaturesOfType(contextualType, typescript.SignatureKind.Call).length > 0;
|
|
5941
|
+
};
|
|
5942
|
+
var effectFnImplicitAny = createDiagnostic({
|
|
5943
|
+
name: "effectFnImplicitAny",
|
|
5944
|
+
code: 54,
|
|
5945
|
+
description: "Mirrors noImplicitAny for unannotated Effect.fn and Effect.fnUntraced callback parameters when no outer contextual function type exists",
|
|
5946
|
+
group: "correctness",
|
|
5947
|
+
severity: "error",
|
|
5948
|
+
fixable: false,
|
|
5949
|
+
supportedEffect: ["v3", "v4"],
|
|
5950
|
+
apply: fn("effectFnImplicitAny.apply")(function* (sourceFile, report) {
|
|
5951
|
+
const ts = yield* service(TypeScriptApi);
|
|
5952
|
+
const program = yield* service(TypeScriptProgram);
|
|
5953
|
+
const typeChecker = yield* service(TypeCheckerApi);
|
|
5954
|
+
const typeParser = yield* service(TypeParser);
|
|
5955
|
+
const noImplicitAny = program.getCompilerOptions().noImplicitAny ?? program.getCompilerOptions().strict ?? false;
|
|
5956
|
+
if (!noImplicitAny) {
|
|
5957
|
+
return;
|
|
5958
|
+
}
|
|
5959
|
+
const nodeToVisit = [sourceFile];
|
|
5960
|
+
const appendNodeToVisit = (node) => {
|
|
5961
|
+
nodeToVisit.push(node);
|
|
5962
|
+
return void 0;
|
|
5963
|
+
};
|
|
5964
|
+
while (nodeToVisit.length > 0) {
|
|
5965
|
+
const node = nodeToVisit.pop();
|
|
5966
|
+
ts.forEachChild(node, appendNodeToVisit);
|
|
5967
|
+
const parsed = yield* pipe(
|
|
5968
|
+
typeParser.effectFn(node),
|
|
5969
|
+
map4((result) => ({
|
|
5970
|
+
call: result.node,
|
|
5971
|
+
fn: result.regularFunction
|
|
5972
|
+
})),
|
|
5973
|
+
orElse2(
|
|
5974
|
+
() => pipe(
|
|
5975
|
+
typeParser.effectFnGen(node),
|
|
5976
|
+
map4((result) => ({
|
|
5977
|
+
call: result.node,
|
|
5978
|
+
fn: result.generatorFunction
|
|
5979
|
+
}))
|
|
5980
|
+
)
|
|
5981
|
+
),
|
|
5982
|
+
orElse2(
|
|
5983
|
+
() => pipe(
|
|
5984
|
+
typeParser.effectFnUntracedGen(node),
|
|
5985
|
+
map4((result) => ({
|
|
5986
|
+
call: result.node,
|
|
5987
|
+
fn: result.generatorFunction
|
|
5988
|
+
}))
|
|
5989
|
+
)
|
|
5990
|
+
),
|
|
5991
|
+
orUndefined
|
|
5992
|
+
);
|
|
5993
|
+
if (!parsed || hasOuterContextualFunctionType(ts, typeChecker, parsed.call)) {
|
|
5994
|
+
continue;
|
|
5995
|
+
}
|
|
5996
|
+
for (const parameter of parsed.fn.parameters) {
|
|
5997
|
+
if (parameter.type || parameter.initializer) {
|
|
5998
|
+
continue;
|
|
5999
|
+
}
|
|
6000
|
+
const parameterName = getParameterName(ts, parameter.name);
|
|
6001
|
+
report({
|
|
6002
|
+
location: parameter.name,
|
|
6003
|
+
messageText: `Parameter '${parameterName}' implicitly has an 'any' type in Effect.fn/Effect.fnUntraced. Add an explicit type annotation or provide a contextual function type.`,
|
|
6004
|
+
fixes: []
|
|
6005
|
+
});
|
|
6006
|
+
}
|
|
6007
|
+
}
|
|
6008
|
+
})
|
|
6009
|
+
});
|
|
6010
|
+
|
|
5927
6011
|
// src/diagnostics/effectFnOpportunity.ts
|
|
5928
6012
|
var effectFnOpportunity = createDiagnostic({
|
|
5929
6013
|
name: "effectFnOpportunity",
|
|
@@ -7458,6 +7542,24 @@ var leakingRequirements = createDiagnostic({
|
|
|
7458
7542
|
const typeCheckerUtils = yield* service(TypeCheckerUtils);
|
|
7459
7543
|
const typeParser = yield* service(TypeParser);
|
|
7460
7544
|
const tsUtils = yield* service(TypeScriptUtils);
|
|
7545
|
+
const isExpectedLeakingServiceSuppressed = (leakedServiceName, startNode) => {
|
|
7546
|
+
const sourceFile2 = tsUtils.getSourceFileOfNode(startNode);
|
|
7547
|
+
if (!sourceFile2) return false;
|
|
7548
|
+
return !!ts.findAncestor(startNode, (current) => {
|
|
7549
|
+
const ranges = ts.getLeadingCommentRanges(sourceFile2.text, current.pos) ?? [];
|
|
7550
|
+
const isSuppressed = ranges.some((range) => {
|
|
7551
|
+
const commentText = sourceFile2.text.slice(range.pos, range.end);
|
|
7552
|
+
return commentText.split("\n").filter((line) => line.includes("@effect-expect-leaking")).some((line) => {
|
|
7553
|
+
const markerIndex = line.indexOf("@effect-expect-leaking");
|
|
7554
|
+
return markerIndex !== -1 && line.slice(markerIndex + "@effect-expect-leaking".length).includes(leakedServiceName);
|
|
7555
|
+
});
|
|
7556
|
+
});
|
|
7557
|
+
if (isSuppressed) {
|
|
7558
|
+
return true;
|
|
7559
|
+
}
|
|
7560
|
+
return ts.isClassDeclaration(current) || ts.isVariableStatement(current) || ts.isExpressionStatement(current) || ts.isStatement(current) ? "quit" : false;
|
|
7561
|
+
});
|
|
7562
|
+
};
|
|
7461
7563
|
const parseLeakedRequirements = cachedBy(
|
|
7462
7564
|
fn("leakingServices.checkServiceLeaking")(
|
|
7463
7565
|
function* (service2, atLocation) {
|
|
@@ -7539,8 +7641,12 @@ var leakingRequirements = createDiagnostic({
|
|
|
7539
7641
|
(_, service2) => service2
|
|
7540
7642
|
);
|
|
7541
7643
|
function reportLeakingRequirements(node, requirements) {
|
|
7542
|
-
|
|
7543
|
-
|
|
7644
|
+
const filteredRequirements = requirements.filter(
|
|
7645
|
+
(requirement) => !isExpectedLeakingServiceSuppressed(typeChecker.typeToString(requirement), node)
|
|
7646
|
+
);
|
|
7647
|
+
if (filteredRequirements.length === 0) return;
|
|
7648
|
+
const requirementsStr = filteredRequirements.map((_) => typeChecker.typeToString(_)).join(" | ");
|
|
7649
|
+
const firstStr = typeChecker.typeToString(filteredRequirements[0]);
|
|
7544
7650
|
report({
|
|
7545
7651
|
location: node,
|
|
7546
7652
|
messageText: `Methods of this Service require \`${requirementsStr}\` from every caller.
|
|
@@ -7549,7 +7655,7 @@ This leaks implementation details into the service's public type \u2014 callers
|
|
|
7549
7655
|
|
|
7550
7656
|
Resolve these dependencies at Layer creation and provide them to each method, so the service's type reflects its purpose, not its implementation.
|
|
7551
7657
|
|
|
7552
|
-
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 \`${
|
|
7658
|
+
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.
|
|
7553
7659
|
|
|
7554
7660
|
More info and examples at https://effect.website/docs/requirements-management/layers/#avoiding-requirement-leakage`,
|
|
7555
7661
|
fixes: []
|
|
@@ -11870,6 +11976,7 @@ var diagnostics = [
|
|
|
11870
11976
|
catchUnfailableEffect,
|
|
11871
11977
|
classSelfMismatch,
|
|
11872
11978
|
duplicatePackage,
|
|
11979
|
+
effectFnImplicitAny,
|
|
11873
11980
|
effectGenUsesAdapter,
|
|
11874
11981
|
missingEffectContext,
|
|
11875
11982
|
missingEffectError,
|