@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/index.js CHANGED
@@ -1242,7 +1242,8 @@ var defaults = {
1242
1242
  pattern: "default",
1243
1243
  skipLeadingPath: ["src/"]
1244
1244
  }],
1245
- extendedKeyDetection: false
1245
+ extendedKeyDetection: false,
1246
+ pipeableMinArgCount: 1
1246
1247
  };
1247
1248
  function parseKeyPatterns(patterns) {
1248
1249
  const result = [];
@@ -1277,7 +1278,8 @@ function parse(config) {
1277
1278
  renames: isObject(config) && hasProperty(config, "renames") && isBoolean(config.renames) ? config.renames : defaults.renames,
1278
1279
  noExternal: isObject(config) && hasProperty(config, "noExternal") && isBoolean(config.noExternal) ? config.noExternal : defaults.noExternal,
1279
1280
  keyPatterns: isObject(config) && hasProperty(config, "keyPatterns") && isArray(config.keyPatterns) ? parseKeyPatterns(config.keyPatterns) : defaults.keyPatterns,
1280
- extendedKeyDetection: isObject(config) && hasProperty(config, "extendedKeyDetection") && isBoolean(config.extendedKeyDetection) ? config.extendedKeyDetection : defaults.extendedKeyDetection
1281
+ extendedKeyDetection: isObject(config) && hasProperty(config, "extendedKeyDetection") && isBoolean(config.extendedKeyDetection) ? config.extendedKeyDetection : defaults.extendedKeyDetection,
1282
+ pipeableMinArgCount: isObject(config) && hasProperty(config, "pipeableMinArgCount") && isNumber(config.pipeableMinArgCount) ? config.pipeableMinArgCount : defaults.pipeableMinArgCount
1281
1283
  };
1282
1284
  }
1283
1285
 
@@ -7027,6 +7029,74 @@ More info at https://effect.website/docs/requirements-management/layers/#avoidin
7027
7029
  })
7028
7030
  });
7029
7031
 
7032
+ // src/diagnostics/missedPipeableOpportunity.ts
7033
+ var missedPipeableOpportunity = createDiagnostic({
7034
+ name: "missedPipeableOpportunity",
7035
+ code: 26,
7036
+ severity: "off",
7037
+ apply: fn("missedPipeableOpportunity.apply")(function* (sourceFile, report) {
7038
+ const ts = yield* service(TypeScriptApi);
7039
+ const typeChecker = yield* service(TypeCheckerApi);
7040
+ const typeParser = yield* service(TypeParser);
7041
+ const options = yield* service(LanguageServicePluginOptions);
7042
+ const nodeToVisit = [sourceFile];
7043
+ const prependNodeToVisit = (node) => {
7044
+ nodeToVisit.unshift(node);
7045
+ return void 0;
7046
+ };
7047
+ const callChainNodes = /* @__PURE__ */ new WeakMap();
7048
+ while (nodeToVisit.length > 0) {
7049
+ const node = nodeToVisit.shift();
7050
+ if (ts.isCallExpression(node) && node.arguments.length === 1 && node.parent) {
7051
+ const parentChain = callChainNodes.get(node.parent) || [];
7052
+ callChainNodes.set(node, parentChain.concat(node));
7053
+ } else if (node.parent && callChainNodes.has(node.parent) && ts.isExpression(node)) {
7054
+ const parentChain = callChainNodes.get(node.parent) || [];
7055
+ const originalParentChain = parentChain.slice();
7056
+ parentChain.push(node);
7057
+ while (parentChain.length > options.pipeableMinArgCount) {
7058
+ const subject = parentChain.pop();
7059
+ const resultType = typeChecker.getTypeAtLocation(subject);
7060
+ const pipeableType = yield* pipe(typeParser.pipeableType(resultType, subject), orElse2(() => void_));
7061
+ if (pipeableType) {
7062
+ report({
7063
+ location: parentChain[0],
7064
+ messageText: `Nested function calls can be converted to pipeable style for better readability.`,
7065
+ fixes: [{
7066
+ fixName: "missedPipeableOpportunity_fix",
7067
+ description: "Convert to pipe style",
7068
+ apply: gen(function* () {
7069
+ const changeTracker = yield* service(ChangeTracker);
7070
+ changeTracker.replaceNode(
7071
+ sourceFile,
7072
+ parentChain[0],
7073
+ ts.factory.createCallExpression(
7074
+ ts.factory.createPropertyAccessExpression(
7075
+ subject,
7076
+ "pipe"
7077
+ ),
7078
+ void 0,
7079
+ pipe(
7080
+ parentChain,
7081
+ filter(ts.isCallExpression),
7082
+ map4((call) => call.expression),
7083
+ reverse
7084
+ )
7085
+ )
7086
+ );
7087
+ })
7088
+ }]
7089
+ });
7090
+ originalParentChain.forEach((node2) => callChainNodes.delete(node2));
7091
+ break;
7092
+ }
7093
+ }
7094
+ }
7095
+ ts.forEachChild(node, prependNodeToVisit);
7096
+ }
7097
+ })
7098
+ });
7099
+
7030
7100
  // src/diagnostics/missingEffectContext.ts
