@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.
package/README.md CHANGED
@@ -81,7 +81,7 @@ Some diagnostics are off by default or have a default severity of suggestion, bu
81
81
  <tr><td><code>leakingRequirements</code></td><td>💡</td><td></td><td>Detects implementation services leaked in service methods</td><td>✓</td><td>✓</td></tr>
82
82
  <tr><td><code>multipleEffectProvide</code></td><td>⚠️</td><td>🔧</td><td>Warns against chaining Effect.provide calls which can cause service lifecycle issues</td><td>✓</td><td>✓</td></tr>
83
83
  <tr><td><code>returnEffectInGen</code></td><td>💡</td><td>🔧</td><td>Warns when returning an Effect in a generator causes nested Effect&lt;Effect&lt;...&gt;&gt;</td><td>✓</td><td>✓</td></tr>
84
- <tr><td><code>runEffectInsideEffect</code></td><td>💡</td><td>🔧</td><td>Suggests using Runtime methods instead of Effect.run* inside Effect contexts</td><td>✓</td><td></td></tr>
84
+ <tr><td><code>runEffectInsideEffect</code></td><td>💡</td><td>🔧</td><td>Suggests using Runtime or Effect.run*With methods instead of Effect.run* inside Effect contexts</td><td>✓</td><td>✓</td></tr>
85
85
  <tr><td><code>schemaSyncInEffect</code></td><td>💡</td><td></td><td>Suggests using Effect-based Schema methods instead of sync methods inside Effect generators</td><td>✓</td><td></td></tr>
86
86
  <tr><td><code>scopeInLayerEffect</code></td><td>⚠️</td><td>🔧</td><td>Suggests using Layer.scoped instead of Layer.effect when Scope is in requirements</td><td>✓</td><td></td></tr>
87
87
  <tr><td><code>strictEffectProvide</code></td><td>➖</td><td></td><td>Warns when using Effect.provide with layers outside of application entry points</td><td>✓</td><td>✓</td></tr>
