@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.
@@ -771,6 +771,7 @@ var unsafeGet = /* @__PURE__ */ dual(2, (self, index) => {
771
771
  });
772
772
  var headNonEmpty = /* @__PURE__ */ unsafeGet(0);
773
773
  var tailNonEmpty = (self) => self.slice(1);
774
+ var reverse = (self) => Array.from(self).reverse();
774
775
  var sort = /* @__PURE__ */ dual(2, (self, O) => {
775
776
  const out = Array.from(self);
776
777
  out.sort(O);
@@ -1197,7 +1198,8 @@ var defaults = {
1197
1198
  pattern: "default",
1198
1199
  skipLeadingPath: ["src/"]
1199
1200
  }],
1200
- extendedKeyDetection: false
1201
+ extendedKeyDetection: false,
1202
+ pipeableMinArgCount: 1
1201
1203
  };
1202
1204
  function parseKeyPatterns(patterns) {
1203
1205
  const result = [];
@@ -1232,7 +1234,8 @@ function parse(config) {
1232
1234
  renames: isObject(config) && hasProperty(config, "renames") && isBoolean(config.renames) ? config.renames : defaults.renames,
1233
1235
  noExternal: isObject(config) && hasProperty(config, "noExternal") && isBoolean(config.noExternal) ? config.noExternal : defaults.noExternal,
1234
1236
  keyPatterns: isObject(config) && hasProperty(config, "keyPatterns") && isArray(config.keyPatterns) ? parseKeyPatterns(config.keyPatterns) : defaults.keyPatterns,
1235
- extendedKeyDetection: isObject(config) && hasProperty(config, "extendedKeyDetection") && isBoolean(config.extendedKeyDetection) ? config.extendedKeyDetection : defaults.extendedKeyDetection
1237
+ extendedKeyDetection: isObject(config) && hasProperty(config, "extendedKeyDetection") && isBoolean(config.extendedKeyDetection) ? config.extendedKeyDetection : defaults.extendedKeyDetection,
1238
+ pipeableMinArgCount: isObject(config) && hasProperty(config, "pipeableMinArgCount") && isNumber(config.pipeableMinArgCount) ? config.pipeableMinArgCount : defaults.pipeableMinArgCount
1236
1239
  };
1237
1240
  }
1238
1241
 
@@ -4271,6 +4274,74 @@ More info at https://effect.website/docs/requirements-management/layers/#avoidin
4271
4274
  })
4272
4275
  });
4273
4276
 
4277
+ // src/diagnostics/missedPipeableOpportunity.ts
4278
+ var missedPipeableOpportunity = createDiagnostic({
4279
+ name: "missedPipeableOpportunity",
4280
+ code: 26,
4281
+ severity: "off",
4282
+ apply: fn("missedPipeableOpportunity.apply")(function* (sourceFile, report) {
4283
+ const ts = yield* service(TypeScriptApi);
4284
+ const typeChecker = yield* service(TypeCheckerApi);
4285
+ const typeParser = yield* service(TypeParser);
4286
+ const options = yield* service(LanguageServicePluginOptions);
4287
+ const nodeToVisit = [sourceFile];
4288
+ const prependNodeToVisit = (node) => {
4289
+ nodeToVisit.unshift(node);
4290
+ return void 0;
4291
+ };
4292
+ const callChainNodes = /* @__PURE__ */ new WeakMap();
4293
+ while (nodeToVisit.length > 0) {
4294
+ const node = nodeToVisit.shift();
4295
+ if (ts.isCallExpression(node) && node.arguments.length === 1 && node.parent) {
4296
+ const parentChain = callChainNodes.get(node.parent) || [];
4297
+ callChainNodes.set(node, parentChain.concat(node));
4298
+ } else if (node.parent && callChainNodes.has(node.parent) && ts.isExpression(node)) {
4299
+ const parentChain = callChainNodes.get(node.parent) || [];
4300
+ const originalParentChain = parentChain.slice();
4301
+ parentChain.push(node);
4302
+ while (parentChain.length > options.pipeableMinArgCount) {
4303
+ const subject = parentChain.pop();
4304
+ const resultType = typeChecker.getTypeAtLocation(subject);
4305
+ const pipeableType = yield* pipe(typeParser.pipeableType(resultType, subject), orElse2(() => void_));
4306
+ if (pipeableType) {
4307
+ report({
4308
+ location: parentChain[0],
4309
+ messageText: `Nested function calls can be converted to pipeable style for better readability.`,
4310
+ fixes: [{
4311
+ fixName: "missedPipeableOpportunity_fix",
4312
+ description: "Convert to pipe style",
4313
+ apply: gen(function* () {
4314
+ const changeTracker = yield* service(ChangeTracker);
4315
+ changeTracker.replaceNode(
4316
+ sourceFile,
4317
+ parentChain[0],
4318
+ ts.factory.createCallExpression(
4319
+ ts.factory.createPropertyAccessExpression(
4320
+ subject,
4321
+ "pipe"
4322
+ ),
4323
+ void 0,
4324
+ pipe(
4325
+ parentChain,
4326
+ filter(ts.isCallExpression),
4327
+ map3((call) => call.expression),
4328
+ reverse
4329
+ )
4330
+ )
4331
+ );
4332
+ })
4333
+ }]
4334
+ });
4335
+ originalParentChain.forEach((node2) => callChainNodes.delete(node2));
4336
+ break;
4337
+ }
4338
+ }
4339
+ }
4340
+ ts.forEachChild(node, prependNodeToVisit);
4341
+ }
4342
+ })
4343
+ });
4344
+
4274
4345
  // src/diagnostics/missingEffectContext.ts