7031
7101
  var missingEffectContext = createDiagnostic({
7032
7102
  name: "missingEffectContext",
@@ -8012,6 +8082,50 @@ var strictBooleanExpressions = createDiagnostic({
8012
8082
  })
8013
8083
  });
8014
8084
 
8085
+ // src/diagnostics/strictEffectProvide.ts
8086
+ var strictEffectProvide = createDiagnostic({
8087
+ name: "strictEffectProvide",
8088
+ code: 27,
8089
+ severity: "off",
8090
+ apply: fn("strictEffectProvide.apply")(function* (sourceFile, report) {
8091
+ const ts = yield* service(TypeScriptApi);
8092
+ const typeChecker = yield* service(TypeCheckerApi);
8093
+ const typeParser = yield* service(TypeParser);
8094
+ const parseEffectProvideWithLayer = (node) => gen(function* () {
8095
+ if (!ts.isCallExpression(node) || !ts.isPropertyAccessExpression(node.expression) || !ts.isIdentifier(node.expression.name) || ts.idText(node.expression.name) !== "provide" || node.arguments.length === 0) {
8096
+ return yield* typeParserIssue("Not an Effect.provide call");
8097
+ }
8098
+ yield* typeParser.importedEffectModule(node.expression.expression);
8099
+ return yield* firstSuccessOf(
8100
+ node.arguments.map((arg) => {
8101
+ const argType = typeChecker.getTypeAtLocation(arg);
8102
+ return typeParser.layerType(argType, arg);
8103
+ })
8104
+ );
8105
+ });
8106
+ const nodeToVisit = [];
8107
+ const appendNodeToVisit = (node) => {
8108
+ nodeToVisit.push(node);
8109
+ return void 0;
8110
+ };
8111
+ ts.forEachChild(sourceFile, appendNodeToVisit);
8112
+ while (nodeToVisit.length > 0) {
8113
+ const node = nodeToVisit.shift();
8114
+ ts.forEachChild(node, appendNodeToVisit);
8115
+ if (ts.isCallExpression(node)) {
8116
+ const layerCheck = yield* pipe(parseEffectProvideWithLayer(node), option);
8117
+ if (isSome2(layerCheck)) {
8118
+ report({
8119
+ location: node,
8120
+ 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.",
8121
+ fixes: []
8122
+ });
8123
+ }
8124
+ }
8125
+ }
8126
+ })
8127
+ });
8128
+
8015
8129
  // src/diagnostics/tryCatchInEffectGen.ts
8016
8130
  var tryCatchInEffectGen = createDiagnostic({
8017
8131
  name: "tryCatchInEffectGen",
@@ -8301,7 +8415,9 @@ var diagnostics = [
8301
8415
  overriddenSchemaConstructor,
8302
8416
  unsupportedServiceAccessors,
8303
8417
  nonObjectEffectServiceType,
8304
- deterministicKeys
8418
+ deterministicKeys,
8419
+ missedPipeableOpportunity,
8420
+ strictEffectProvide
8305
8421
  ];
8306
8422
 
8307
8423
  // src/completions/effectDiagnosticsComment.ts