package/cli.js CHANGED
@@ -25942,7 +25942,7 @@ var runWith2 = (command, config2) => {
25942
25942
  // package.json
25943
25943
  var package_default = {
25944
25944
  name: "@effect/language-service",
25945
- version: "0.84.0",
25945
+ version: "0.84.2",
25946
25946
  publishConfig: {
25947
25947
  access: "public",
25948
25948
  directory: "dist"
@@ -28665,10 +28665,42 @@ function makeTypeCheckerUtils(ts, typeChecker, tsUtils) {
28665
28665
  if (node.parent && ts.isJsxOpeningElement(node.parent) && node.parent.tagName === node) return;
28666
28666
  if (node.parent && ts.isJsxClosingElement(node.parent) && node.parent.tagName === node) return;
28667
28667
  if (node.parent && ts.isJsxAttribute(node.parent) && node.parent.name === node) return;
28668
+ if (isInsideTypeOnlyHeritageExpression(node)) return;
28668
28669
  if (ts.isExpression(node) || ts.isTypeNode(node)) {
28669
28670
  return typeChecker.getTypeAtLocation(node);
28670
28671
  }
28671
28672
  }
28673
+ function isInsideTypeOnlyHeritageExpression(node) {
28674
+ if (ts.isExpressionWithTypeArguments(node)) {
28675
+ return isTypeOnlyHeritageClause(node.parent);
28676
+ }
28677
+ if (!ts.isIdentifier(node) && !ts.isPropertyAccessExpression(node)) {
28678
+ return false;
28679
+ }
28680
+ for (let current = node.parent; current; current = current.parent) {
28681
+ if (ts.isPropertyAccessExpression(current)) {
28682
+ continue;
28683
+ }
28684
+ if (ts.isExpressionWithTypeArguments(current)) {
28685
+ return isTypeOnlyHeritageClause(current.parent);
28686
+ }
28687
+ return false;
28688
+ }
28689
+ return false;
28690
+ }
28691
+ function isTypeOnlyHeritageClause(node) {
28692
+ if (!node || !ts.isHeritageClause(node)) {
28693
+ return false;
28694
+ }
28695
+ const container = node.parent;
28696
+ if (!container) {
28697
+ return false;
28698
+ }
28699
+ if (ts.isInterfaceDeclaration(container)) {
28700
+ return true;
28701
+ }
28702
+ return ts.isClassLike(container) && node.token === ts.SyntaxKind.ImplementsKeyword;
28703
+ }
28672
28704
  function resolveToGlobalSymbol(symbol4) {
28673
28705
  if (symbol4.flags & ts.SymbolFlags.Alias) {
28674
28706
  symbol4 = typeChecker.getAliasedSymbol(symbol4);
@@ -33935,12 +33967,14 @@ var getParameterName = (typescript, name) => {
33935
33967
  }
33936
33968
  return "parameter";
33937
33969
  };
33938
- var hasOuterContextualFunctionType = (typescript, typeChecker, node) => {
33970
+ var hasOuterContextualFunctionType = (typescript, typeChecker, typeCheckerUtils, node) => {
33939
33971
  const contextualType = typeChecker.getContextualType(node);
33940
33972
  if (!contextualType) {
33941
33973
  return false;
33942
33974
  }
33943
- return typeChecker.getSignaturesOfType(contextualType, typescript.SignatureKind.Call).length > 0;
33975
+ return typeCheckerUtils.unrollUnionMembers(contextualType).some(
33976
+ (type) => typeChecker.getSignaturesOfType(type, typescript.SignatureKind.Call).length > 0
33977
+ );
33944
33978
  };
33945
33979
  var effectFnImplicitAny = createDiagnostic({
33946
33980
  name: "effectFnImplicitAny",
@@ -33954,6 +33988,7 @@ var effectFnImplicitAny = createDiagnostic({
33954
33988
  const ts = yield* service2(TypeScriptApi);
33955
33989
  const program = yield* service2(TypeScriptProgram);
33956
33990
  const typeChecker = yield* service2(TypeCheckerApi);
33991
+ const typeCheckerUtils = yield* service2(TypeCheckerUtils);
33957
33992
  const typeParser = yield* service2(TypeParser);
33958
33993
  const noImplicitAny = program.getCompilerOptions().noImplicitAny ?? program.getCompilerOptions().strict ?? false;
33959
33994
  if (!noImplicitAny) {
@@ -33993,7 +34028,7 @@ var effectFnImplicitAny = createDiagnostic({
33993
34028
  ),
33994
34029
  orUndefined
33995
34030
  );
33996
- if (!parsed || hasOuterContextualFunctionType(ts, typeChecker, parsed.call)) {
34031
+ if (!parsed || hasOuterContextualFunctionType(ts, typeChecker, typeCheckerUtils, parsed.call)) {
33997
34032
  continue;
33998
34033
  }
33999
34034
  for (const parameter of parsed.fn.parameters) {
@@ -38088,16 +38123,16 @@ Nested Effect-able types may be intended if you plan to later manually flatten o
38088
38123
  var runEffectInsideEffect = createDiagnostic({
38089
38124
  name: "runEffectInsideEffect",
38090
38125
  code: 32,
38091
- description: "Suggests using Runtime methods instead of Effect.run* inside Effect contexts",
38126
+ description: "Suggests using Runtime or Effect.run*With methods instead of Effect.run* inside Effect contexts",
38092
38127
  group: "antipattern",
38093
38128
  severity: "suggestion",
38094
38129
  fixable: true,
38095
- supportedEffect: ["v3"],
38130
+ supportedEffect: ["v3", "v4"],
38096
38131
  apply: fn3("runEffectInsideEffect.apply")(function* (sourceFile, report) {
38097
38132
  const ts = yield* service2(TypeScriptApi);
38098
38133
  const typeParser = yield* service2(TypeParser);
38099
38134
  const tsUtils = yield* service2(TypeScriptUtils);
38100
- if (typeParser.supportedEffect() === "v4") return;
38135
+ const supportedEffect = typeParser.supportedEffect();
38101
38136
  const parseEffectMethod = (node, methodName) => pipe(
38102
38137
  typeParser.isNodeReferenceToEffectModuleApi(methodName)(node),
38103
38138
  map12(() => ({ node, methodName }))
@@ -38130,9 +38165,10 @@ var runEffectInsideEffect = createDiagnostic({
38130
38165
  if (scopeNode && scopeNode !== effectGen.generatorFunction) {
38131
38166
  const fixAddRuntime = gen3(function* () {
38132
38167
  const changeTracker = yield* service2(ChangeTracker);
38133
- const runtimeModuleIdentifier = tsUtils.findImportedModuleIdentifierByPackageAndNameOrBarrel(sourceFile, "effect", "Runtime") || "Runtime";
38134
38168
  const effectModuleIdentifier = tsUtils.findImportedModuleIdentifierByPackageAndNameOrBarrel(sourceFile, "effect", "Effect") || "Effect";
38169
+ const runtimeModuleIdentifier = tsUtils.findImportedModuleIdentifierByPackageAndNameOrBarrel(sourceFile, "effect", "Runtime") || "Runtime";
38135
38170
  let runtimeIdentifier = void 0;
38171
+ let servicesIdentifier = void 0;
38136
38172
  for (const statement of effectGen.generatorFunction.body.statements) {
38137
38173
  if (ts.isVariableStatement(statement) && statement.declarationList.declarations.length === 1) {
38138
38174
  const declaration = statement.declarationList.declarations[0];
@@ -38146,11 +38182,46 @@ var runEffectInsideEffect = createDiagnostic({
38146
38182
  if (isSome2(maybeEffectRuntime) && ts.isIdentifier(declaration.name)) {
38147
38183
  runtimeIdentifier = ts.idText(declaration.name);
38148
38184
  }
38185
+ const maybeEffectServices = yield* pipe(
38186
+ typeParser.isNodeReferenceToEffectModuleApi("services")(yieldedExpression.expression),
38187
+ option4
38188
+ );
38189
+ if (isSome2(maybeEffectServices) && ts.isIdentifier(declaration.name)) {
38190
+ servicesIdentifier = ts.idText(declaration.name);
38191
+ }
38149
38192
  }
38150
38193
  }
38151
38194
  }
38152
38195
  }
38153
- if (!runtimeIdentifier) {
38196
+ if (supportedEffect === "v4" && !servicesIdentifier) {
38197
+ changeTracker.insertNodeAt(
38198
+ sourceFile,
38199
+ effectGen.body.statements[0].pos,
38200
+ ts.factory.createVariableStatement(
38201
+ void 0,
38202
+ ts.factory.createVariableDeclarationList([ts.factory.createVariableDeclaration(
38203
+ "effectServices",
38204
+ void 0,
38205
+ void 0,
38206
+ ts.factory.createYieldExpression(
38207
+ ts.factory.createToken(ts.SyntaxKind.AsteriskToken),
38208
+ ts.factory.createCallExpression(
38209
+ ts.factory.createPropertyAccessExpression(
38210
+ ts.factory.createIdentifier(effectModuleIdentifier),
38211
+ "services"
38212
+ ),
38213
+ [ts.factory.createKeywordTypeNode(ts.SyntaxKind.NeverKeyword)],
38214
+ []
38215
+ )
38216
+ )
38217
+ )], ts.NodeFlags.Const)
38218
+ ),
38219
+ {
38220
+ prefix: "\n",
38221
+ suffix: "\n"
38222
+ }
38223
+ );
38224
+ } else if (supportedEffect === "v3" && !runtimeIdentifier) {
38154
38225
  changeTracker.insertNodeAt(
38155
38226
  sourceFile,
38156
38227
  effectGen.body.statements[0].pos,
@@ -38186,16 +38257,19 @@ var runEffectInsideEffect = createDiagnostic({
38186
38257
  changeTracker.insertText(
38187
38258
  sourceFile,
38188
38259
  node.arguments[0].pos,
38189
- `${runtimeModuleIdentifier}.${isEffectRunCall.value.methodName}(${runtimeIdentifier || "effectRuntime"}, `
38260
+ supportedEffect === "v4" ? `${effectModuleIdentifier}.${isEffectRunCall.value.methodName}With(${servicesIdentifier || "effectServices"})(` : `${runtimeModuleIdentifier}.${isEffectRunCall.value.methodName}(${runtimeIdentifier || "effectRuntime"}, `
38190
38261
  );
38191
38262
  });
38263
+ const v4MethodName = `${isEffectRunCall.value.methodName}With`;
38264
+ const messageText = supportedEffect === "v4" ? `Using ${nodeText} inside an Effect is not recommended. The same services should generally be used instead to run child effects.
38265
+ 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.
38266
+ Consider extracting the Runtime by using for example Effect.runtime and then use Runtime.${isEffectRunCall.value.methodName} with the extracted runtime instead.`;
38192
38267
  report({
38193
38268
  location: node.expression,
38194
- messageText: `Using ${nodeText} inside an Effect is not recommended. The same runtime should generally be used instead to run child effects.
38195
- Consider extracting the Runtime by using for example Effect.runtime and then use Runtime.${isEffectRunCall.value.methodName} with the extracted runtime instead.`,
38269
+ messageText,
38196
38270
  fixes: [{
38197
38271
  fixName: "runEffectInsideEffect_fix",
38198
- description: "Use a runtime to run the Effect",
38272
+ description: supportedEffect === "v4" ? "Use the current services to run the Effect" : "Use a runtime to run the Effect",
38199
38273
  apply: fixAddRuntime
38200
38274
  }]
38201
38275
  });
@@ -39861,11 +39935,12 @@ var metadata_default = {
39861
39935
  {
39862
39936
  name: "runEffectInsideEffect",
39863
39937
  group: "antipattern",
39864
- description: "Suggests using Runtime methods instead of Effect.run* inside Effect contexts",
39938
+ description: "Suggests using Runtime or Effect.run*With methods instead of Effect.run* inside Effect contexts",
39865
39939
  defaultSeverity: "suggestion",
39866
39940
  fixable: true,
39867
39941
  supportedEffect: [
39868
- "v3"
39942
+ "v3",
39943
+ "v4"
39869
39944
  ],
39870
39945
  preview: {
39871
39946
  sourceText: 'import { Effect } from "effect"\n\nexport const preview = Effect.gen(function*() {\n const run = () => Effect.runSync(Effect.succeed(1))\n return run()\n})\n',
@@ -39873,7 +39948,7 @@ var metadata_default = {
39873
39948
  {
39874
39949
  start: 101,
39875
39950
  end: 115,
39876
- text: "Using Effect.runSync inside an Effect is not recommended. The same runtime should generally be used instead to run child effects.\nConsider extracting the Runtime by using for example Effect.runtime and then use Runtime.runSync with the extracted runtime instead. effect(runEffectInsideEffect)"
39951
+ text: "Using Effect.runSync inside an Effect is not recommended. The same services should generally be used instead to run child effects.\nConsider extracting the current services by using for example Effect.services and then use Effect.runSyncWith with the extracted services instead. effect(runEffectInsideEffect)"
39877
39952
  }
39878
39953
  ]
39879
39954
  }