@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
package/package.json
CHANGED
package/transform.js
CHANGED
|
@@ -1226,6 +1226,7 @@ var defaults = {
|
|
|
1226
1226
|
}],
|
|
1227
1227
|
extendedKeyDetection: false,
|
|
1228
1228
|
pipeableMinArgCount: 2,
|
|
1229
|
+
effectFn: ["span"],
|
|
1229
1230
|
layerGraphFollowDepth: 0,
|
|
1230
1231
|
mermaidProvider: "mermaid.live"
|
|
1231
1232
|
};
|
|
@@ -1265,6 +1266,7 @@ function parse(config) {
|
|
|
1265
1266
|
keyPatterns: isObject(config) && hasProperty(config, "keyPatterns") && isArray(config.keyPatterns) ? parseKeyPatterns(config.keyPatterns) : defaults.keyPatterns,
|
|
1266
1267
|
extendedKeyDetection: isObject(config) && hasProperty(config, "extendedKeyDetection") && isBoolean(config.extendedKeyDetection) ? config.extendedKeyDetection : defaults.extendedKeyDetection,
|
|
1267
1268
|
pipeableMinArgCount: isObject(config) && hasProperty(config, "pipeableMinArgCount") && isNumber(config.pipeableMinArgCount) ? config.pipeableMinArgCount : defaults.pipeableMinArgCount,
|
|
1269
|
+
effectFn: isObject(config) && hasProperty(config, "effectFn") && isArray(config.effectFn) && config.effectFn.every(isString) ? config.effectFn.map((_) => _.toLowerCase()) : defaults.effectFn,
|
|
1268
1270
|
layerGraphFollowDepth: isObject(config) && hasProperty(config, "layerGraphFollowDepth") && isNumber(config.layerGraphFollowDepth) ? config.layerGraphFollowDepth : defaults.layerGraphFollowDepth,
|
|
1269
1271
|
mermaidProvider: isObject(config) && hasProperty(config, "mermaidProvider") && isString(config.mermaidProvider) ? config.mermaidProvider : defaults.mermaidProvider
|
|
1270
1272
|
};
|
|
@@ -3075,6 +3077,7 @@ function make2(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
|
|
|
3075
3077
|
node
|
|
3076
3078
|
);
|
|
3077
3079
|
}
|
|
3080
|
+
const traceExpression = ts.isCallExpression(node.expression) && node.expression.arguments.length > 0 ? node.expression.arguments[0] : void 0;
|
|
3078
3081
|
const propertyAccess = expressionToTest;
|
|
3079
3082
|
const pipeArguments2 = node.arguments.slice(1);
|
|
3080
3083
|
return pipe(
|
|
@@ -3084,7 +3087,8 @@ function make2(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
|
|
|
3084
3087
|
generatorFunction,
|
|
3085
3088
|
effectModule: propertyAccess.expression,
|
|
3086
3089
|
body: generatorFunction.body,
|
|
3087
|
-
pipeArguments: pipeArguments2
|
|
3090
|
+
pipeArguments: pipeArguments2,
|
|
3091
|
+
traceExpression
|
|
3088
3092
|
}))
|
|
3089
3093
|
);
|
|
3090
3094
|
},
|
|
@@ -3167,6 +3171,7 @@ function make2(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
|
|
|
3167
3171
|
if (!ts.isPropertyAccessExpression(expressionToTest)) {
|
|
3168
3172
|
return typeParserIssue("Node is not a property access expression", void 0, node);
|
|
3169
3173
|
}
|
|
3174
|
+
const traceExpression = ts.isCallExpression(node.expression) && node.expression.arguments.length > 0 ? node.expression.arguments[0] : void 0;
|
|
3170
3175
|
const propertyAccess = expressionToTest;
|
|
3171
3176
|
const pipeArguments2 = node.arguments.slice(1);
|
|
3172
3177
|
return pipe(
|
|
@@ -3175,7 +3180,8 @@ function make2(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
|
|
|
3175
3180
|
node,
|
|
3176
3181
|
effectModule: propertyAccess.expression,
|
|
3177
3182
|
regularFunction,
|
|
3178
|
-
pipeArguments: pipeArguments2
|
|
3183
|
+
pipeArguments: pipeArguments2,
|
|
3184
|
+
traceExpression
|
|
3179
3185
|
}))
|
|
3180
3186
|
);
|
|
3181
3187
|
},
|
|
@@ -4884,7 +4890,8 @@ var effectFnIife = createDiagnostic({
|
|
|
4884
4890
|
kind: "fn",
|
|
4885
4891
|
effectModule: result.effectModule,
|
|
4886
4892
|
generatorFunction: result.generatorFunction,
|
|
4887
|
-
pipeArguments: result.pipeArguments
|
|
4893
|
+
pipeArguments: result.pipeArguments,
|
|
4894
|
+
traceExpression: result.traceExpression
|
|
4888
4895
|
})),
|
|
4889
4896
|
orElse2(
|
|
4890
4897
|
() => pipe(
|
|
@@ -4893,7 +4900,8 @@ var effectFnIife = createDiagnostic({
|
|
|
4893
4900
|
kind: "fnUntraced",
|
|
4894
4901
|
effectModule: result.effectModule,
|
|
4895
4902
|
generatorFunction: result.generatorFunction,
|
|
4896
|
-
pipeArguments: result.pipeArguments
|
|
4903
|
+
pipeArguments: result.pipeArguments,
|
|
4904
|
+
traceExpression: void 0
|
|
4897
4905
|
}))
|
|
4898
4906
|
)
|
|
4899
4907
|
),
|
|
@@ -4904,14 +4912,15 @@ var effectFnIife = createDiagnostic({
|
|
|
4904
4912
|
kind: "fn",
|
|
4905
4913
|
effectModule: result.effectModule,
|
|
4906
4914
|
generatorFunction: void 0,
|
|
4907
|
-
pipeArguments: result.pipeArguments
|
|
4915
|
+
pipeArguments: result.pipeArguments,
|
|
4916
|
+
traceExpression: result.traceExpression
|
|
4908
4917
|
}))
|
|
4909
4918
|
)
|
|
4910
4919
|
),
|
|
4911
4920
|
option
|
|
4912
4921
|
);
|
|
4913
4922
|
if (isNone2(parsed)) continue;
|
|
4914
|
-
const { effectModule, generatorFunction, kind, pipeArguments: pipeArguments2 } = parsed.value;
|
|
4923
|
+
const { effectModule, generatorFunction, kind, pipeArguments: pipeArguments2, traceExpression } = parsed.value;
|
|
4915
4924
|
const effectModuleName = ts.isIdentifier(effectModule) ? ts.idText(effectModule) : sourceEffectModuleName;
|
|
4916
4925
|
const fixes = [];
|
|
4917
4926
|
if (generatorFunction && generatorFunction.parameters.length === 0) {
|
|
@@ -4940,9 +4949,10 @@ var effectFnIife = createDiagnostic({
|
|
|
4940
4949
|
})
|
|
4941
4950
|
});
|
|
4942
4951
|
}
|
|
4952
|
+
const traceExpressionText = traceExpression ? sourceFile.text.slice(traceExpression.pos, traceExpression.end) : void 0;
|
|
4943
4953
|
report({
|
|
4944
4954
|
location: node,
|
|
4945
|
-
messageText: `${effectModuleName}.${kind} returns a reusable function that can take arguments, but here it's called immediately. Use Effect.gen instead
|
|
4955
|
+
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` : ``}.`,
|
|
4946
4956
|
fixes
|
|
4947
4957
|
});
|
|
4948
4958
|
}
|
|
@@ -4961,6 +4971,7 @@ var effectFnOpportunity = createDiagnostic({
|
|
|
4961
4971
|
const typeCheckerUtils = yield* service(TypeCheckerUtils);
|
|
4962
4972
|
const typeParser = yield* service(TypeParser);
|
|
4963
4973
|
const tsUtils = yield* service(TypeScriptUtils);
|
|
4974
|
+
const pluginOptions = yield* service(LanguageServicePluginOptions);
|
|
4964
4975
|
const sourceEffectModuleName = tsUtils.findImportedModuleIdentifierByPackageAndNameOrBarrel(
|
|
4965
4976
|
sourceFile,
|
|
4966
4977
|
"effect",
|
|
@@ -5037,6 +5048,18 @@ var effectFnOpportunity = createDiagnostic({
|
|
|
5037
5048
|
}
|
|
5038
5049
|
return false;
|
|
5039
5050
|
};
|
|
5051
|
+
const tryExtractWithSpanExpression = (expr) => gen(function* () {
|
|
5052
|
+
if (!ts.isCallExpression(expr)) return void 0;
|
|
5053
|
+
const callee = expr.expression;
|
|
5054
|
+
const isWithSpan = yield* pipe(
|
|
5055
|
+
typeParser.isNodeReferenceToEffectModuleApi("withSpan")(callee),
|
|
5056
|
+
map4(() => true),
|
|
5057
|
+
orElse2(() => succeed(false))
|
|
5058
|
+
);
|
|
5059
|
+
if (!isWithSpan) return void 0;
|
|
5060
|
+
if (expr.arguments.length === 0) return void 0;
|
|
5061
|
+
return expr.arguments[0];
|
|
5062
|
+
});
|
|
5040
5063
|
const tryParseGenOpportunity = (fnNode) => gen(function* () {
|
|
5041
5064
|
const bodyExpression = getBodyExpression(fnNode);
|
|
5042
5065
|
if (!bodyExpression) return yield* TypeParserIssue.issue;
|
|
@@ -5047,7 +5070,15 @@ var effectFnOpportunity = createDiagnostic({
|
|
|
5047
5070
|
);
|
|
5048
5071
|
const { effectModule, generatorFunction } = yield* typeParser.effectGen(subject);
|
|
5049
5072
|
const effectModuleName = ts.isIdentifier(effectModule) ? ts.idText(effectModule) : sourceEffectModuleName;
|
|
5050
|
-
|
|
5073
|
+
let explicitTraceExpression;
|
|
5074
|
+
if (pipeArguments2.length > 0) {
|
|
5075
|
+
const lastArg = pipeArguments2[pipeArguments2.length - 1];
|
|
5076
|
+
const withSpanExpr = yield* tryExtractWithSpanExpression(lastArg);
|
|
5077
|
+
if (withSpanExpr) {
|
|
5078
|
+
explicitTraceExpression = withSpanExpr;
|
|
5079
|
+
}
|
|
5080
|
+
}
|
|
5081
|
+
return { effectModuleName, generatorFunction, pipeArguments: pipeArguments2, explicitTraceExpression };
|
|
5051
5082
|
});
|
|
5052
5083
|
const isInsideEffectFn = (fnNode) => {
|
|
5053
5084
|
const parent = fnNode.parent;
|
|
@@ -5075,6 +5106,9 @@ var effectFnOpportunity = createDiagnostic({
|
|
|
5075
5106
|
if (ts.isFunctionExpression(node) && node.name) {
|
|
5076
5107
|
return yield* TypeParserIssue.issue;
|
|
5077
5108
|
}
|
|
5109
|
+
if (node.type) {
|
|
5110
|
+
return yield* TypeParserIssue.issue;
|
|
5111
|
+
}
|
|
5078
5112
|
if (yield* isInsideEffectFn(node)) {
|
|
5079
5113
|
return yield* TypeParserIssue.issue;
|
|
5080
5114
|
}
|
|
@@ -5102,7 +5136,8 @@ var effectFnOpportunity = createDiagnostic({
|
|
|
5102
5136
|
return succeed({
|
|
5103
5137
|
effectModuleName: sourceEffectModuleName,
|
|
5104
5138
|
pipeArguments: [],
|
|
5105
|
-
generatorFunction: void 0
|
|
5139
|
+
generatorFunction: void 0,
|
|
5140
|
+
explicitTraceExpression: void 0
|
|
5106
5141
|
});
|
|
5107
5142
|
})
|
|
5108
5143
|
);
|
|
@@ -5110,7 +5145,8 @@ var effectFnOpportunity = createDiagnostic({
|
|
|
5110
5145
|
node,
|
|
5111
5146
|
nameIdentifier,
|
|
5112
5147
|
effectModuleName: opportunity.effectModuleName,
|
|
5113
|
-
traceName,
|
|
5148
|
+
inferredTraceName: traceName,
|
|
5149
|
+
explicitTraceExpression: opportunity.explicitTraceExpression,
|
|
5114
5150
|
pipeArguments: opportunity.pipeArguments,
|
|
5115
5151
|
generatorFunction: opportunity.generatorFunction,
|
|
5116
5152
|
hasParamsInPipeArgs: areParametersReferencedIn(node, opportunity.pipeArguments)
|
|
@@ -5129,7 +5165,7 @@ var effectFnOpportunity = createDiagnostic({
|
|
|
5129
5165
|
if (ts.isArrowFunction(node)) return false;
|
|
5130
5166
|
return node.asteriskToken !== void 0;
|
|
5131
5167
|
};
|
|
5132
|
-
const createEffectFnNode = (originalNode, innerFunction, effectModuleName,
|
|
5168
|
+
const createEffectFnNode = (originalNode, innerFunction, effectModuleName, traceNameOrExpression, pipeArguments2) => {
|
|
5133
5169
|
const isGenerator = isGeneratorFunction(innerFunction);
|
|
5134
5170
|
const newFunction = ts.factory.createFunctionExpression(
|
|
5135
5171
|
void 0,
|
|
@@ -5144,11 +5180,12 @@ var effectFnOpportunity = createDiagnostic({
|
|
|
5144
5180
|
ts.factory.createIdentifier(effectModuleName),
|
|
5145
5181
|
"fn"
|
|
5146
5182
|
);
|
|
5147
|
-
if (
|
|
5183
|
+
if (traceNameOrExpression) {
|
|
5184
|
+
const traceArg = typeof traceNameOrExpression === "string" ? ts.factory.createStringLiteral(traceNameOrExpression) : traceNameOrExpression;
|
|
5148
5185
|
fnExpression = ts.factory.createCallExpression(
|
|
5149
5186
|
fnExpression,
|
|
5150
5187
|
void 0,
|
|
5151
|
-
[
|
|
5188
|
+
[traceArg]
|
|
5152
5189
|
);
|
|
5153
5190
|
}
|
|
5154
5191
|
const effectFnCall = ts.factory.createCallExpression(fnExpression, void 0, [newFunction, ...pipeArguments2]);
|
|
@@ -5190,19 +5227,35 @@ var effectFnOpportunity = createDiagnostic({
|
|
|
5190
5227
|
const target = yield* pipe(parseEffectFnOpportunityTarget(node), option);
|
|
5191
5228
|
if (isNone2(target)) continue;
|
|
5192
5229
|
if (target.value.hasParamsInPipeArgs) continue;
|
|
5193
|
-
const {
|
|
5230
|
+
const {
|
|
5231
|
+
effectModuleName,
|
|
5232
|
+
explicitTraceExpression,
|
|
5233
|
+
inferredTraceName,
|
|
5234
|
+
nameIdentifier,
|
|
5235
|
+
node: targetNode,
|
|
5236
|
+
pipeArguments: pipeArguments2
|
|
5237
|
+
} = target.value;
|
|
5194
5238
|
const innerFunction = target.value.generatorFunction ?? targetNode;
|
|
5195
5239
|
const fixes = [];
|
|
5196
|
-
|
|
5197
|
-
|
|
5198
|
-
|
|
5199
|
-
|
|
5200
|
-
|
|
5201
|
-
|
|
5202
|
-
|
|
5203
|
-
|
|
5204
|
-
|
|
5205
|
-
|
|
5240
|
+
if (pluginOptions.effectFn.includes("span") && explicitTraceExpression) {
|
|
5241
|
+
fixes.push({
|
|
5242
|
+
fixName: "effectFnOpportunity_toEffectFnWithSpan",
|
|
5243
|
+
description: "Convert to Effect.fn (with span from withSpan)",
|
|
5244
|
+
apply: gen(function* () {
|
|
5245
|
+
const changeTracker = yield* service(ChangeTracker);
|
|
5246
|
+
const finalPipeArguments = pipeArguments2.slice(0, -1);
|
|
5247
|
+
const newNode = createEffectFnNode(
|
|
5248
|
+
targetNode,
|
|
5249
|
+
innerFunction,
|
|
5250
|
+
effectModuleName,
|
|
5251
|
+
explicitTraceExpression,
|
|
5252
|
+
finalPipeArguments
|
|
5253
|
+
);
|
|
5254
|
+
changeTracker.replaceNode(sourceFile, targetNode, newNode);
|
|
5255
|
+
})
|
|
5256
|
+
});
|
|
5257
|
+
}
|
|
5258
|
+
if (pluginOptions.effectFn.includes("untraced") && target.value.generatorFunction) {
|
|
5206
5259
|
fixes.push({
|
|
5207
5260
|
fixName: "effectFnOpportunity_toEffectFnUntraced",
|
|
5208
5261
|
description: "Convert to Effect.fnUntraced",
|
|
@@ -5213,10 +5266,67 @@ var effectFnOpportunity = createDiagnostic({
|
|
|
5213
5266
|
})
|
|
5214
5267
|
});
|
|
5215
5268
|
}
|
|
5216
|
-
|
|
5269
|
+
if (pluginOptions.effectFn.includes("no-span")) {
|
|
5270
|
+
fixes.push({
|
|
5271
|
+
fixName: "effectFnOpportunity_toEffectFnNoSpan",
|
|
5272
|
+
description: "Convert to Effect.fn (no span)",
|
|
5273
|
+
apply: gen(function* () {
|
|
5274
|
+
const changeTracker = yield* service(ChangeTracker);
|
|
5275
|
+
const newNode = createEffectFnNode(targetNode, innerFunction, effectModuleName, void 0, pipeArguments2);
|
|
5276
|
+
changeTracker.replaceNode(sourceFile, targetNode, newNode);
|
|
5277
|
+
})
|
|
5278
|
+
});
|
|
5279
|
+
}
|
|
5280
|
+
if (pluginOptions.effectFn.includes("inferred-span") && inferredTraceName && !explicitTraceExpression) {
|
|
5281
|
+
fixes.push({
|
|
5282
|
+
fixName: "effectFnOpportunity_toEffectFnSpanInferred",
|
|
5283
|
+
description: `Convert to Effect.fn("${inferredTraceName}")`,
|
|
5284
|
+
apply: gen(function* () {
|
|
5285
|
+
const changeTracker = yield* service(ChangeTracker);
|
|
5286
|
+
const newNode = createEffectFnNode(
|
|
5287
|
+
targetNode,
|
|
5288
|
+
innerFunction,
|
|
5289
|
+
effectModuleName,
|
|
5290
|
+
inferredTraceName,
|
|
5291
|
+
pipeArguments2
|
|
5292
|
+
);
|
|
5293
|
+
changeTracker.replaceNode(sourceFile, targetNode, newNode);
|
|
5294
|
+
})
|
|
5295
|
+
});
|
|
5296
|
+
}
|
|
5297
|
+
if (fixes.length === 0) continue;
|
|
5298
|
+
const generateExpectedSignature = () => {
|
|
5299
|
+
const firstFix = fixes[0];
|
|
5300
|
+
if (!firstFix) return "Effect.fn(function*() { ... })";
|
|
5301
|
+
const typeParamNames = targetNode.typeParameters ? `<${targetNode.typeParameters.map((tp) => ts.idText(tp.name)).join(", ")}>` : "";
|
|
5302
|
+
const paramNames = targetNode.parameters.map((param) => {
|
|
5303
|
+
if (ts.isIdentifier(param.name)) {
|
|
5304
|
+
return ts.idText(param.name);
|
|
5305
|
+
}
|
|
5306
|
+
return "_";
|
|
5307
|
+
}).join(", ");
|
|
5308
|
+
const fnSignature = `function*${typeParamNames}(${paramNames}) { ... }`;
|
|
5309
|
+
const pipeArgsForWithSpan = pipeArguments2.slice(0, -1);
|
|
5310
|
+
const pipeArgsSuffix = (args2) => args2.length > 0 ? ", ...pipeTransformations" : "";
|
|
5311
|
+
switch (firstFix.fixName) {
|
|
5312
|
+
case "effectFnOpportunity_toEffectFnWithSpan": {
|
|
5313
|
+
const traceName = explicitTraceExpression ? sourceFile.text.slice(explicitTraceExpression.pos, explicitTraceExpression.end).trim() : void 0;
|
|
5314
|
+
return `${effectModuleName}.fn(${traceName})(${fnSignature}${pipeArgsSuffix(pipeArgsForWithSpan)})`;
|
|
5315
|
+
}
|
|
5316
|
+
case "effectFnOpportunity_toEffectFnUntraced":
|
|
5317
|
+
return `${effectModuleName}.fnUntraced(${fnSignature}${pipeArgsSuffix(pipeArguments2)})`;
|
|
5318
|
+
case "effectFnOpportunity_toEffectFnNoSpan":
|
|
5319
|
+
return `${effectModuleName}.fn(${fnSignature}${pipeArgsSuffix(pipeArguments2)})`;
|
|
5320
|
+
case "effectFnOpportunity_toEffectFnSpanInferred":
|
|
5321
|
+
return `${effectModuleName}.fn("${inferredTraceName}")(${fnSignature}${pipeArgsSuffix(pipeArguments2)})`;
|
|
5322
|
+
default:
|
|
5323
|
+
return `${effectModuleName}.fn(${fnSignature})`;
|
|
5324
|
+
}
|
|
5325
|
+
};
|
|
5326
|
+
const expectedSignature = generateExpectedSignature();
|
|
5217
5327
|
report({
|
|
5218
5328
|
location: nameIdentifier ?? targetNode,
|
|
5219
|
-
messageText:
|
|
5329
|
+
messageText: `Can be rewritten as a reusable function: ${expectedSignature}`,
|
|
5220
5330
|
fixes
|
|
5221
5331
|
});
|
|
5222
5332
|
}
|