@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/index.js
CHANGED
|
@@ -7429,6 +7429,7 @@ var classSelfMismatch = createDiagnostic({
|
|
|
7429
7429
|
if (ts.isClassDeclaration(node) && node.name && node.heritageClauses) {
|
|
7430
7430
|
const result = yield* pipe(
|
|
7431
7431
|
typeParser.extendsEffectService(node),
|
|
7432
|
+
orElse2(() => typeParser.extendsServiceMapService(node)),
|
|
7432
7433
|
orElse2(() => typeParser.extendsContextTag(node)),
|
|
7433
7434
|
orElse2(() => typeParser.extendsEffectTag(node)),
|
|
7434
7435
|
orElse2(() => typeParser.extendsSchemaClass(node)),
|
|
@@ -7738,6 +7739,89 @@ var effectFnIife = createDiagnostic({
|
|
|
7738
7739
|
})
|
|
7739
7740
|
});
|
|
7740
7741
|
|
|
7742
|
+
// src/diagnostics/effectFnImplicitAny.ts
|
|
7743
|
+
var getParameterName = (typescript, name) => {
|
|
7744
|
+
if (typescript.isIdentifier(name)) {
|
|
7745
|
+
return typescript.idText(name);
|
|
7746
|
+
}
|
|
7747
|
+
return "parameter";
|
|
7748
|
+
};
|
|
7749
|
+
var hasOuterContextualFunctionType = (typescript, typeChecker, node) => {
|
|
7750
|
+
const contextualType = typeChecker.getContextualType(node);
|
|
7751
|
+
if (!contextualType) {
|
|
7752
|
+
return false;
|
|
7753
|
+
}
|
|
7754
|
+
return typeChecker.getSignaturesOfType(contextualType, typescript.SignatureKind.Call).length > 0;
|
|
7755
|
+
};
|
|
7756
|
+
var effectFnImplicitAny = createDiagnostic({
|
|
7757
|
+
name: "effectFnImplicitAny",
|
|
7758
|
+
code: 54,
|
|
7759
|
+
description: "Mirrors noImplicitAny for unannotated Effect.fn and Effect.fnUntraced callback parameters when no outer contextual function type exists",
|
|
7760
|
+
group: "correctness",
|
|
7761
|
+
severity: "error",
|
|
7762
|
+
fixable: false,
|
|
7763
|
+
supportedEffect: ["v3", "v4"],
|
|
7764
|
+
apply: fn("effectFnImplicitAny.apply")(function* (sourceFile, report) {
|
|
7765
|
+
const ts = yield* service(TypeScriptApi);
|
|
7766
|
+
const program = yield* service(TypeScriptProgram);
|
|
7767
|
+
const typeChecker = yield* service(TypeCheckerApi);
|
|
7768
|
+
const typeParser = yield* service(TypeParser);
|
|
7769
|
+
const noImplicitAny = program.getCompilerOptions().noImplicitAny ?? program.getCompilerOptions().strict ?? false;
|
|
7770
|
+
if (!noImplicitAny) {
|
|
7771
|
+
return;
|
|
7772
|
+
}
|
|
7773
|
+
const nodeToVisit = [sourceFile];
|
|
7774
|
+
const appendNodeToVisit = (node) => {
|
|
7775
|
+
nodeToVisit.push(node);
|
|
7776
|
+
return void 0;
|
|
7777
|
+
};
|
|
7778
|
+
while (nodeToVisit.length > 0) {
|
|
7779
|
+
const node = nodeToVisit.pop();
|
|
7780
|
+
ts.forEachChild(node, appendNodeToVisit);
|
|
7781
|
+
const parsed = yield* pipe(
|
|
7782
|
+
typeParser.effectFn(node),
|
|
7783
|
+
map5((result) => ({
|
|
7784
|
+
call: result.node,
|
|
7785
|
+
fn: result.regularFunction
|
|
7786
|
+
})),
|
|
7787
|
+
orElse2(
|
|
7788
|
+
() => pipe(
|
|
7789
|
+
typeParser.effectFnGen(node),
|
|
7790
|
+
map5((result) => ({
|
|
7791
|
+
call: result.node,
|
|
7792
|
+
fn: result.generatorFunction
|
|
7793
|
+
}))
|
|
7794
|
+
)
|
|
7795
|
+
),
|
|
7796
|
+
orElse2(
|
|
7797
|
+
() => pipe(
|
|
7798
|
+
typeParser.effectFnUntracedGen(node),
|
|
7799
|
+
map5((result) => ({
|
|
7800
|
+
call: result.node,
|
|
7801
|
+
fn: result.generatorFunction
|
|
7802
|
+
}))
|
|
7803
|
+
)
|
|
7804
|
+
),
|
|
7805
|
+
orUndefined
|
|
7806
|
+
);
|
|
7807
|
+
if (!parsed || hasOuterContextualFunctionType(ts, typeChecker, parsed.call)) {
|
|
7808
|
+
continue;
|
|
7809
|
+
}
|
|
7810
|
+
for (const parameter of parsed.fn.parameters) {
|
|
7811
|
+
if (parameter.type || parameter.initializer) {
|
|
7812
|
+
continue;
|
|
7813
|
+
}
|
|
7814
|
+
const parameterName = getParameterName(ts, parameter.name);
|
|
7815
|
+
report({
|
|
7816
|
+
location: parameter.name,
|
|
7817
|
+
messageText: `Parameter '${parameterName}' implicitly has an 'any' type in Effect.fn/Effect.fnUntraced. Add an explicit type annotation or provide a contextual function type.`,
|
|
7818
|
+
fixes: []
|
|
7819
|
+
});
|
|
7820
|
+
}
|
|
7821
|
+
}
|
|
7822
|
+
})
|
|
7823
|
+
});
|
|
7824
|
+
|
|
7741
7825
|
// src/diagnostics/effectFnOpportunity.ts
|
|
7742
7826
|
var effectFnOpportunity = createDiagnostic({
|
|
7743
7827
|
name: "effectFnOpportunity",
|
|
@@ -9272,6 +9356,24 @@ var leakingRequirements = createDiagnostic({
|
|
|
9272
9356
|
const typeCheckerUtils = yield* service(TypeCheckerUtils);
|
|
9273
9357
|
const typeParser = yield* service(TypeParser);
|
|
9274
9358
|
const tsUtils = yield* service(TypeScriptUtils);
|
|
9359
|
+
const isExpectedLeakingServiceSuppressed = (leakedServiceName, startNode) => {
|
|
9360
|
+
const sourceFile2 = tsUtils.getSourceFileOfNode(startNode);
|
|
9361
|
+
if (!sourceFile2) return false;
|
|
9362
|
+
return !!ts.findAncestor(startNode, (current) => {
|
|
9363
|
+
const ranges = ts.getLeadingCommentRanges(sourceFile2.text, current.pos) ?? [];
|
|
9364
|
+
const isSuppressed = ranges.some((range) => {
|
|
9365
|
+
const commentText = sourceFile2.text.slice(range.pos, range.end);
|
|
9366
|
+
return commentText.split("\n").filter((line) => line.includes("@effect-expect-leaking")).some((line) => {
|
|
9367
|
+
const markerIndex = line.indexOf("@effect-expect-leaking");
|
|
9368
|
+
return markerIndex !== -1 && line.slice(markerIndex + "@effect-expect-leaking".length).includes(leakedServiceName);
|
|
9369
|
+
});
|
|
9370
|
+
});
|
|
9371
|
+
if (isSuppressed) {
|
|
9372
|
+
return true;
|
|
9373
|
+
}
|
|
9374
|
+
return ts.isClassDeclaration(current) || ts.isVariableStatement(current) || ts.isExpressionStatement(current) || ts.isStatement(current) ? "quit" : false;
|
|
9375
|
+
});
|
|
9376
|
+
};
|
|
9275
9377
|
const parseLeakedRequirements = cachedBy(
|
|
9276
9378
|
fn("leakingServices.checkServiceLeaking")(
|
|
9277
9379
|
function* (service2, atLocation) {
|
|
@@ -9353,8 +9455,12 @@ var leakingRequirements = createDiagnostic({
|
|
|
9353
9455
|
(_, service2) => service2
|
|
9354
9456
|
);
|
|
9355
9457
|
function reportLeakingRequirements(node, requirements) {
|
|
9356
|
-
|
|
9357
|
-
|
|
9458
|
+
const filteredRequirements = requirements.filter(
|
|
9459
|
+
(requirement) => !isExpectedLeakingServiceSuppressed(typeChecker.typeToString(requirement), node)
|
|
9460
|
+
);
|
|
9461
|
+
if (filteredRequirements.length === 0) return;
|
|
9462
|
+
const requirementsStr = filteredRequirements.map((_) => typeChecker.typeToString(_)).join(" | ");
|
|
9463
|
+
const firstStr = typeChecker.typeToString(filteredRequirements[0]);
|
|
9358
9464
|
report({
|
|
9359
9465
|
location: node,
|
|
9360
9466
|
messageText: `Methods of this Service require \`${requirementsStr}\` from every caller.
|
|
@@ -9363,7 +9469,7 @@ This leaks implementation details into the service's public type \u2014 callers
|
|
|
9363
9469
|
|
|
9364
9470
|
Resolve these dependencies at Layer creation and provide them to each method, so the service's type reflects its purpose, not its implementation.
|
|
9365
9471
|
|
|
9366
|
-
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 \`${
|
|
9472
|
+
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.
|
|
9367
9473
|
|
|
9368
9474
|
More info and examples at https://effect.website/docs/requirements-management/layers/#avoiding-requirement-leakage`,
|
|
9369
9475
|
fixes: []
|
|
@@ -12615,6 +12721,7 @@ var diagnostics = [
|
|
|
12615
12721
|
catchUnfailableEffect,
|
|
12616
12722
|
classSelfMismatch,
|
|
12617
12723
|
duplicatePackage,
|
|
12724
|
+
effectFnImplicitAny,
|
|
12618
12725
|
effectGenUsesAdapter,
|
|
12619
12726
|
missingEffectContext,
|
|
12620
12727
|
missingEffectError,
|