@effect/language-service 0.84.0 → 0.84.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.
@@ -3122,10 +3122,42 @@ function makeTypeCheckerUtils(ts, typeChecker, tsUtils) {
3122
3122
  if (node.parent && ts.isJsxOpeningElement(node.parent) && node.parent.tagName === node) return;
3123
3123
  if (node.parent && ts.isJsxClosingElement(node.parent) && node.parent.tagName === node) return;
3124
3124
  if (node.parent && ts.isJsxAttribute(node.parent) && node.parent.name === node) return;
3125
+ if (isInsideTypeOnlyHeritageExpression(node)) return;
3125
3126
  if (ts.isExpression(node) || ts.isTypeNode(node)) {
3126
3127
  return typeChecker.getTypeAtLocation(node);
3127
3128
  }
3128
3129
  }
3130
+ function isInsideTypeOnlyHeritageExpression(node) {
3131
+ if (ts.isExpressionWithTypeArguments(node)) {
3132
+ return isTypeOnlyHeritageClause(node.parent);
3133
+ }
3134
+ if (!ts.isIdentifier(node) && !ts.isPropertyAccessExpression(node)) {
3135
+ return false;
3136
+ }
3137
+ for (let current = node.parent; current; current = current.parent) {
3138
+ if (ts.isPropertyAccessExpression(current)) {
3139
+ continue;
3140
+ }
3141
+ if (ts.isExpressionWithTypeArguments(current)) {
3142
+ return isTypeOnlyHeritageClause(current.parent);
3143
+ }
3144
+ return false;
3145
+ }
3146
+ return false;
3147
+ }
3148
+ function isTypeOnlyHeritageClause(node) {
3149
+ if (!node || !ts.isHeritageClause(node)) {
3150
+ return false;
3151
+ }
3152
+ const container = node.parent;
3153
+ if (!container) {
3154
+ return false;
3155
+ }
3156
+ if (ts.isInterfaceDeclaration(container)) {
3157
+ return true;
3158
+ }
3159
+ return ts.isClassLike(container) && node.token === ts.SyntaxKind.ImplementsKeyword;
3160
+ }
3129
3161
  function resolveToGlobalSymbol(symbol3) {
3130
3162
  if (symbol3.flags & ts.SymbolFlags.Alias) {
3131
3163
  symbol3 = typeChecker.getAliasedSymbol(symbol3);
@@ -5937,12 +5969,14 @@ var getParameterName = (typescript, name) => {
5937
5969
  }
5938
5970
  return "parameter";
5939
5971
  };
5940
- var hasOuterContextualFunctionType = (typescript, typeChecker, node) => {
5972
+ var hasOuterContextualFunctionType = (typescript, typeChecker, typeCheckerUtils, node) => {
5941
5973
  const contextualType = typeChecker.getContextualType(node);
5942
5974
  if (!contextualType) {
5943
5975
  return false;
5944
5976
  }
5945
- return typeChecker.getSignaturesOfType(contextualType, typescript.SignatureKind.Call).length > 0;
5977
+ return typeCheckerUtils.unrollUnionMembers(contextualType).some(
5978
+ (type) => typeChecker.getSignaturesOfType(type, typescript.SignatureKind.Call).length > 0
5979
+ );
5946
5980
  };
5947
5981
  var effectFnImplicitAny = createDiagnostic({
5948
5982
  name: "effectFnImplicitAny",
@@ -5956,6 +5990,7 @@ var effectFnImplicitAny = createDiagnostic({
5956
5990
  const ts = yield* service(TypeScriptApi);
5957
5991
  const program = yield* service(TypeScriptProgram);
5958
5992
  const typeChecker = yield* service(TypeCheckerApi);
5993
+ const typeCheckerUtils = yield* service(TypeCheckerUtils);
5959
5994
  const typeParser = yield* service(TypeParser);
5960
5995
  const noImplicitAny = program.getCompilerOptions().noImplicitAny ?? program.getCompilerOptions().strict ?? false;
5961
5996
  if (!noImplicitAny) {
@@ -5995,7 +6030,7 @@ var effectFnImplicitAny = createDiagnostic({
5995
6030
  ),
5996
6031
  orUndefined
5997
6032
  );
5998
- if (!parsed || hasOuterContextualFunctionType(ts, typeChecker, parsed.call)) {
6033
+ if (!parsed || hasOuterContextualFunctionType(ts, typeChecker, typeCheckerUtils, parsed.call)) {
5999
6034
  continue;
6000
6035
  }
6001
6036
  for (const parameter of parsed.fn.parameters) {
@@ -11159,16 +11194,16 @@ Nested Effect-able types may be intended if you plan to later manually flatten o
11159
11194
  var runEffectInsideEffect = createDiagnostic({
11160
11195
  name: "runEffectInsideEffect",
11161
11196
  code: 32,
11162
- description: "Suggests using Runtime methods instead of Effect.run* inside Effect contexts",
11197
+ description: "Suggests using Runtime or Effect.run*With methods instead of Effect.run* inside Effect contexts",
11163
11198
  group: "antipattern",
11164
11199
  severity: "suggestion",
11165
11200
  fixable: true,
11166
- supportedEffect: ["v3"],
11201
+ supportedEffect: ["v3", "v4"],
11167
11202
  apply: fn("runEffectInsideEffect.apply")(function* (sourceFile, report) {
11168
11203
  const ts = yield* service(TypeScriptApi);
11169
11204
  const typeParser = yield* service(TypeParser);
11170
11205
  const tsUtils = yield* service(TypeScriptUtils);
11171
- if (typeParser.supportedEffect() === "v4") return;
11206
+ const supportedEffect = typeParser.supportedEffect();
11172
11207
  const parseEffectMethod = (node, methodName) => pipe(
11173
11208
  typeParser.isNodeReferenceToEffectModuleApi(methodName)(node),
11174
11209
  map4(() => ({ node, methodName }))
@@ -11201,9 +11236,10 @@ var runEffectInsideEffect = createDiagnostic({
11201
11236
  if (scopeNode && scopeNode !== effectGen.generatorFunction) {
11202
11237
  const fixAddRuntime = gen(function* () {
11203
11238
  const changeTracker = yield* service(ChangeTracker);
11204
- const runtimeModuleIdentifier = tsUtils.findImportedModuleIdentifierByPackageAndNameOrBarrel(sourceFile, "effect", "Runtime") || "Runtime";
11205
11239
  const effectModuleIdentifier = tsUtils.findImportedModuleIdentifierByPackageAndNameOrBarrel(sourceFile, "effect", "Effect") || "Effect";
11240
+ const runtimeModuleIdentifier = tsUtils.findImportedModuleIdentifierByPackageAndNameOrBarrel(sourceFile, "effect", "Runtime") || "Runtime";
11206
11241
  let runtimeIdentifier = void 0;
11242
+ let servicesIdentifier = void 0;
11207
11243
  for (const statement of effectGen.generatorFunction.body.statements) {
11208
11244
  if (ts.isVariableStatement(statement) && statement.declarationList.declarations.length === 1) {
11209
11245
  const declaration = statement.declarationList.declarations[0];
@@ -11217,11 +11253,46 @@ var runEffectInsideEffect = createDiagnostic({
11217
11253
  if (isSome2(maybeEffectRuntime) && ts.isIdentifier(declaration.name)) {
11218
11254
  runtimeIdentifier = ts.idText(declaration.name);
11219
11255
  }
11256
+ const maybeEffectServices = yield* pipe(
11257
+ typeParser.isNodeReferenceToEffectModuleApi("services")(yieldedExpression.expression),
11258
+ option
11259
+ );
11260
+ if (isSome2(maybeEffectServices) && ts.isIdentifier(declaration.name)) {
11261
+ servicesIdentifier = ts.idText(declaration.name);
11262
+ }
11220
11263
  }
11221
11264
  }
11222
11265
  }
11223
11266
  }
11224
- if (!runtimeIdentifier) {
11267
+ if (supportedEffect === "v4" && !servicesIdentifier) {
11268
+ changeTracker.insertNodeAt(
11269
+ sourceFile,
11270
+ effectGen.body.statements[0].pos,
11271
+ ts.factory.createVariableStatement(
11272
+ void 0,
11273
+ ts.factory.createVariableDeclarationList([ts.factory.createVariableDeclaration(
11274
+ "effectServices",
11275
+ void 0,
11276
+ void 0,
11277
+ ts.factory.createYieldExpression(
11278
+ ts.factory.createToken(ts.SyntaxKind.AsteriskToken),
11279
+ ts.factory.createCallExpression(
11280
+ ts.factory.createPropertyAccessExpression(
11281
+ ts.factory.createIdentifier(effectModuleIdentifier),
11282
+ "services"
11283
+ ),
11284
+ [ts.factory.createKeywordTypeNode(ts.SyntaxKind.NeverKeyword)],
11285
+ []
11286
+ )
11287
+ )
11288
+ )], ts.NodeFlags.Const)
11289
+ ),
11290
+ {
11291
+ prefix: "\n",
11292
+ suffix: "\n"
11293
+ }
11294
+ );
11295
+ } else if (supportedEffect === "v3" && !runtimeIdentifier) {
11225
11296
  changeTracker.insertNodeAt(
11226
11297
  sourceFile,
11227
11298
  effectGen.body.statements[0].pos,
@@ -11257,16 +11328,19 @@ var runEffectInsideEffect = createDiagnostic({
11257
11328
  changeTracker.insertText(
11258
11329
  sourceFile,
11259
11330
  node.arguments[0].pos,
11260
- `${runtimeModuleIdentifier}.${isEffectRunCall.value.methodName}(${runtimeIdentifier || "effectRuntime"}, `
11331
+ supportedEffect === "v4" ? `${effectModuleIdentifier}.${isEffectRunCall.value.methodName}With(${servicesIdentifier || "effectServices"})(` : `${runtimeModuleIdentifier}.${isEffectRunCall.value.methodName}(${runtimeIdentifier || "effectRuntime"}, `
11261
11332
  );
11262
11333
  });
11334
+ const v4MethodName = `${isEffectRunCall.value.methodName}With`;
11335
+ const messageText = supportedEffect === "v4" ? `Using ${nodeText} inside an Effect is not recommended. The same services should generally be used instead to run child effects.
11336
+ Consider extracting the current services by using for example Effect.services and then use Effect.${v4MethodName} with the extracted services instead.` : `Using ${nodeText} inside an Effect is not recommended. The same runtime should generally be used instead to run child effects.
11337
+ Consider extracting the Runtime by using for example Effect.runtime and then use Runtime.${isEffectRunCall.value.methodName} with the extracted runtime instead.`;
11263
11338
  report({
11264
11339
  location: node.expression,
11265
- messageText: `Using ${nodeText} inside an Effect is not recommended. The same runtime should generally be used instead to run child effects.
11266
- Consider extracting the Runtime by using for example Effect.runtime and then use Runtime.${isEffectRunCall.value.methodName} with the extracted runtime instead.`,
11340
+ messageText,
11267
11341
  fixes: [{
11268
11342
  fixName: "runEffectInsideEffect_fix",
11269
- description: "Use a runtime to run the Effect",
11343
+ description: supportedEffect === "v4" ? "Use the current services to run the Effect" : "Use a runtime to run the Effect",
11270
11344
  apply: fixAddRuntime
11271
11345
  }]
11272
11346
  });