@effect/language-service 0.69.0 → 0.69.2
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/cli.js +137 -27
- package/cli.js.map +1 -1
- package/effect-lsp-patch-utils.js +136 -26
- package/effect-lsp-patch-utils.js.map +1 -1
- package/index.js +136 -26
- package/index.js.map +1 -1
- package/package.json +1 -1
- package/transform.js +136 -26
- package/transform.js.map +1 -1
|
@@ -1225,6 +1225,7 @@ var defaults = {
|
|
|
1225
1225
|
}],
|
|
1226
1226
|
extendedKeyDetection: false,
|
|
1227
1227
|
pipeableMinArgCount: 2,
|
|
1228
|
+
effectFn: ["span"],
|
|
1228
1229
|
layerGraphFollowDepth: 0,
|
|
1229
1230
|
mermaidProvider: "mermaid.live"
|
|
1230
1231
|
};
|
|
@@ -1264,6 +1265,7 @@ function parse(config) {
|
|
|
1264
1265
|
keyPatterns: isObject(config) && hasProperty(config, "keyPatterns") && isArray(config.keyPatterns) ? parseKeyPatterns(config.keyPatterns) : defaults.keyPatterns,
|
|
1265
1266
|
extendedKeyDetection: isObject(config) && hasProperty(config, "extendedKeyDetection") && isBoolean(config.extendedKeyDetection) ? config.extendedKeyDetection : defaults.extendedKeyDetection,
|
|
1266
1267
|
pipeableMinArgCount: isObject(config) && hasProperty(config, "pipeableMinArgCount") && isNumber(config.pipeableMinArgCount) ? config.pipeableMinArgCount : defaults.pipeableMinArgCount,
|
|
1268
|
+
effectFn: isObject(config) && hasProperty(config, "effectFn") && isArray(config.effectFn) && config.effectFn.every(isString) ? config.effectFn.map((_) => _.toLowerCase()) : defaults.effectFn,
|
|
1267
1269
|
layerGraphFollowDepth: isObject(config) && hasProperty(config, "layerGraphFollowDepth") && isNumber(config.layerGraphFollowDepth) ? config.layerGraphFollowDepth : defaults.layerGraphFollowDepth,
|
|
1268
1270
|
mermaidProvider: isObject(config) && hasProperty(config, "mermaidProvider") && isString(config.mermaidProvider) ? config.mermaidProvider : defaults.mermaidProvider
|
|
1269
1271
|
};
|
|
@@ -3079,6 +3081,7 @@ function make2(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
|
|
|
3079
3081
|
node
|
|
3080
3082
|
);
|
|
3081
3083
|
}
|
|
3084
|
+
const traceExpression = ts.isCallExpression(node.expression) && node.expression.arguments.length > 0 ? node.expression.arguments[0] : void 0;
|
|
3082
3085
|
const propertyAccess = expressionToTest;
|
|
3083
3086
|
const pipeArguments2 = node.arguments.slice(1);
|
|
3084
3087
|
return pipe(
|
|
@@ -3088,7 +3091,8 @@ function make2(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
|
|
|
3088
3091
|
generatorFunction,
|
|
3089
3092
|
effectModule: propertyAccess.expression,
|
|
3090
3093
|
body: generatorFunction.body,
|
|
3091
|
-
pipeArguments: pipeArguments2
|
|
3094
|
+
pipeArguments: pipeArguments2,
|
|
3095
|
+
traceExpression
|
|
3092
3096
|
}))
|
|
3093
3097
|
);
|
|
3094
3098
|
},
|
|
@@ -3171,6 +3175,7 @@ function make2(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
|
|
|
3171
3175
|
if (!ts.isPropertyAccessExpression(expressionToTest)) {
|
|
3172
3176
|
return typeParserIssue("Node is not a property access expression", void 0, node);
|
|
3173
3177
|
}
|
|
3178
|
+
const traceExpression = ts.isCallExpression(node.expression) && node.expression.arguments.length > 0 ? node.expression.arguments[0] : void 0;
|
|
3174
3179
|
const propertyAccess = expressionToTest;
|
|
3175
3180
|
const pipeArguments2 = node.arguments.slice(1);
|
|
3176
3181
|
return pipe(
|
|
@@ -3179,7 +3184,8 @@ function make2(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
|
|
|
3179
3184
|
node,
|
|
3180
3185
|
effectModule: propertyAccess.expression,
|
|
3181
3186
|
regularFunction,
|
|
3182
|
-
pipeArguments: pipeArguments2
|
|
3187
|
+
pipeArguments: pipeArguments2,
|
|
3188
|
+
traceExpression
|
|
3183
3189
|
}))
|
|
3184
3190
|
);
|
|
3185
3191
|
},
|
|
@@ -4888,7 +4894,8 @@ var effectFnIife = createDiagnostic({
|
|
|
4888
4894
|
kind: "fn",
|
|
4889
4895
|
effectModule: result.effectModule,
|
|
4890
4896
|
generatorFunction: result.generatorFunction,
|
|
4891
|
-
pipeArguments: result.pipeArguments
|
|
4897
|
+
pipeArguments: result.pipeArguments,
|
|
4898
|
+
traceExpression: result.traceExpression
|
|
4892
4899
|
})),
|
|
4893
4900
|
orElse2(
|
|
4894
4901
|
() => pipe(
|
|
@@ -4897,7 +4904,8 @@ var effectFnIife = createDiagnostic({
|
|
|
4897
4904
|
kind: "fnUntraced",
|
|
4898
4905
|
effectModule: result.effectModule,
|
|
4899
4906
|
generatorFunction: result.generatorFunction,
|
|
4900
|
-
pipeArguments: result.pipeArguments
|
|
4907
|
+
pipeArguments: result.pipeArguments,
|
|
4908
|
+
traceExpression: void 0
|
|
4901
4909
|
}))
|
|
4902
4910
|
)
|
|
4903
4911
|
),
|
|
@@ -4908,14 +4916,15 @@ var effectFnIife = createDiagnostic({
|
|
|
4908
4916
|
kind: "fn",
|
|
4909
4917
|
effectModule: result.effectModule,
|
|
4910
4918
|
generatorFunction: void 0,
|
|
4911
|
-
pipeArguments: result.pipeArguments
|
|
4919
|
+
pipeArguments: result.pipeArguments,
|
|
4920
|
+
traceExpression: result.traceExpression
|
|
4912
4921
|
}))
|
|
4913
4922
|
)
|
|
4914
4923
|
),
|
|
4915
4924
|
option
|
|
4916
4925
|
);
|
|
4917
4926
|
if (isNone2(parsed)) continue;
|
|
4918
|
-
const { effectModule, generatorFunction, kind, pipeArguments: pipeArguments2 } = parsed.value;
|
|
4927
|
+
const { effectModule, generatorFunction, kind, pipeArguments: pipeArguments2, traceExpression } = parsed.value;
|
|
4919
4928
|
const effectModuleName = ts.isIdentifier(effectModule) ? ts.idText(effectModule) : sourceEffectModuleName;
|
|
4920
4929
|
const fixes = [];
|
|
4921
4930
|
if (generatorFunction && generatorFunction.parameters.length === 0) {
|
|
@@ -4944,9 +4953,10 @@ var effectFnIife = createDiagnostic({
|
|
|
4944
4953
|
})
|
|
4945
4954
|
});
|
|
4946
4955
|
}
|
|
4956
|
+
const traceExpressionText = traceExpression ? sourceFile.text.slice(traceExpression.pos, traceExpression.end) : void 0;
|
|
4947
4957
|
report({
|
|
4948
4958
|
location: node,
|
|
4949
|
-
messageText: `${effectModuleName}.${kind} returns a reusable function that can take arguments, but here it's called immediately. Use Effect.gen instead
|
|
4959
|
+
messageText: `${effectModuleName}.${kind} returns a reusable function that can take arguments, but here it's called immediately. Use Effect.gen instead${traceExpressionText ? ` with Effect.withSpan(${traceExpressionText}) piped in the end to mantain tracing spans` : ``}.`,
|
|
4950
4960
|
fixes
|
|
4951
4961
|
});
|
|
4952
4962
|
}
|
|
@@ -4965,6 +4975,7 @@ var effectFnOpportunity = createDiagnostic({
|
|
|
4965
4975
|
const typeCheckerUtils = yield* service(TypeCheckerUtils);
|
|
4966
4976
|
const typeParser = yield* service(TypeParser);
|
|
4967
4977
|
const tsUtils = yield* service(TypeScriptUtils);
|
|
4978
|
+
const pluginOptions = yield* service(LanguageServicePluginOptions);
|
|
4968
4979
|
const sourceEffectModuleName = tsUtils.findImportedModuleIdentifierByPackageAndNameOrBarrel(
|
|
4969
4980
|
sourceFile,
|
|
4970
4981
|
"effect",
|
|
@@ -5041,6 +5052,18 @@ var effectFnOpportunity = createDiagnostic({
|
|
|
5041
5052
|
}
|
|
5042
5053
|
return false;
|
|
5043
5054
|
};
|
|
5055
|
+
const tryExtractWithSpanExpression = (expr) => gen(function* () {
|
|
5056
|
+
if (!ts.isCallExpression(expr)) return void 0;
|
|
5057
|
+
const callee = expr.expression;
|
|
5058
|
+
const isWithSpan = yield* pipe(
|
|
5059
|
+
typeParser.isNodeReferenceToEffectModuleApi("withSpan")(callee),
|
|
5060
|
+
map4(() => true),
|
|
5061
|
+
orElse2(() => succeed(false))
|
|
5062
|
+
);
|
|
5063
|
+
if (!isWithSpan) return void 0;
|
|
5064
|
+
if (expr.arguments.length === 0) return void 0;
|
|
5065
|
+
return expr.arguments[0];
|
|
5066
|
+
});
|
|
5044
5067
|
const tryParseGenOpportunity = (fnNode) => gen(function* () {
|
|
5045
5068
|
const bodyExpression = getBodyExpression(fnNode);
|
|
5046
5069
|
if (!bodyExpression) return yield* TypeParserIssue.issue;
|
|
@@ -5051,7 +5074,15 @@ var effectFnOpportunity = createDiagnostic({
|
|
|
5051
5074
|
);
|
|
5052
5075
|
const { effectModule, generatorFunction } = yield* typeParser.effectGen(subject);
|
|
5053
5076
|
const effectModuleName = ts.isIdentifier(effectModule) ? ts.idText(effectModule) : sourceEffectModuleName;
|
|
5054
|
-
|
|
5077
|
+
let explicitTraceExpression;
|
|
5078
|
+
if (pipeArguments2.length > 0) {
|
|
5079
|
+
const lastArg = pipeArguments2[pipeArguments2.length - 1];
|
|
5080
|
+
const withSpanExpr = yield* tryExtractWithSpanExpression(lastArg);
|
|
5081
|
+
if (withSpanExpr) {
|
|
5082
|
+
explicitTraceExpression = withSpanExpr;
|
|
5083
|
+
}
|
|
5084
|
+
}
|
|
5085
|
+
return { effectModuleName, generatorFunction, pipeArguments: pipeArguments2, explicitTraceExpression };
|
|
5055
5086
|
});
|
|
5056
5087
|
const isInsideEffectFn = (fnNode) => {
|
|
5057
5088
|
const parent = fnNode.parent;
|
|
@@ -5079,6 +5110,9 @@ var effectFnOpportunity = createDiagnostic({
|
|
|
5079
5110
|
if (ts.isFunctionExpression(node) && node.name) {
|
|
5080
5111
|
return yield* TypeParserIssue.issue;
|
|
5081
5112
|
}
|
|
5113
|
+
if (node.type) {
|
|
5114
|
+
return yield* TypeParserIssue.issue;
|
|
5115
|
+
}
|
|
5082
5116
|
if (yield* isInsideEffectFn(node)) {
|
|
5083
5117
|
return yield* TypeParserIssue.issue;
|
|
5084
5118
|
}
|
|
@@ -5106,7 +5140,8 @@ var effectFnOpportunity = createDiagnostic({
|
|
|
5106
5140
|
return succeed({
|
|
5107
5141
|
effectModuleName: sourceEffectModuleName,
|
|
5108
5142
|
pipeArguments: [],
|
|
5109
|
-
generatorFunction: void 0
|
|
5143
|
+
generatorFunction: void 0,
|
|
5144
|
+
explicitTraceExpression: void 0
|
|
5110
5145
|
});
|
|
5111
5146
|
})
|
|
5112
5147
|
);
|
|
@@ -5114,7 +5149,8 @@ var effectFnOpportunity = createDiagnostic({
|
|
|
5114
5149
|
node,
|
|
5115
5150
|
nameIdentifier,
|
|
5116
5151
|
effectModuleName: opportunity.effectModuleName,
|
|
5117
|
-
traceName,
|
|
5152
|
+
inferredTraceName: traceName,
|
|
5153
|
+
explicitTraceExpression: opportunity.explicitTraceExpression,
|
|
5118
5154
|
pipeArguments: opportunity.pipeArguments,
|
|
5119
5155
|
generatorFunction: opportunity.generatorFunction,
|
|
5120
5156
|
hasParamsInPipeArgs: areParametersReferencedIn(node, opportunity.pipeArguments)
|
|
@@ -5133,7 +5169,7 @@ var effectFnOpportunity = createDiagnostic({
|
|
|
5133
5169
|
if (ts.isArrowFunction(node)) return false;
|
|
5134
5170
|
return node.asteriskToken !== void 0;
|
|
5135
5171
|
};
|
|
5136
|
-
const createEffectFnNode = (originalNode, innerFunction, effectModuleName,
|
|
5172
|
+
const createEffectFnNode = (originalNode, innerFunction, effectModuleName, traceNameOrExpression, pipeArguments2) => {
|
|
5137
5173
|
const isGenerator = isGeneratorFunction(innerFunction);
|
|
5138
5174
|
const newFunction = ts.factory.createFunctionExpression(
|
|
5139
5175
|
void 0,
|
|
@@ -5148,11 +5184,12 @@ var effectFnOpportunity = createDiagnostic({
|
|
|
5148
5184
|
ts.factory.createIdentifier(effectModuleName),
|
|
5149
5185
|
"fn"
|
|
5150
5186
|
);
|
|
5151
|
-
if (
|
|
5187
|
+
if (traceNameOrExpression) {
|
|
5188
|
+
const traceArg = typeof traceNameOrExpression === "string" ? ts.factory.createStringLiteral(traceNameOrExpression) : traceNameOrExpression;
|
|
5152
5189
|
fnExpression = ts.factory.createCallExpression(
|
|
5153
5190
|
fnExpression,
|
|
5154
5191
|
void 0,
|
|
5155
|
-
[
|
|
5192
|
+
[traceArg]
|
|
5156
5193
|
);
|
|
5157
5194
|
}
|
|
5158
5195
|
const effectFnCall = ts.factory.createCallExpression(fnExpression, void 0, [newFunction, ...pipeArguments2]);
|
|
@@ -5194,19 +5231,35 @@ var effectFnOpportunity = createDiagnostic({
|
|
|
5194
5231
|
const target = yield* pipe(parseEffectFnOpportunityTarget(node), option);
|
|
5195
5232
|
if (isNone2(target)) continue;
|
|
5196
5233
|
if (target.value.hasParamsInPipeArgs) continue;
|
|
5197
|
-
const {
|
|
5234
|
+
const {
|
|
5235
|
+
effectModuleName,
|
|
5236
|
+
explicitTraceExpression,
|
|
5237
|
+
inferredTraceName,
|
|
5238
|
+
nameIdentifier,
|
|
5239
|
+
node: targetNode,
|
|
5240
|
+
pipeArguments: pipeArguments2
|
|
5241
|
+
} = target.value;
|
|
5198
5242
|
const innerFunction = target.value.generatorFunction ?? targetNode;
|
|
5199
5243
|
const fixes = [];
|
|
5200
|
-
|
|
5201
|
-
|
|
5202
|
-
|
|
5203
|
-
|
|
5204
|
-
|
|
5205
|
-
|
|
5206
|
-
|
|
5207
|
-
|
|
5208
|
-
|
|
5209
|
-
|
|
5244
|
+
if (pluginOptions.effectFn.includes("span") && explicitTraceExpression) {
|
|
5245
|
+
fixes.push({
|
|
5246
|
+
fixName: "effectFnOpportunity_toEffectFnWithSpan",
|
|
5247
|
+
description: "Convert to Effect.fn (with span from withSpan)",
|
|
5248
|
+
apply: gen(function* () {
|
|
5249
|
+
const changeTracker = yield* service(ChangeTracker);
|
|
5250
|
+
const finalPipeArguments = pipeArguments2.slice(0, -1);
|
|
5251
|
+
const newNode = createEffectFnNode(
|
|
5252
|
+
targetNode,
|
|
5253
|
+
innerFunction,
|
|
5254
|
+
effectModuleName,
|
|
5255
|
+
explicitTraceExpression,
|
|
5256
|
+
finalPipeArguments
|
|
5257
|
+
);
|
|
5258
|
+
changeTracker.replaceNode(sourceFile, targetNode, newNode);
|
|
5259
|
+
})
|
|
5260
|
+
});
|
|
5261
|
+
}
|
|
5262
|
+
if (pluginOptions.effectFn.includes("untraced") && target.value.generatorFunction) {
|
|
5210
5263
|
fixes.push({
|
|
5211
5264
|
fixName: "effectFnOpportunity_toEffectFnUntraced",
|
|
5212
5265
|
description: "Convert to Effect.fnUntraced",
|
|
@@ -5217,10 +5270,67 @@ var effectFnOpportunity = createDiagnostic({
|
|
|
5217
5270
|
})
|
|
5218
5271
|
});
|
|
5219
5272
|
}
|
|
5220
|
-
|
|
5273
|
+
if (pluginOptions.effectFn.includes("no-span")) {
|
|
5274
|
+
fixes.push({
|
|
5275
|
+
fixName: "effectFnOpportunity_toEffectFnNoSpan",
|
|
5276
|
+
description: "Convert to Effect.fn (no span)",
|
|
5277
|
+
apply: gen(function* () {
|
|
5278
|
+
const changeTracker = yield* service(ChangeTracker);
|
|
5279
|
+
const newNode = createEffectFnNode(targetNode, innerFunction, effectModuleName, void 0, pipeArguments2);
|
|
5280
|
+
changeTracker.replaceNode(sourceFile, targetNode, newNode);
|
|
5281
|
+
})
|
|
5282
|
+
});
|
|
5283
|
+
}
|
|
5284
|
+
if (pluginOptions.effectFn.includes("inferred-span") && inferredTraceName && !explicitTraceExpression) {
|
|
5285
|
+
fixes.push({
|
|
5286
|
+
fixName: "effectFnOpportunity_toEffectFnSpanInferred",
|
|
5287
|
+
description: `Convert to Effect.fn("${inferredTraceName}")`,
|
|
5288
|
+
apply: gen(function* () {
|
|
5289
|
+
const changeTracker = yield* service(ChangeTracker);
|
|
5290
|
+
const newNode = createEffectFnNode(
|
|
5291
|
+
targetNode,
|
|
5292
|
+
innerFunction,
|
|
5293
|
+
effectModuleName,
|
|
5294
|
+
inferredTraceName,
|
|
5295
|
+
pipeArguments2
|
|
5296
|
+
);
|
|
5297
|
+
changeTracker.replaceNode(sourceFile, targetNode, newNode);
|
|
5298
|
+
})
|
|
5299
|
+
});
|
|
5300
|
+
}
|
|
5301
|
+
if (fixes.length === 0) continue;
|
|
5302
|
+
const generateExpectedSignature = () => {
|
|
5303
|
+
const firstFix = fixes[0];
|
|
5304
|
+
if (!firstFix) return "Effect.fn(function*() { ... })";
|
|
5305
|
+
const typeParamNames = targetNode.typeParameters ? `<${targetNode.typeParameters.map((tp) => ts.idText(tp.name)).join(", ")}>` : "";
|
|
5306
|
+
const paramNames = targetNode.parameters.map((param) => {
|
|
5307
|
+
if (ts.isIdentifier(param.name)) {
|
|
5308
|
+
return ts.idText(param.name);
|
|
5309
|
+
}
|
|
5310
|
+
return "_";
|
|
5311
|
+
}).join(", ");
|
|
5312
|
+
const fnSignature = `function*${typeParamNames}(${paramNames}) { ... }`;
|
|
5313
|
+
const pipeArgsForWithSpan = pipeArguments2.slice(0, -1);
|
|
5314
|
+
const pipeArgsSuffix = (args2) => args2.length > 0 ? ", ...pipeTransformations" : "";
|
|
5315
|
+
switch (firstFix.fixName) {
|
|
5316
|
+
case "effectFnOpportunity_toEffectFnWithSpan": {
|
|
5317
|
+
const traceName = explicitTraceExpression ? sourceFile.text.slice(explicitTraceExpression.pos, explicitTraceExpression.end).trim() : void 0;
|
|
5318
|
+
return `${effectModuleName}.fn(${traceName})(${fnSignature}${pipeArgsSuffix(pipeArgsForWithSpan)})`;
|
|
5319
|
+
}
|
|
5320
|
+
case "effectFnOpportunity_toEffectFnUntraced":
|
|
5321
|
+
return `${effectModuleName}.fnUntraced(${fnSignature}${pipeArgsSuffix(pipeArguments2)})`;
|
|
5322
|
+
case "effectFnOpportunity_toEffectFnNoSpan":
|
|
5323
|
+
return `${effectModuleName}.fn(${fnSignature}${pipeArgsSuffix(pipeArguments2)})`;
|
|
5324
|
+
case "effectFnOpportunity_toEffectFnSpanInferred":
|
|
5325
|
+
return `${effectModuleName}.fn("${inferredTraceName}")(${fnSignature}${pipeArgsSuffix(pipeArguments2)})`;
|
|
5326
|
+
default:
|
|
5327
|
+
return `${effectModuleName}.fn(${fnSignature})`;
|
|
5328
|
+
}
|
|
5329
|
+
};
|
|
5330
|
+
const expectedSignature = generateExpectedSignature();
|
|
5221
5331
|
report({
|
|
5222
5332
|
location: nameIdentifier ?? targetNode,
|
|
5223
|
-
messageText:
|
|
5333
|
+
messageText: `Can be rewritten as a reusable function: ${expectedSignature}`,
|
|
5224
5334
|
fixes
|
|
5225
5335
|
});
|
|
5226
5336
|
}
|