@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/index.js
CHANGED
|
@@ -3479,6 +3479,7 @@ var defaults = {
|
|
|
3479
3479
|
}],
|
|
3480
3480
|
extendedKeyDetection: false,
|
|
3481
3481
|
pipeableMinArgCount: 2,
|
|
3482
|
+
effectFn: ["span"],
|
|
3482
3483
|
layerGraphFollowDepth: 0,
|
|
3483
3484
|
mermaidProvider: "mermaid.live"
|
|
3484
3485
|
};
|
|
@@ -3518,6 +3519,7 @@ function parse(config) {
|
|
|
3518
3519
|
keyPatterns: isObject(config) && hasProperty(config, "keyPatterns") && isArray(config.keyPatterns) ? parseKeyPatterns(config.keyPatterns) : defaults.keyPatterns,
|
|
3519
3520
|
extendedKeyDetection: isObject(config) && hasProperty(config, "extendedKeyDetection") && isBoolean(config.extendedKeyDetection) ? config.extendedKeyDetection : defaults.extendedKeyDetection,
|
|
3520
3521
|
pipeableMinArgCount: isObject(config) && hasProperty(config, "pipeableMinArgCount") && isNumber(config.pipeableMinArgCount) ? config.pipeableMinArgCount : defaults.pipeableMinArgCount,
|
|
3522
|
+
effectFn: isObject(config) && hasProperty(config, "effectFn") && isArray(config.effectFn) && config.effectFn.every(isString) ? config.effectFn.map((_) => _.toLowerCase()) : defaults.effectFn,
|
|
3521
3523
|
layerGraphFollowDepth: isObject(config) && hasProperty(config, "layerGraphFollowDepth") && isNumber(config.layerGraphFollowDepth) ? config.layerGraphFollowDepth : defaults.layerGraphFollowDepth,
|
|
3522
3524
|
mermaidProvider: isObject(config) && hasProperty(config, "mermaidProvider") && isString(config.mermaidProvider) ? config.mermaidProvider : defaults.mermaidProvider
|
|
3523
3525
|
};
|
|
@@ -5421,6 +5423,7 @@ function make7(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
|
|
|
5421
5423
|
node
|
|
5422
5424
|
);
|
|
5423
5425
|
}
|
|
5426
|
+
const traceExpression = ts.isCallExpression(node.expression) && node.expression.arguments.length > 0 ? node.expression.arguments[0] : void 0;
|
|
5424
5427
|
const propertyAccess = expressionToTest;
|
|
5425
5428
|
const pipeArguments2 = node.arguments.slice(1);
|
|
5426
5429
|
return pipe(
|
|
@@ -5430,7 +5433,8 @@ function make7(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
|
|
|
5430
5433
|
generatorFunction,
|
|
5431
5434
|
effectModule: propertyAccess.expression,
|
|
5432
5435
|
body: generatorFunction.body,
|
|
5433
|
-
pipeArguments: pipeArguments2
|
|
5436
|
+
pipeArguments: pipeArguments2,
|
|
5437
|
+
traceExpression
|
|
5434
5438
|
}))
|
|
5435
5439
|
);
|
|
5436
5440
|
},
|
|
@@ -5513,6 +5517,7 @@ function make7(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
|
|
|
5513
5517
|
if (!ts.isPropertyAccessExpression(expressionToTest)) {
|
|
5514
5518
|
return typeParserIssue("Node is not a property access expression", void 0, node);
|
|
5515
5519
|
}
|
|
5520
|
+
const traceExpression = ts.isCallExpression(node.expression) && node.expression.arguments.length > 0 ? node.expression.arguments[0] : void 0;
|
|
5516
5521
|
const propertyAccess = expressionToTest;
|
|
5517
5522
|
const pipeArguments2 = node.arguments.slice(1);
|
|
5518
5523
|
return pipe(
|
|
@@ -5521,7 +5526,8 @@ function make7(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
|
|
|
5521
5526
|
node,
|
|
5522
5527
|
effectModule: propertyAccess.expression,
|
|
5523
5528
|
regularFunction,
|
|
5524
|
-
pipeArguments: pipeArguments2
|
|
5529
|
+
pipeArguments: pipeArguments2,
|
|
5530
|
+
traceExpression
|
|
5525
5531
|
}))
|
|
5526
5532
|
);
|
|
5527
5533
|
},
|
|
@@ -8358,7 +8364,8 @@ var effectFnIife = createDiagnostic({
|
|
|
8358
8364
|
kind: "fn",
|
|
8359
8365
|
effectModule: result.effectModule,
|
|
8360
8366
|
generatorFunction: result.generatorFunction,
|
|
8361
|
-
pipeArguments: result.pipeArguments
|
|
8367
|
+
pipeArguments: result.pipeArguments,
|
|
8368
|
+
traceExpression: result.traceExpression
|
|
8362
8369
|
})),
|
|
8363
8370
|
orElse2(
|
|
8364
8371
|
() => pipe(
|
|
@@ -8367,7 +8374,8 @@ var effectFnIife = createDiagnostic({
|
|
|
8367
8374
|
kind: "fnUntraced",
|
|
8368
8375
|
effectModule: result.effectModule,
|
|
8369
8376
|
generatorFunction: result.generatorFunction,
|
|
8370
|
-
pipeArguments: result.pipeArguments
|
|
8377
|
+
pipeArguments: result.pipeArguments,
|
|
8378
|
+
traceExpression: void 0
|
|
8371
8379
|
}))
|
|
8372
8380
|
)
|
|
8373
8381
|
),
|
|
@@ -8378,14 +8386,15 @@ var effectFnIife = createDiagnostic({
|
|
|
8378
8386
|
kind: "fn",
|
|
8379
8387
|
effectModule: result.effectModule,
|
|
8380
8388
|
generatorFunction: void 0,
|
|
8381
|
-
pipeArguments: result.pipeArguments
|
|
8389
|
+
pipeArguments: result.pipeArguments,
|
|
8390
|
+
traceExpression: result.traceExpression
|
|
8382
8391
|
}))
|
|
8383
8392
|
)
|
|
8384
8393
|
),
|
|
8385
8394
|
option
|
|
8386
8395
|
);
|
|
8387
8396
|
if (isNone2(parsed)) continue;
|
|
8388
|
-
const { effectModule, generatorFunction, kind, pipeArguments: pipeArguments2 } = parsed.value;
|
|
8397
|
+
const { effectModule, generatorFunction, kind, pipeArguments: pipeArguments2, traceExpression } = parsed.value;
|
|
8389
8398
|
const effectModuleName = ts.isIdentifier(effectModule) ? ts.idText(effectModule) : sourceEffectModuleName;
|
|
8390
8399
|
const fixes = [];
|
|
8391
8400
|
if (generatorFunction && generatorFunction.parameters.length === 0) {
|
|
@@ -8414,9 +8423,10 @@ var effectFnIife = createDiagnostic({
|
|
|
8414
8423
|
})
|
|
8415
8424
|
});
|
|
8416
8425
|
}
|
|
8426
|
+
const traceExpressionText = traceExpression ? sourceFile.text.slice(traceExpression.pos, traceExpression.end) : void 0;
|
|
8417
8427
|
report({
|
|
8418
8428
|
location: node,
|
|
8419
|
-
messageText: `${effectModuleName}.${kind} returns a reusable function that can take arguments, but here it's called immediately. Use Effect.gen instead
|
|
8429
|
+
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` : ``}.`,
|
|
8420
8430
|
fixes
|
|
8421
8431
|
});
|
|
8422
8432
|
}
|
|
@@ -8435,6 +8445,7 @@ var effectFnOpportunity = createDiagnostic({
|
|
|
8435
8445
|
const typeCheckerUtils = yield* service(TypeCheckerUtils);
|
|
8436
8446
|
const typeParser = yield* service(TypeParser);
|
|
8437
8447
|
const tsUtils = yield* service(TypeScriptUtils);
|
|
8448
|
+
const pluginOptions = yield* service(LanguageServicePluginOptions);
|
|
8438
8449
|
const sourceEffectModuleName = tsUtils.findImportedModuleIdentifierByPackageAndNameOrBarrel(
|
|
8439
8450
|
sourceFile,
|
|
8440
8451
|
"effect",
|
|
@@ -8511,6 +8522,18 @@ var effectFnOpportunity = createDiagnostic({
|
|
|
8511
8522
|
}
|
|
8512
8523
|
return false;
|
|
8513
8524
|
};
|
|
8525
|
+
const tryExtractWithSpanExpression = (expr) => gen(function* () {
|
|
8526
|
+
if (!ts.isCallExpression(expr)) return void 0;
|
|
8527
|
+
const callee = expr.expression;
|
|
8528
|
+
const isWithSpan = yield* pipe(
|
|
8529
|
+
typeParser.isNodeReferenceToEffectModuleApi("withSpan")(callee),
|
|
8530
|
+
map8(() => true),
|
|
8531
|
+
orElse2(() => succeed(false))
|
|
8532
|
+
);
|
|
8533
|
+
if (!isWithSpan) return void 0;
|
|
8534
|
+
if (expr.arguments.length === 0) return void 0;
|
|
8535
|
+
return expr.arguments[0];
|
|
8536
|
+
});
|
|
8514
8537
|
const tryParseGenOpportunity = (fnNode) => gen(function* () {
|
|
8515
8538
|
const bodyExpression = getBodyExpression(fnNode);
|
|
8516
8539
|
if (!bodyExpression) return yield* TypeParserIssue.issue;
|
|
@@ -8521,7 +8544,15 @@ var effectFnOpportunity = createDiagnostic({
|
|
|
8521
8544
|
);
|
|
8522
8545
|
const { effectModule, generatorFunction } = yield* typeParser.effectGen(subject);
|
|
8523
8546
|
const effectModuleName = ts.isIdentifier(effectModule) ? ts.idText(effectModule) : sourceEffectModuleName;
|
|
8524
|
-
|
|
8547
|
+
let explicitTraceExpression;
|
|
8548
|
+
if (pipeArguments2.length > 0) {
|
|
8549
|
+
const lastArg = pipeArguments2[pipeArguments2.length - 1];
|
|
8550
|
+
const withSpanExpr = yield* tryExtractWithSpanExpression(lastArg);
|
|
8551
|
+
if (withSpanExpr) {
|
|
8552
|
+
explicitTraceExpression = withSpanExpr;
|
|
8553
|
+
}
|
|
8554
|
+
}
|
|
8555
|
+
return { effectModuleName, generatorFunction, pipeArguments: pipeArguments2, explicitTraceExpression };
|
|
8525
8556
|
});
|
|
8526
8557
|
const isInsideEffectFn = (fnNode) => {
|
|
8527
8558
|
const parent = fnNode.parent;
|
|
@@ -8549,6 +8580,9 @@ var effectFnOpportunity = createDiagnostic({
|
|
|
8549
8580
|
if (ts.isFunctionExpression(node) && node.name) {
|
|
8550
8581
|
return yield* TypeParserIssue.issue;
|
|
8551
8582
|
}
|
|
8583
|
+
if (node.type) {
|
|
8584
|
+
return yield* TypeParserIssue.issue;
|
|
8585
|
+
}
|
|
8552
8586
|
if (yield* isInsideEffectFn(node)) {
|
|
8553
8587
|
return yield* TypeParserIssue.issue;
|
|
8554
8588
|
}
|
|
@@ -8576,7 +8610,8 @@ var effectFnOpportunity = createDiagnostic({
|
|
|
8576
8610
|
return succeed({
|
|
8577
8611
|
effectModuleName: sourceEffectModuleName,
|
|
8578
8612
|
pipeArguments: [],
|
|
8579
|
-
generatorFunction: void 0
|
|
8613
|
+
generatorFunction: void 0,
|
|
8614
|
+
explicitTraceExpression: void 0
|
|
8580
8615
|
});
|
|
8581
8616
|
})
|
|
8582
8617
|
);
|
|
@@ -8584,7 +8619,8 @@ var effectFnOpportunity = createDiagnostic({
|
|
|
8584
8619
|
node,
|
|
8585
8620
|
nameIdentifier,
|
|
8586
8621
|
effectModuleName: opportunity.effectModuleName,
|
|
8587
|
-
traceName,
|
|
8622
|
+
inferredTraceName: traceName,
|
|
8623
|
+
explicitTraceExpression: opportunity.explicitTraceExpression,
|
|
8588
8624
|
pipeArguments: opportunity.pipeArguments,
|
|
8589
8625
|
generatorFunction: opportunity.generatorFunction,
|
|
8590
8626
|
hasParamsInPipeArgs: areParametersReferencedIn(node, opportunity.pipeArguments)
|
|
@@ -8603,7 +8639,7 @@ var effectFnOpportunity = createDiagnostic({
|
|
|
8603
8639
|
if (ts.isArrowFunction(node)) return false;
|
|
8604
8640
|
return node.asteriskToken !== void 0;
|
|
8605
8641
|
};
|
|
8606
|
-
const createEffectFnNode = (originalNode, innerFunction, effectModuleName,
|
|
8642
|
+
const createEffectFnNode = (originalNode, innerFunction, effectModuleName, traceNameOrExpression, pipeArguments2) => {
|
|
8607
8643
|
const isGenerator = isGeneratorFunction(innerFunction);
|
|
8608
8644
|
const newFunction = ts.factory.createFunctionExpression(
|
|
8609
8645
|
void 0,
|
|
@@ -8618,11 +8654,12 @@ var effectFnOpportunity = createDiagnostic({
|
|
|
8618
8654
|
ts.factory.createIdentifier(effectModuleName),
|
|
8619
8655
|
"fn"
|
|
8620
8656
|
);
|
|
8621
|
-
if (
|
|
8657
|
+
if (traceNameOrExpression) {
|
|
8658
|
+
const traceArg = typeof traceNameOrExpression === "string" ? ts.factory.createStringLiteral(traceNameOrExpression) : traceNameOrExpression;
|
|
8622
8659
|
fnExpression = ts.factory.createCallExpression(
|
|
8623
8660
|
fnExpression,
|
|
8624
8661
|
void 0,
|
|
8625
|
-
[
|
|
8662
|
+
[traceArg]
|
|
8626
8663
|
);
|
|
8627
8664
|
}
|
|
8628
8665
|
const effectFnCall = ts.factory.createCallExpression(fnExpression, void 0, [newFunction, ...pipeArguments2]);
|
|
@@ -8664,19 +8701,35 @@ var effectFnOpportunity = createDiagnostic({
|
|
|
8664
8701
|
const target = yield* pipe(parseEffectFnOpportunityTarget(node), option);
|
|
8665
8702
|
if (isNone2(target)) continue;
|
|
8666
8703
|
if (target.value.hasParamsInPipeArgs) continue;
|
|
8667
|
-
const {
|
|
8704
|
+
const {
|
|
8705
|
+
effectModuleName,
|
|
8706
|
+
explicitTraceExpression,
|
|
8707
|
+
inferredTraceName,
|
|
8708
|
+
nameIdentifier,
|
|
8709
|
+
node: targetNode,
|
|
8710
|
+
pipeArguments: pipeArguments2
|
|
8711
|
+
} = target.value;
|
|
8668
8712
|
const innerFunction = target.value.generatorFunction ?? targetNode;
|
|
8669
8713
|
const fixes = [];
|
|
8670
|
-
|
|
8671
|
-
|
|
8672
|
-
|
|
8673
|
-
|
|
8674
|
-
|
|
8675
|
-
|
|
8676
|
-
|
|
8677
|
-
|
|
8678
|
-
|
|
8679
|
-
|
|
8714
|
+
if (pluginOptions.effectFn.includes("span") && explicitTraceExpression) {
|
|
8715
|
+
fixes.push({
|
|
8716
|
+
fixName: "effectFnOpportunity_toEffectFnWithSpan",
|
|
8717
|
+
description: "Convert to Effect.fn (with span from withSpan)",
|
|
8718
|
+
apply: gen(function* () {
|
|
8719
|
+
const changeTracker = yield* service(ChangeTracker);
|
|
8720
|
+
const finalPipeArguments = pipeArguments2.slice(0, -1);
|
|
8721
|
+
const newNode = createEffectFnNode(
|
|
8722
|
+
targetNode,
|
|
8723
|
+
innerFunction,
|
|
8724
|
+
effectModuleName,
|
|
8725
|
+
explicitTraceExpression,
|
|
8726
|
+
finalPipeArguments
|
|
8727
|
+
);
|
|
8728
|
+
changeTracker.replaceNode(sourceFile, targetNode, newNode);
|
|
8729
|
+
})
|
|
8730
|
+
});
|
|
8731
|
+
}
|
|
8732
|
+
if (pluginOptions.effectFn.includes("untraced") && target.value.generatorFunction) {
|
|
8680
8733
|
fixes.push({
|
|
8681
8734
|
fixName: "effectFnOpportunity_toEffectFnUntraced",
|
|
8682
8735
|
description: "Convert to Effect.fnUntraced",
|
|
@@ -8687,10 +8740,67 @@ var effectFnOpportunity = createDiagnostic({
|
|
|
8687
8740
|
})
|
|
8688
8741
|
});
|
|
8689
8742
|
}
|
|
8690
|
-
|
|
8743
|
+
if (pluginOptions.effectFn.includes("no-span")) {
|
|
8744
|
+
fixes.push({
|
|
8745
|
+
fixName: "effectFnOpportunity_toEffectFnNoSpan",
|
|
8746
|
+
description: "Convert to Effect.fn (no span)",
|
|
8747
|
+
apply: gen(function* () {
|
|
8748
|
+
const changeTracker = yield* service(ChangeTracker);
|
|
8749
|
+
const newNode = createEffectFnNode(targetNode, innerFunction, effectModuleName, void 0, pipeArguments2);
|
|
8750
|
+
changeTracker.replaceNode(sourceFile, targetNode, newNode);
|
|
8751
|
+
})
|
|
8752
|
+
});
|
|
8753
|
+
}
|
|
8754
|
+
if (pluginOptions.effectFn.includes("inferred-span") && inferredTraceName && !explicitTraceExpression) {
|
|
8755
|
+
fixes.push({
|
|
8756
|
+
fixName: "effectFnOpportunity_toEffectFnSpanInferred",
|
|
8757
|
+
description: `Convert to Effect.fn("${inferredTraceName}")`,
|
|
8758
|
+
apply: gen(function* () {
|
|
8759
|
+
const changeTracker = yield* service(ChangeTracker);
|
|
8760
|
+
const newNode = createEffectFnNode(
|
|
8761
|
+
targetNode,
|
|
8762
|
+
innerFunction,
|
|
8763
|
+
effectModuleName,
|
|
8764
|
+
inferredTraceName,
|
|
8765
|
+
pipeArguments2
|
|
8766
|
+
);
|
|
8767
|
+
changeTracker.replaceNode(sourceFile, targetNode, newNode);
|
|
8768
|
+
})
|
|
8769
|
+
});
|
|
8770
|
+
}
|
|
8771
|
+
if (fixes.length === 0) continue;
|
|
8772
|
+
const generateExpectedSignature = () => {
|
|
8773
|
+
const firstFix = fixes[0];
|
|
8774
|
+
if (!firstFix) return "Effect.fn(function*() { ... })";
|
|
8775
|
+
const typeParamNames = targetNode.typeParameters ? `<${targetNode.typeParameters.map((tp) => ts.idText(tp.name)).join(", ")}>` : "";
|
|
8776
|
+
const paramNames = targetNode.parameters.map((param) => {
|
|
8777
|
+
if (ts.isIdentifier(param.name)) {
|
|
8778
|
+
return ts.idText(param.name);
|
|
8779
|
+
}
|
|
8780
|
+
return "_";
|
|
8781
|
+
}).join(", ");
|
|
8782
|
+
const fnSignature = `function*${typeParamNames}(${paramNames}) { ... }`;
|
|
8783
|
+
const pipeArgsForWithSpan = pipeArguments2.slice(0, -1);
|
|
8784
|
+
const pipeArgsSuffix = (args2) => args2.length > 0 ? ", ...pipeTransformations" : "";
|
|
8785
|
+
switch (firstFix.fixName) {
|
|
8786
|
+
case "effectFnOpportunity_toEffectFnWithSpan": {
|
|
8787
|
+
const traceName = explicitTraceExpression ? sourceFile.text.slice(explicitTraceExpression.pos, explicitTraceExpression.end).trim() : void 0;
|
|
8788
|
+
return `${effectModuleName}.fn(${traceName})(${fnSignature}${pipeArgsSuffix(pipeArgsForWithSpan)})`;
|
|
8789
|
+
}
|
|
8790
|
+
case "effectFnOpportunity_toEffectFnUntraced":
|
|
8791
|
+
return `${effectModuleName}.fnUntraced(${fnSignature}${pipeArgsSuffix(pipeArguments2)})`;
|
|
8792
|
+
case "effectFnOpportunity_toEffectFnNoSpan":
|
|
8793
|
+
return `${effectModuleName}.fn(${fnSignature}${pipeArgsSuffix(pipeArguments2)})`;
|
|
8794
|
+
case "effectFnOpportunity_toEffectFnSpanInferred":
|
|
8795
|
+
return `${effectModuleName}.fn("${inferredTraceName}")(${fnSignature}${pipeArgsSuffix(pipeArguments2)})`;
|
|
8796
|
+
default:
|
|
8797
|
+
return `${effectModuleName}.fn(${fnSignature})`;
|
|
8798
|
+
}
|
|
8799
|
+
};
|
|
8800
|
+
const expectedSignature = generateExpectedSignature();
|
|
8691
8801
|
report({
|
|
8692
8802
|
location: nameIdentifier ?? targetNode,
|
|
8693
|
-
messageText:
|
|
8803
|
+
messageText: `Can be rewritten as a reusable function: ${expectedSignature}`,
|
|
8694
8804
|
fixes
|
|
8695
8805
|
});
|
|
8696
8806
|
}
|