4275
4346
  var missingEffectContext = createDiagnostic({
4276
4347
  name: "missingEffectContext",
@@ -5550,6 +5621,50 @@ var strictBooleanExpressions = createDiagnostic({
5550
5621
  })
5551
5622
  });
5552
5623
 
5624
+ // src/diagnostics/strictEffectProvide.ts
5625
+ var strictEffectProvide = createDiagnostic({
5626
+ name: "strictEffectProvide",
5627
+ code: 27,
5628
+ severity: "off",
5629
+ apply: fn("strictEffectProvide.apply")(function* (sourceFile, report) {
5630
+ const ts = yield* service(TypeScriptApi);
5631
+ const typeChecker = yield* service(TypeCheckerApi);
5632
+ const typeParser = yield* service(TypeParser);
5633
+ const parseEffectProvideWithLayer = (node) => gen(function* () {
5634
+ if (!ts.isCallExpression(node) || !ts.isPropertyAccessExpression(node.expression) || !ts.isIdentifier(node.expression.name) || ts.idText(node.expression.name) !== "provide" || node.arguments.length === 0) {
5635
+ return yield* typeParserIssue("Not an Effect.provide call");
5636
+ }
5637
+ yield* typeParser.importedEffectModule(node.expression.expression);
5638
+ return yield* firstSuccessOf(
5639
+ node.arguments.map((arg) => {
5640
+ const argType = typeChecker.getTypeAtLocation(arg);
5641
+ return typeParser.layerType(argType, arg);
5642
+ })
5643
+ );
5644
+ });
5645
+ const nodeToVisit = [];
5646
+ const appendNodeToVisit = (node) => {
5647
+ nodeToVisit.push(node);
5648
+ return void 0;
5649
+ };
5650
+ ts.forEachChild(sourceFile, appendNodeToVisit);
5651
+ while (nodeToVisit.length > 0) {
5652
+ const node = nodeToVisit.shift();
5653
+ ts.forEachChild(node, appendNodeToVisit);
5654
+ if (ts.isCallExpression(node)) {
5655
+ const layerCheck = yield* pipe(parseEffectProvideWithLayer(node), option);
5656
+ if (isSome2(layerCheck)) {
5657
+ report({
5658
+ location: node,
5659
+ 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.",
5660
+ fixes: []
5661
+ });
5662
+ }
5663
+ }
5664
+ }
5665
+ })
5666
+ });
5667
+
5553
5668
  // src/diagnostics/tryCatchInEffectGen.ts
5554
5669
  var tryCatchInEffectGen = createDiagnostic({
5555
5670
  name: "tryCatchInEffectGen",
@@ -5839,7 +5954,9 @@ var diagnostics = [
5839
5954
  overriddenSchemaConstructor,
5840
5955
  unsupportedServiceAccessors,
5841
5956
  nonObjectEffectServiceType,
5842
- deterministicKeys
5957
+ deterministicKeys,
5958
+ missedPipeableOpportunity,
5959
+ strictEffectProvide
5843
5960
  ];
5844
5961
 
5845
5962
  // src/effect-lsp-patch-utils.ts