@effect/language-service 0.49.0 → 0.51.0
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 +4 -0
- package/cli.js +119 -3
- package/cli.js.map +1 -1
- package/effect-lsp-patch-utils.js +120 -3
- package/effect-lsp-patch-utils.js.map +1 -1
- package/index.js +119 -3
- package/index.js.map +1 -1
- package/package.json +1 -1
- package/transform.js +120 -3
- package/transform.js.map +1 -1
package/README.md
CHANGED
|
@@ -5,6 +5,8 @@ This package implements a TypeScript language service plugin that allows additio
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
7
7
|
1. `npm install @effect/language-service --save-dev` in your project
|
|
8
|
+
- For monorepos: We suggest installing `@effect/language-service` in the monorepo root and configuring it in the root `tsconfig.json` for consistent behavior across all packages
|
|
9
|
+
- For any other package: Install directly in the package where you want to use it
|
|
8
10
|
2. Inside your tsconfig.json, you should add the plugin configuration as follows:
|
|
9
11
|
```jsonc
|
|
10
12
|
{
|
|
@@ -53,6 +55,7 @@ And you're done! You'll now be able to use a set of refactors and diagnostics th
|
|
|
53
55
|
- Warn on leaking requirements in Effect services
|
|
54
56
|
- Warn on Scope as requirement of a Layer
|
|
55
57
|
- Warn on subsequent `Effect.provide` anti-pattern
|
|
58
|
+
- Warn when using `Effect.provide` with Layer outside of application entry points
|
|
56
59
|
- Detect wrong `Self` type parameter for APIs like `Effect.Service` or `Schema.TaggedError` and similar
|
|
57
60
|
- Unnecessary usages of `Effect.gen` or `pipe()`
|
|
58
61
|
- Warn when using `Effect.gen` with the old generator adapter pattern
|
|
@@ -64,6 +67,7 @@ And you're done! You'll now be able to use a set of refactors and diagnostics th
|
|
|
64
67
|
- Warn when `Effect.Service` is used with a primitive type instead of an object type
|
|
65
68
|
- Warn when schema classes override the default constructor behavior
|
|
66
69
|
- Warn when `@effect-diagnostics-next-line` comments have no effect (i.e., they don't suppress any diagnostic)
|
|
70
|
+
- Detect nested function calls that can be converted to pipeable style for better readability
|
|
67
71
|
|
|
68
72
|
### Completions
|
|
69
73
|
|
package/cli.js
CHANGED
|
@@ -31198,7 +31198,8 @@ var defaults = {
|
|
|
31198
31198
|
pattern: "default",
|
|
31199
31199
|
skipLeadingPath: ["src/"]
|
|
31200
31200
|
}],
|
|
31201
|
-
extendedKeyDetection: false
|
|
31201
|
+
extendedKeyDetection: false,
|
|
31202
|
+
pipeableMinArgCount: 1
|
|
31202
31203
|
};
|
|
31203
31204
|
function parseKeyPatterns(patterns) {
|
|
31204
31205
|
const result = [];
|
|
@@ -31233,7 +31234,8 @@ function parse4(config2) {
|
|
|
31233
31234
|
renames: isObject(config2) && hasProperty(config2, "renames") && isBoolean(config2.renames) ? config2.renames : defaults.renames,
|
|
31234
31235
|
noExternal: isObject(config2) && hasProperty(config2, "noExternal") && isBoolean(config2.noExternal) ? config2.noExternal : defaults.noExternal,
|
|
31235
31236
|
keyPatterns: isObject(config2) && hasProperty(config2, "keyPatterns") && isArray(config2.keyPatterns) ? parseKeyPatterns(config2.keyPatterns) : defaults.keyPatterns,
|
|
31236
|
-
extendedKeyDetection: isObject(config2) && hasProperty(config2, "extendedKeyDetection") && isBoolean(config2.extendedKeyDetection) ? config2.extendedKeyDetection : defaults.extendedKeyDetection
|
|
31237
|
+
extendedKeyDetection: isObject(config2) && hasProperty(config2, "extendedKeyDetection") && isBoolean(config2.extendedKeyDetection) ? config2.extendedKeyDetection : defaults.extendedKeyDetection,
|
|
31238
|
+
pipeableMinArgCount: isObject(config2) && hasProperty(config2, "pipeableMinArgCount") && isNumber(config2.pipeableMinArgCount) ? config2.pipeableMinArgCount : defaults.pipeableMinArgCount
|
|
31237
31239
|
};
|
|
31238
31240
|
}
|
|
31239
31241
|
|
|
@@ -33724,6 +33726,74 @@ More info at https://effect.website/docs/requirements-management/layers/#avoidin
|
|
|
33724
33726
|
})
|
|
33725
33727
|
});
|
|
33726
33728
|
|
|
33729
|
+
// src/diagnostics/missedPipeableOpportunity.ts
|
|
33730
|
+
var missedPipeableOpportunity = createDiagnostic({
|
|
33731
|
+
name: "missedPipeableOpportunity",
|
|
33732
|
+
code: 26,
|
|
33733
|
+
severity: "off",
|
|
33734
|
+
apply: fn2("missedPipeableOpportunity.apply")(function* (sourceFile, report) {
|
|
33735
|
+
const ts = yield* service2(TypeScriptApi);
|
|
33736
|
+
const typeChecker = yield* service2(TypeCheckerApi);
|
|
33737
|
+
const typeParser = yield* service2(TypeParser);
|
|
33738
|
+
const options3 = yield* service2(LanguageServicePluginOptions);
|
|
33739
|
+
const nodeToVisit = [sourceFile];
|
|
33740
|
+
const prependNodeToVisit = (node) => {
|
|
33741
|
+
nodeToVisit.unshift(node);
|
|
33742
|
+
return void 0;
|
|
33743
|
+
};
|
|
33744
|
+
const callChainNodes = /* @__PURE__ */ new WeakMap();
|
|
33745
|
+
while (nodeToVisit.length > 0) {
|
|
33746
|
+
const node = nodeToVisit.shift();
|
|
33747
|
+
if (ts.isCallExpression(node) && node.arguments.length === 1 && node.parent) {
|
|
33748
|
+
const parentChain = callChainNodes.get(node.parent) || [];
|
|
33749
|
+
callChainNodes.set(node, parentChain.concat(node));
|
|
33750
|
+
} else if (node.parent && callChainNodes.has(node.parent) && ts.isExpression(node)) {
|
|
33751
|
+
const parentChain = callChainNodes.get(node.parent) || [];
|
|
33752
|
+
const originalParentChain = parentChain.slice();
|
|
33753
|
+
parentChain.push(node);
|
|
33754
|
+
while (parentChain.length > options3.pipeableMinArgCount) {
|
|
33755
|
+
const subject = parentChain.pop();
|
|
33756
|
+
const resultType = typeChecker.getTypeAtLocation(subject);
|
|
33757
|
+
const pipeableType = yield* pipe(typeParser.pipeableType(resultType, subject), orElse14(() => void_8));
|
|
33758
|
+
if (pipeableType) {
|
|
33759
|
+
report({
|
|
33760
|
+
location: parentChain[0],
|
|
33761
|
+
messageText: `Nested function calls can be converted to pipeable style for better readability.`,
|
|
33762
|
+
fixes: [{
|
|
33763
|
+
fixName: "missedPipeableOpportunity_fix",
|
|
33764
|
+
description: "Convert to pipe style",
|
|
33765
|
+
apply: gen3(function* () {
|
|
33766
|
+
const changeTracker = yield* service2(ChangeTracker);
|
|
33767
|
+
changeTracker.replaceNode(
|
|
33768
|
+
sourceFile,
|
|
33769
|
+
parentChain[0],
|
|
33770
|
+
ts.factory.createCallExpression(
|
|
33771
|
+
ts.factory.createPropertyAccessExpression(
|
|
33772
|
+
subject,
|
|
33773
|
+
"pipe"
|
|
33774
|
+
),
|
|
33775
|
+
void 0,
|
|
33776
|
+
pipe(
|
|
33777
|
+
parentChain,
|
|
33778
|
+
filter2(ts.isCallExpression),
|
|
33779
|
+
map4((call) => call.expression),
|
|
33780
|
+
reverse
|
|
33781
|
+
)
|
|
33782
|
+
)
|
|
33783
|
+
);
|
|
33784
|
+
})
|
|
33785
|
+
}]
|
|
33786
|
+
});
|
|
33787
|
+
originalParentChain.forEach((node2) => callChainNodes.delete(node2));
|
|
33788
|
+
break;
|
|
33789
|
+
}
|
|
33790
|
+
}
|
|
33791
|
+
}
|
|
33792
|
+
ts.forEachChild(node, prependNodeToVisit);
|
|
33793
|
+
}
|
|
33794
|
+
})
|
|
33795
|
+
});
|
|
33796
|
+
|
|
33727
33797
|
// src/diagnostics/missingEffectContext.ts
|
|
33728
33798
|
var missingEffectContext = createDiagnostic({
|
|
33729
33799
|
name: "missingEffectContext",
|
|
@@ -35003,6 +35073,50 @@ var strictBooleanExpressions = createDiagnostic({
|
|
|
35003
35073
|
})
|
|
35004
35074
|
});
|
|
35005
35075
|
|
|
35076
|
+
// src/diagnostics/strictEffectProvide.ts
|
|
35077
|
+
var strictEffectProvide = createDiagnostic({
|
|
35078
|
+
name: "strictEffectProvide",
|
|
35079
|
+
code: 27,
|
|
35080
|
+
severity: "off",
|
|
35081
|
+
apply: fn2("strictEffectProvide.apply")(function* (sourceFile, report) {
|
|
35082
|
+
const ts = yield* service2(TypeScriptApi);
|
|
35083
|
+
const typeChecker = yield* service2(TypeCheckerApi);
|
|
35084
|
+
const typeParser = yield* service2(TypeParser);
|
|
35085
|
+
const parseEffectProvideWithLayer = (node) => gen3(function* () {
|
|
35086
|
+
if (!ts.isCallExpression(node) || !ts.isPropertyAccessExpression(node.expression) || !ts.isIdentifier(node.expression.name) || ts.idText(node.expression.name) !== "provide" || node.arguments.length === 0) {
|
|
35087
|
+
return yield* typeParserIssue("Not an Effect.provide call");
|
|
35088
|
+
}
|
|
35089
|
+
yield* typeParser.importedEffectModule(node.expression.expression);
|
|
35090
|
+
return yield* firstSuccessOf2(
|
|
35091
|
+
node.arguments.map((arg) => {
|
|
35092
|
+
const argType = typeChecker.getTypeAtLocation(arg);
|
|
35093
|
+
return typeParser.layerType(argType, arg);
|
|
35094
|
+
})
|
|
35095
|
+
);
|
|
35096
|
+
});
|
|
35097
|
+
const nodeToVisit = [];
|
|
35098
|
+
const appendNodeToVisit = (node) => {
|
|
35099
|
+
nodeToVisit.push(node);
|
|
35100
|
+
return void 0;
|
|
35101
|
+
};
|
|
35102
|
+
ts.forEachChild(sourceFile, appendNodeToVisit);
|
|
35103
|
+
while (nodeToVisit.length > 0) {
|
|
35104
|
+
const node = nodeToVisit.shift();
|
|
35105
|
+
ts.forEachChild(node, appendNodeToVisit);
|
|
35106
|
+
if (ts.isCallExpression(node)) {
|
|
35107
|
+
const layerCheck = yield* pipe(parseEffectProvideWithLayer(node), option4);
|
|
35108
|
+
if (isSome2(layerCheck)) {
|
|
35109
|
+
report({
|
|
35110
|
+
location: node,
|
|
35111
|
+
messageText: "Effect.provide with a Layer should only be used at application entry points. If this is an entry point, you can safely disable this diagnostic. Otherwise, using Effect.provide may break scope lifetimes. Compose all layers at your entry point and provide them at once.",
|
|
35112
|
+
fixes: []
|
|
35113
|
+
});
|
|
35114
|
+
}
|
|
35115
|
+
}
|
|
35116
|
+
}
|
|
35117
|
+
})
|
|
35118
|
+
});
|
|
35119
|
+
|
|
35006
35120
|
// src/diagnostics/tryCatchInEffectGen.ts
|
|
35007
35121
|
var tryCatchInEffectGen = createDiagnostic({
|
|
35008
35122
|
name: "tryCatchInEffectGen",
|
|
@@ -35292,7 +35406,9 @@ var diagnostics = [
|
|
|
35292
35406
|
overriddenSchemaConstructor,
|
|
35293
35407
|
unsupportedServiceAccessors,
|
|
35294
35408
|
nonObjectEffectServiceType,
|
|
35295
|
-
deterministicKeys
|
|
35409
|
+
deterministicKeys,
|
|
35410
|
+
missedPipeableOpportunity,
|
|
35411
|
+
strictEffectProvide
|
|
35296
35412
|
];
|
|
35297
35413
|
|
|
35298
35414
|
// src/cli/diagnostics.ts
|