@effect/language-service 0.70.0 → 0.71.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 +3 -0
- package/cli.js +113 -7
- package/cli.js.map +1 -1
- package/effect-lsp-patch-utils.js +80 -5
- package/effect-lsp-patch-utils.js.map +1 -1
- package/index.js +58 -3
- package/index.js.map +1 -1
- package/package.json +1 -1
- package/transform.js +58 -3
- package/transform.js.map +1 -1
package/README.md
CHANGED
|
@@ -84,6 +84,7 @@ And you're done! You'll now be able to use a set of refactors and diagnostics th
|
|
|
84
84
|
- Warn when `Effect.fn` or `Effect.fnUntraced` is used as an IIFE (Immediately Invoked Function Expression), suggesting `Effect.gen` instead
|
|
85
85
|
- Suggest removing redundant identifier argument when it equals the tag value in `Schema.TaggedClass`, `Schema.TaggedError`, or `Schema.TaggedRequest`
|
|
86
86
|
- Suggest using `Schema.is` instead of `instanceof` for Effect Schema types
|
|
87
|
+
- Suggest using `Effect.void` instead of `Effect.succeed(undefined)` or `Effect.succeed(void 0)`
|
|
87
88
|
|
|
88
89
|
### Completions
|
|
89
90
|
|
|
@@ -140,6 +141,8 @@ Few options can be provided alongside the initialization of the Language Service
|
|
|
140
141
|
"diagnosticsName": true, // controls whether to include the rule name in diagnostic messages (default: true)
|
|
141
142
|
"missingDiagnosticNextLine": "warning", // controls the severity of warnings for unused @effect-diagnostics-next-line comments (default: "warning", allowed values: off,error,warning,message,suggestion)
|
|
142
143
|
"includeSuggestionsInTsc": true, // when enabled with effect-language-service patch enabled, diagnostics with "suggestion" severity will be reported as "message" in TSC with "[suggestion]" prefix; useful to help steer LLM output (default: true)
|
|
144
|
+
"ignoreEffectWarningsInTscExitCode": false, // if set to true, effect-related warnings won't change the exit code of tsc, meaning that tsc will compile fine even if effect warnings are emitted (default: false)
|
|
145
|
+
"ignoreEffectSuggestionsInTscExitCode": true, // if set to true, effect-related suggestions won't change the exit code of tsc (default: true)
|
|
143
146
|
"quickinfo": true, // controls Effect quickinfo (default: true)
|
|
144
147
|
"quickinfoEffectParameters": "whenTruncated", // (default: "whenTruncated") controls when to display effect type parameters always,never,whenTruncated
|
|
145
148
|
"quickinfoMaximumLength": -1, // controls how long can be the types in the quickinfo hover (helps with very long type to improve perfs, defaults to -1 for no truncation, can be any number eg. 1000 and TS will try to fit as much as possible in that budget, higher number means more info.)
|
package/cli.js
CHANGED
|
@@ -30214,7 +30214,7 @@ var runMain3 = runMain2;
|
|
|
30214
30214
|
// package.json
|
|
30215
30215
|
var package_default = {
|
|
30216
30216
|
name: "@effect/language-service",
|
|
30217
|
-
version: "0.
|
|
30217
|
+
version: "0.71.1",
|
|
30218
30218
|
packageManager: "pnpm@8.11.0",
|
|
30219
30219
|
publishConfig: {
|
|
30220
30220
|
access: "public",
|
|
@@ -32038,6 +32038,8 @@ var defaults = {
|
|
|
32038
32038
|
skipLeadingPath: ["src/"]
|
|
32039
32039
|
}],
|
|
32040
32040
|
extendedKeyDetection: false,
|
|
32041
|
+
ignoreEffectWarningsInTscExitCode: false,
|
|
32042
|
+
ignoreEffectSuggestionsInTscExitCode: true,
|
|
32041
32043
|
pipeableMinArgCount: 2,
|
|
32042
32044
|
effectFn: ["span"],
|
|
32043
32045
|
layerGraphFollowDepth: 0,
|
|
@@ -32063,6 +32065,8 @@ function parse4(config2) {
|
|
|
32063
32065
|
diagnosticsName: isObject(config2) && hasProperty(config2, "diagnosticsName") && isBoolean(config2.diagnosticsName) ? config2.diagnosticsName : defaults.diagnosticsName,
|
|
32064
32066
|
missingDiagnosticNextLine: isObject(config2) && hasProperty(config2, "missingDiagnosticNextLine") && isString(config2.missingDiagnosticNextLine) && isValidSeverityLevel(config2.missingDiagnosticNextLine) ? config2.missingDiagnosticNextLine : defaults.missingDiagnosticNextLine,
|
|
32065
32067
|
includeSuggestionsInTsc: isObject(config2) && hasProperty(config2, "includeSuggestionsInTsc") && isBoolean(config2.includeSuggestionsInTsc) ? config2.includeSuggestionsInTsc : defaults.includeSuggestionsInTsc,
|
|
32068
|
+
ignoreEffectWarningsInTscExitCode: isObject(config2) && hasProperty(config2, "ignoreEffectWarningsInTscExitCode") && isBoolean(config2.ignoreEffectWarningsInTscExitCode) ? config2.ignoreEffectWarningsInTscExitCode : defaults.ignoreEffectWarningsInTscExitCode,
|
|
32069
|
+
ignoreEffectSuggestionsInTscExitCode: isObject(config2) && hasProperty(config2, "ignoreEffectSuggestionsInTscExitCode") && isBoolean(config2.ignoreEffectSuggestionsInTscExitCode) ? config2.ignoreEffectSuggestionsInTscExitCode : defaults.ignoreEffectSuggestionsInTscExitCode,
|
|
32066
32070
|
quickinfo: isObject(config2) && hasProperty(config2, "quickinfo") && isBoolean(config2.quickinfo) ? config2.quickinfo : defaults.quickinfo,
|
|
32067
32071
|
quickinfoEffectParameters: isObject(config2) && hasProperty(config2, "quickinfoEffectParameters") && isString(config2.quickinfoEffectParameters) && ["always", "never", "whentruncated"].includes(config2.quickinfoEffectParameters.toLowerCase()) ? config2.quickinfoEffectParameters.toLowerCase() : defaults.quickinfoEffectParameters,
|
|
32068
32072
|
quickinfoMaximumLength: isObject(config2) && hasProperty(config2, "quickinfoMaximumLength") && isNumber(config2.quickinfoMaximumLength) ? config2.quickinfoMaximumLength : defaults.quickinfoMaximumLength,
|
|
@@ -36899,6 +36903,57 @@ var effectMapVoid = createDiagnostic({
|
|
|
36899
36903
|
})
|
|
36900
36904
|
});
|
|
36901
36905
|
|
|
36906
|
+
// src/diagnostics/effectSucceedWithVoid.ts
|
|
36907
|
+
var effectSucceedWithVoid = createDiagnostic({
|
|
36908
|
+
name: "effectSucceedWithVoid",
|
|
36909
|
+
code: 47,
|
|
36910
|
+
description: "Suggests using Effect.void instead of Effect.succeed(undefined) or Effect.succeed(void 0)",
|
|
36911
|
+
severity: "suggestion",
|
|
36912
|
+
apply: fn2("effectSucceedWithVoid.apply")(function* (sourceFile, report) {
|
|
36913
|
+
const ts = yield* service2(TypeScriptApi);
|
|
36914
|
+
const typeParser = yield* service2(TypeParser);
|
|
36915
|
+
const tsUtils = yield* service2(TypeScriptUtils);
|
|
36916
|
+
const nodeToVisit = [];
|
|
36917
|
+
const appendNodeToVisit = (node) => {
|
|
36918
|
+
nodeToVisit.push(node);
|
|
36919
|
+
return void 0;
|
|
36920
|
+
};
|
|
36921
|
+
ts.forEachChild(sourceFile, appendNodeToVisit);
|
|
36922
|
+
while (nodeToVisit.length > 0) {
|
|
36923
|
+
const node = nodeToVisit.shift();
|
|
36924
|
+
ts.forEachChild(node, appendNodeToVisit);
|
|
36925
|
+
if (ts.isCallExpression(node)) {
|
|
36926
|
+
const isSucceedCall = yield* pipe(
|
|
36927
|
+
typeParser.isNodeReferenceToEffectModuleApi("succeed")(node.expression),
|
|
36928
|
+
option5
|
|
36929
|
+
);
|
|
36930
|
+
if (isSome2(isSucceedCall)) {
|
|
36931
|
+
const argument = node.arguments[0];
|
|
36932
|
+
if (!argument) continue;
|
|
36933
|
+
if (!tsUtils.isVoidExpression(argument)) continue;
|
|
36934
|
+
report({
|
|
36935
|
+
location: node,
|
|
36936
|
+
messageText: "Effect.void can be used instead of Effect.succeed(undefined) or Effect.succeed(void 0)",
|
|
36937
|
+
fixes: [{
|
|
36938
|
+
fixName: "effectSucceedWithVoid_fix",
|
|
36939
|
+
description: "Replace with Effect.void",
|
|
36940
|
+
apply: gen3(function* () {
|
|
36941
|
+
const changeTracker = yield* service2(ChangeTracker);
|
|
36942
|
+
const effectModuleIdentifier = tsUtils.findImportedModuleIdentifierByPackageAndNameOrBarrel(sourceFile, "effect", "Effect") || "Effect";
|
|
36943
|
+
const newNode = ts.factory.createPropertyAccessExpression(
|
|
36944
|
+
ts.factory.createIdentifier(effectModuleIdentifier),
|
|
36945
|
+
ts.factory.createIdentifier("void")
|
|
36946
|
+
);
|
|
36947
|
+
changeTracker.replaceNode(sourceFile, node, newNode);
|
|
36948
|
+
})
|
|
36949
|
+
}]
|
|
36950
|
+
});
|
|
36951
|
+
}
|
|
36952
|
+
}
|
|
36953
|
+
}
|
|
36954
|
+
})
|
|
36955
|
+
});
|
|
36956
|
+
|
|
36902
36957
|
// src/diagnostics/floatingEffect.ts
|
|
36903
36958
|
var floatingEffect = createDiagnostic({
|
|
36904
36959
|
name: "floatingEffect",
|
|
@@ -37042,8 +37097,7 @@ var globalErrorInEffectCatch = createDiagnostic({
|
|
|
37042
37097
|
);
|
|
37043
37098
|
report({
|
|
37044
37099
|
location: node.expression,
|
|
37045
|
-
messageText: `The 'catch' callback in ${nodeText} returns
|
|
37046
|
-
Instead, use tagged errors or custom errors with a discriminator property to get properly type-checked errors.`,
|
|
37100
|
+
messageText: `The 'catch' callback in ${nodeText} returns global 'Error', which loses type safety as untagged errors merge together. Consider using a tagged error and optionally wrapping the original in a 'cause' property.`,
|
|
37047
37101
|
fixes: []
|
|
37048
37102
|
});
|
|
37049
37103
|
}
|
|
@@ -37095,7 +37149,7 @@ var globalErrorInEffectFailure = createDiagnostic({
|
|
|
37095
37149
|
if (hasGlobalError) {
|
|
37096
37150
|
report({
|
|
37097
37151
|
location: node,
|
|
37098
|
-
messageText: `
|
|
37152
|
+
messageText: `Global 'Error' loses type safety as untagged errors merge together in the Effect failure channel. Consider using a tagged error and optionally wrapping the original in a 'cause' property.`,
|
|
37099
37153
|
fixes: []
|
|
37100
37154
|
});
|
|
37101
37155
|
}
|
|
@@ -39769,6 +39823,7 @@ var diagnostics = [
|
|
|
39769
39823
|
globalErrorInEffectFailure,
|
|
39770
39824
|
layerMergeAllWithDependencies,
|
|
39771
39825
|
effectMapVoid,
|
|
39826
|
+
effectSucceedWithVoid,
|
|
39772
39827
|
effectFnIife,
|
|
39773
39828
|
effectFnOpportunity,
|
|
39774
39829
|
redundantSchemaTagIdentifier,
|
|
@@ -41455,6 +41510,8 @@ var getPatchesForModule = fn("getPatchesForModule")(
|
|
|
41455
41510
|
let insertCheckSourceFilePosition = none2();
|
|
41456
41511
|
let insertSkipPrecedingCommentDirectivePosition = none2();
|
|
41457
41512
|
let insertAppendMetadataRelationErrorPosition = none2();
|
|
41513
|
+
let insertExtractDiagnosticsForExitStatusPosition = none2();
|
|
41514
|
+
let replacedBindingExtractDiagnosticsForExitStatus = none2();
|
|
41458
41515
|
let nodesToCheck = [];
|
|
41459
41516
|
function findNodeAtPositionIncludingTrivia(sourceFile2, position) {
|
|
41460
41517
|
function find3(node) {
|
|
@@ -41483,6 +41540,7 @@ var getPatchesForModule = fn("getPatchesForModule")(
|
|
|
41483
41540
|
if (!pushFunctionDeclarationNode("checkSourceFileWorker")) requiresFullScan = true;
|
|
41484
41541
|
if (!pushFunctionDeclarationNode("markPrecedingCommentDirectiveLine")) requiresFullScan = true;
|
|
41485
41542
|
if (!pushFunctionDeclarationNode("reportRelationError")) requiresFullScan = true;
|
|
41543
|
+
if (!pushFunctionDeclarationNode("emitFilesAndReportErrorsAndGetExitStatus")) requiresFullScan = true;
|
|
41486
41544
|
if (requiresFullScan) nodesToCheck = [sourceFile];
|
|
41487
41545
|
while (nodesToCheck.length > 0) {
|
|
41488
41546
|
const node = nodesToCheck.shift();
|
|
@@ -41517,6 +41575,29 @@ var getPatchesForModule = fn("getPatchesForModule")(
|
|
|
41517
41575
|
});
|
|
41518
41576
|
}
|
|
41519
41577
|
}
|
|
41578
|
+
} else if (ts.isCallExpression(node)) {
|
|
41579
|
+
const callee = node.expression;
|
|
41580
|
+
if (ts.isIdentifier(callee) && ts.idText(callee) === "emitFilesAndReportErrors") {
|
|
41581
|
+
const parentVariableDeclaration = ts.findAncestor(node, ts.isVariableDeclaration);
|
|
41582
|
+
if (parentVariableDeclaration) {
|
|
41583
|
+
const parentVariableStatement = ts.findAncestor(parentVariableDeclaration, ts.isVariableStatement);
|
|
41584
|
+
if (parentVariableStatement) {
|
|
41585
|
+
const parentBlock = parentVariableStatement.parent;
|
|
41586
|
+
if (ts.isBlock(parentBlock)) {
|
|
41587
|
+
const parentFunctionDeclaration = parentBlock.parent;
|
|
41588
|
+
if (ts.isFunctionDeclaration(parentFunctionDeclaration) && parentFunctionDeclaration.name && ts.isIdentifier(parentFunctionDeclaration.name) && ts.idText(parentFunctionDeclaration.name) === "emitFilesAndReportErrorsAndGetExitStatus") {
|
|
41589
|
+
insertExtractDiagnosticsForExitStatusPosition = some2({
|
|
41590
|
+
position: parentVariableStatement.end
|
|
41591
|
+
});
|
|
41592
|
+
replacedBindingExtractDiagnosticsForExitStatus = some2({
|
|
41593
|
+
pos: parentVariableDeclaration.name.pos,
|
|
41594
|
+
end: parentVariableDeclaration.name.end
|
|
41595
|
+
});
|
|
41596
|
+
}
|
|
41597
|
+
}
|
|
41598
|
+
}
|
|
41599
|
+
}
|
|
41600
|
+
}
|
|
41520
41601
|
}
|
|
41521
41602
|
ts.forEachChild(node, (child) => {
|
|
41522
41603
|
nodesToCheck.push(child);
|
|
@@ -41571,9 +41652,7 @@ var getPatchesForModule = fn("getPatchesForModule")(
|
|
|
41571
41652
|
)
|
|
41572
41653
|
);
|
|
41573
41654
|
if (isNone2(insertSkipPrecedingCommentDirectivePosition)) {
|
|
41574
|
-
return yield*
|
|
41575
|
-
new UnableToFindPositionToPatchError({ positionToFind: "skip preceding comment directive" })
|
|
41576
|
-
);
|
|
41655
|
+
return yield* new UnableToFindPositionToPatchError({ positionToFind: "skip preceding comment directive" });
|
|
41577
41656
|
}
|
|
41578
41657
|
patches.push(
|
|
41579
41658
|
yield* makeEffectLspPatchChange(
|
|
@@ -41585,6 +41664,33 @@ var getPatchesForModule = fn("getPatchesForModule")(
|
|
|
41585
41664
|
version
|
|
41586
41665
|
)
|
|
41587
41666
|
);
|
|
41667
|
+
if (isNone2(insertExtractDiagnosticsForExitStatusPosition)) {
|
|
41668
|
+
return yield* new UnableToFindPositionToPatchError({ positionToFind: "extractDiagnosticsForExitStatus" });
|
|
41669
|
+
}
|
|
41670
|
+
if (isNone2(replacedBindingExtractDiagnosticsForExitStatus)) {
|
|
41671
|
+
return yield* new UnableToFindPositionToPatchError({ positionToFind: "extractDiagnosticsForExitStatus-binding" });
|
|
41672
|
+
}
|
|
41673
|
+
patches.push(
|
|
41674
|
+
yield* makeEffectLspPatchChange(
|
|
41675
|
+
sourceFile.text,
|
|
41676
|
+
replacedBindingExtractDiagnosticsForExitStatus.value.pos,
|
|
41677
|
+
replacedBindingExtractDiagnosticsForExitStatus.value.end,
|
|
41678
|
+
` { emitResult, diagnostics: tscDiagnostics }`,
|
|
41679
|
+
" ",
|
|
41680
|
+
version
|
|
41681
|
+
)
|
|
41682
|
+
);
|
|
41683
|
+
patches.push(
|
|
41684
|
+
yield* makeEffectLspPatchChange(
|
|
41685
|
+
sourceFile.text,
|
|
41686
|
+
insertExtractDiagnosticsForExitStatusPosition.value.position,
|
|
41687
|
+
insertExtractDiagnosticsForExitStatusPosition.value.position,
|
|
41688
|
+
`const diagnostics = effectLspPatchUtils().extractDiagnosticsForExitStatus(${moduleName === "typescript" ? "module.exports" : "effectLspTypeScriptApis()"}, program, tscDiagnostics, "${moduleName}")
|
|
41689
|
+
`,
|
|
41690
|
+
"\n",
|
|
41691
|
+
version
|
|
41692
|
+
)
|
|
41693
|
+
);
|
|
41588
41694
|
let eofPos = sourceFile.text.lastIndexOf("// src/") - 1;
|
|
41589
41695
|
if (eofPos < 0) eofPos = sourceFile.text.length;
|
|
41590
41696
|
if (moduleName !== "typescript") {
|