@effect/language-service 0.28.0 → 0.28.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/README.md +2 -0
- package/cli.js +149 -2
- package/cli.js.map +1 -1
- package/index.js +418 -3
- package/index.js.map +1 -1
- package/package.json +1 -1
- package/transform.js +149 -2
- package/transform.js.map +1 -1
package/package.json
CHANGED
package/transform.js
CHANGED
|
@@ -2542,6 +2542,47 @@ function make2(ts, typeChecker) {
|
|
|
2542
2542
|
"TypeParser.scopeType",
|
|
2543
2543
|
(type) => type
|
|
2544
2544
|
);
|
|
2545
|
+
const promiseLike = cachedBy(
|
|
2546
|
+
function(type, atLocation) {
|
|
2547
|
+
const thenProperty = type.getProperty("then");
|
|
2548
|
+
if (!thenProperty) return typeParserIssue("not a promise - missing then property", type, atLocation);
|
|
2549
|
+
const thenType = typeChecker.getTypeOfSymbolAtLocation(thenProperty, atLocation);
|
|
2550
|
+
if (!thenType) return typeParserIssue("not a promise - missing then property", type, atLocation);
|
|
2551
|
+
for (const callSignature of thenType.getCallSignatures()) {
|
|
2552
|
+
const parameter = callSignature.parameters[0];
|
|
2553
|
+
if (!parameter) continue;
|
|
2554
|
+
const parameterType = callSignature.getTypeParameterAtPosition(0);
|
|
2555
|
+
if (!parameterType) continue;
|
|
2556
|
+
let callbackCallSignatures = [];
|
|
2557
|
+
let toTest = [parameterType];
|
|
2558
|
+
while (toTest.length > 0) {
|
|
2559
|
+
const type2 = toTest.shift();
|
|
2560
|
+
if (!type2) continue;
|
|
2561
|
+
const callSignatures = type2.getCallSignatures();
|
|
2562
|
+
callbackCallSignatures = callbackCallSignatures.concat(callSignatures);
|
|
2563
|
+
if (type2.isUnion()) {
|
|
2564
|
+
toTest = toTest.concat(type2.types);
|
|
2565
|
+
}
|
|
2566
|
+
}
|
|
2567
|
+
for (const callableType of callbackCallSignatures) {
|
|
2568
|
+
const callbackParameter = callableType.parameters[0];
|
|
2569
|
+
if (!callbackParameter) {
|
|
2570
|
+
continue;
|
|
2571
|
+
}
|
|
2572
|
+
const callbackParameterType = callableType.getTypeParameterAtPosition(0);
|
|
2573
|
+
if (!callbackParameterType) {
|
|
2574
|
+
continue;
|
|
2575
|
+
}
|
|
2576
|
+
return succeed({
|
|
2577
|
+
type: callbackParameterType
|
|
2578
|
+
});
|
|
2579
|
+
}
|
|
2580
|
+
}
|
|
2581
|
+
return typeParserIssue("not a promise", type, atLocation);
|
|
2582
|
+
},
|
|
2583
|
+
"TypeParser.promiseLike",
|
|
2584
|
+
(type) => type
|
|
2585
|
+
);
|
|
2545
2586
|
return {
|
|
2546
2587
|
effectType,
|
|
2547
2588
|
strictEffectType,
|
|
@@ -2557,7 +2598,8 @@ function make2(ts, typeChecker) {
|
|
|
2557
2598
|
contextTag,
|
|
2558
2599
|
pipeableType,
|
|
2559
2600
|
pipeCall,
|
|
2560
|
-
scopeType
|
|
2601
|
+
scopeType,
|
|
2602
|
+
promiseLike
|
|
2561
2603
|
};
|
|
2562
2604
|
}
|
|
2563
2605
|
|
|
@@ -3320,6 +3362,110 @@ var missingStarInYieldEffectGen = createDiagnostic({
|
|
|
3320
3362
|
})
|
|
3321
3363
|
});
|
|
3322
3364
|
|
|
3365
|
+
// src/diagnostics/multipleEffectProvide.ts
|
|
3366
|
+
var multipleEffectProvide = createDiagnostic({
|
|
3367
|
+
name: "multipleEffectProvide",
|
|
3368
|
+
code: 18,
|
|
3369
|
+
severity: "warning",
|
|
3370
|
+
apply: fn("multipleEffectProvide.apply")(function* (sourceFile, report) {
|
|
3371
|
+
const ts = yield* service(TypeScriptApi);
|
|
3372
|
+
const typeChecker = yield* service(TypeCheckerApi);
|
|
3373
|
+
const typeParser = yield* service(TypeParser);
|
|
3374
|
+
const effectModuleIdentifier = yield* pipe(
|
|
3375
|
+
findImportedModuleIdentifierByPackageAndNameOrBarrel(
|
|
3376
|
+
sourceFile,
|
|
3377
|
+
"effect",
|
|
3378
|
+
"Effect"
|
|
3379
|
+
),
|
|
3380
|
+
map3((_) => _.text),
|
|
3381
|
+
orElse2(() => succeed("Effect"))
|
|
3382
|
+
);
|
|
3383
|
+
const layerModuleIdentifier = yield* pipe(
|
|
3384
|
+
findImportedModuleIdentifierByPackageAndNameOrBarrel(
|
|
3385
|
+
sourceFile,
|
|
3386
|
+
"effect",
|
|
3387
|
+
"Layer"
|
|
3388
|
+
),
|
|
3389
|
+
map3((_) => _.text),
|
|
3390
|
+
orElse2(() => succeed("Layer"))
|
|
3391
|
+
);
|
|
3392
|
+
const parseEffectProvideLayer = (node) => {
|
|
3393
|
+
if (ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression) && ts.isIdentifier(node.expression.name) && node.expression.name.text === "provide" && node.arguments.length > 0) {
|
|
3394
|
+
const layer = node.arguments[0];
|
|
3395
|
+
const type = typeChecker.getTypeAtLocation(layer);
|
|
3396
|
+
return pipe(
|
|
3397
|
+
typeParser.importedEffectModule(node.expression.expression),
|
|
3398
|
+
flatMap2(() => typeParser.layerType(type, layer)),
|
|
3399
|
+
map3(() => ({ layer, node })),
|
|
3400
|
+
orElse2(() => void_)
|
|
3401
|
+
);
|
|
3402
|
+
}
|
|
3403
|
+
return void_;
|
|
3404
|
+
};
|
|
3405
|
+
const parsePipeCall = (node) => gen(function* () {
|
|
3406
|
+
const { args: args2 } = yield* typeParser.pipeCall(node);
|
|
3407
|
+
let currentChunk = 0;
|
|
3408
|
+
const previousLayers = [[]];
|
|
3409
|
+
for (const pipeArg of args2) {
|
|
3410
|
+
const parsedProvide = yield* parseEffectProvideLayer(pipeArg);
|
|
3411
|
+
if (parsedProvide) {
|
|
3412
|
+
previousLayers[currentChunk].push(parsedProvide);
|
|
3413
|
+
} else {
|
|
3414
|
+
currentChunk++;
|
|
3415
|
+
previousLayers.push([]);
|
|
3416
|
+
}
|
|
3417
|
+
}
|
|
3418
|
+
for (const chunk of previousLayers) {
|
|
3419
|
+
if (chunk.length < 2) continue;
|
|
3420
|
+
report({
|
|
3421
|
+
node: chunk[0].node,
|
|
3422
|
+
messageText: "Calling multiple subsequent times Effect.provide is an anti-pattern and can lead to service lifecycle issues. You should combine the layers and provide them once instead.",
|
|
3423
|
+
fixes: [{
|
|
3424
|
+
fixName: "multipleEffectProvide_fix",
|
|
3425
|
+
description: "Combine into a single provide",
|
|
3426
|
+
apply: gen(function* () {
|
|
3427
|
+
const changeTracker = yield* service(ChangeTracker);
|
|
3428
|
+
changeTracker.deleteRange(sourceFile, {
|
|
3429
|
+
pos: chunk[0].node.getStart(sourceFile),
|
|
3430
|
+
end: chunk[chunk.length - 1].node.getEnd()
|
|
3431
|
+
});
|
|
3432
|
+
const newNode = ts.factory.createCallExpression(
|
|
3433
|
+
ts.factory.createPropertyAccessExpression(
|
|
3434
|
+
ts.factory.createIdentifier(effectModuleIdentifier),
|
|
3435
|
+
ts.factory.createIdentifier("provide")
|
|
3436
|
+
),
|
|
3437
|
+
void 0,
|
|
3438
|
+
[ts.factory.createCallExpression(
|
|
3439
|
+
ts.factory.createPropertyAccessExpression(
|
|
3440
|
+
ts.factory.createIdentifier(layerModuleIdentifier),
|
|
3441
|
+
ts.factory.createIdentifier("mergeAll")
|
|
3442
|
+
),
|
|
3443
|
+
void 0,
|
|
3444
|
+
chunk.map((c) => c.layer)
|
|
3445
|
+
)]
|
|
3446
|
+
);
|
|
3447
|
+
changeTracker.insertNodeAt(sourceFile, chunk[0].node.getStart(sourceFile), newNode);
|
|
3448
|
+
})
|
|
3449
|
+
}]
|
|
3450
|
+
});
|
|
3451
|
+
}
|
|
3452
|
+
});
|
|
3453
|
+
const nodeToVisit = [];
|
|
3454
|
+
const appendNodeToVisit = (node) => {
|
|
3455
|
+
nodeToVisit.push(node);
|
|
3456
|
+
return void 0;
|
|
3457
|
+
};
|
|
3458
|
+
ts.forEachChild(sourceFile, appendNodeToVisit);
|
|
3459
|
+
while (nodeToVisit.length > 0) {
|
|
3460
|
+
const node = nodeToVisit.shift();
|
|
3461
|
+
ts.forEachChild(node, appendNodeToVisit);
|
|
3462
|
+
if (ts.isCallExpression(node)) {
|
|
3463
|
+
yield* pipe(parsePipeCall(node), ignore);
|
|
3464
|
+
}
|
|
3465
|
+
}
|
|
3466
|
+
})
|
|
3467
|
+
});
|
|
3468
|
+
|
|
3323
3469
|
// src/diagnostics/returnEffectInGen.ts
|
|
3324
3470
|
var returnEffectInGen = createDiagnostic({
|
|
3325
3471
|
name: "returnEffectInGen",
|
|
@@ -3781,7 +3927,8 @@ var diagnostics = [
|
|
|
3781
3927
|
scopeInLayerEffect,
|
|
3782
3928
|
effectInVoidSuccess,
|
|
3783
3929
|
unnecessaryPipeChain,
|
|
3784
|
-
strictBooleanExpressions
|
|
3930
|
+
strictBooleanExpressions,
|
|
3931
|
+
multipleEffectProvide
|
|
3785
3932
|
];
|
|
3786
3933
|
|
|
3787
3934
|
// src/transform.ts
|