@effect/language-service 0.83.1 → 0.84.1

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
@@ -3775,6 +3775,25 @@ function makeTypeCheckerUtils(ts, typeChecker, tsUtils) {
3775
3775
  return typeChecker.getTypeAtLocation(node);
3776
3776
  }
3777
3777
  }
3778
+ function resolveToGlobalSymbol(symbol3) {
3779
+ if (symbol3.flags & ts.SymbolFlags.Alias) {
3780
+ symbol3 = typeChecker.getAliasedSymbol(symbol3);
3781
+ }
3782
+ let depth = 0;
3783
+ while (depth < 2 && symbol3.valueDeclaration && ts.isVariableDeclaration(symbol3.valueDeclaration)) {
3784
+ const initializer = symbol3.valueDeclaration.initializer;
3785
+ if (!initializer) break;
3786
+ let nextSymbol = typeChecker.getSymbolAtLocation(initializer);
3787
+ if (!nextSymbol) break;
3788
+ if (nextSymbol.flags & ts.SymbolFlags.Alias) {
3789
+ nextSymbol = typeChecker.getAliasedSymbol(nextSymbol);
3790
+ }
3791
+ if (nextSymbol === symbol3) break;
3792
+ symbol3 = nextSymbol;
3793
+ depth++;
3794
+ }
3795
+ return symbol3;
3796
+ }
3778
3797
  return {
3779
3798
  isUnion,
3780
3799
  isReadonlyArrayType,
@@ -3788,7 +3807,8 @@ function makeTypeCheckerUtils(ts, typeChecker, tsUtils) {
3788
3807
  expectedAndRealType,
3789
3808
  typeToSimplifiedTypeNode,
3790
3809
  isGlobalErrorType,
3791
- getTypeAtLocation
3810
+ getTypeAtLocation,
3811
+ resolveToGlobalSymbol
3792
3812
  };
3793
3813
  }
3794
3814
 
@@ -4476,7 +4496,11 @@ function make3(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
4476
4496
  }
4477
4497
  currentParent = nodeToCheck.parent;
4478
4498
  }
4479
- return { scopeNode, effectGen: effectGenResult };
4499
+ return {
4500
+ inEffect: effectGenResult !== void 0 && effectGenResult.body.statements.length > 0 && scopeNode === effectGenResult.generatorFunction,
4501
+ scopeNode,
4502
+ effectGen: effectGenResult
4503
+ };
4480
4504
  });
4481
4505
  const effectFn = cachedBy(
4482
4506
  function(node) {
@@ -7727,12 +7751,14 @@ var getParameterName = (typescript, name) => {
7727
7751
  }
7728
7752
  return "parameter";
7729
7753
  };
7730
- var hasOuterContextualFunctionType = (typescript, typeChecker, node) => {
7754
+ var hasOuterContextualFunctionType = (typescript, typeChecker, typeCheckerUtils, node) => {
7731
7755
  const contextualType = typeChecker.getContextualType(node);
7732
7756
  if (!contextualType) {
7733
7757
  return false;
7734
7758
  }
7735
- return typeChecker.getSignaturesOfType(contextualType, typescript.SignatureKind.Call).length > 0;
7759
+ return typeCheckerUtils.unrollUnionMembers(contextualType).some(
7760
+ (type) => typeChecker.getSignaturesOfType(type, typescript.SignatureKind.Call).length > 0
7761
+ );
7736
7762
  };
7737
7763
  var effectFnImplicitAny = createDiagnostic({
7738
7764
  name: "effectFnImplicitAny",
@@ -7746,6 +7772,7 @@ var effectFnImplicitAny = createDiagnostic({
7746
7772
  const ts = yield* service(TypeScriptApi);
7747
7773
  const program = yield* service(TypeScriptProgram);
7748
7774
  const typeChecker = yield* service(TypeCheckerApi);
7775
+ const typeCheckerUtils = yield* service(TypeCheckerUtils);
7749
7776
  const typeParser = yield* service(TypeParser);
7750
7777
  const noImplicitAny = program.getCompilerOptions().noImplicitAny ?? program.getCompilerOptions().strict ?? false;
7751
7778
  if (!noImplicitAny) {
@@ -7785,7 +7812,7 @@ var effectFnImplicitAny = createDiagnostic({
7785
7812
  ),
7786
7813
  orUndefined
7787
7814
  );
7788
- if (!parsed || hasOuterContextualFunctionType(ts, typeChecker, parsed.call)) {
7815
+ if (!parsed || hasOuterContextualFunctionType(ts, typeChecker, typeCheckerUtils, parsed.call)) {
7789
7816
  continue;
7790
7817
  }
7791
7818
  for (const parameter of parsed.fn.parameters) {
@@ -8836,6 +8863,128 @@ var genericEffectServices = createDiagnostic({
8836
8863
  })
8837
8864
  });
8838
8865
 
8866
+ // src/diagnostics/globalConsoleInEffect.ts
8867
+ var consoleMethodAlternatives = {
8868
+ "log": "Effect.log or Logger",
8869
+ "warn": "Effect.logWarning or Logger",
8870
+ "error": "Effect.logError or Logger",
8871
+ "info": "Effect.logInfo or Logger",
8872
+ "debug": "Effect.logDebug or Logger",
8873
+ "trace": "Effect.logTrace or Logger"
8874
+ };
8875
+ var makeGlobalConsoleApply = (checkInEffect) => fn(`globalConsole${checkInEffect ? "InEffect" : ""}.apply`)(function* (sourceFile, report) {
8876
+ const ts = yield* service(TypeScriptApi);
8877
+ const typeChecker = yield* service(TypeCheckerApi);
8878
+ const typeCheckerUtils = yield* service(TypeCheckerUtils);
8879
+ const typeParser = yield* service(TypeParser);
8880
+ const consoleSymbol = typeChecker.resolveName("console", void 0, ts.SymbolFlags.Value, false);
8881
+ if (!consoleSymbol) return;
8882
+ const nodeToVisit = [];
8883
+ const appendNodeToVisit = (node) => {
8884
+ nodeToVisit.push(node);
8885
+ return void 0;
8886
+ };
8887
+ ts.forEachChild(sourceFile, appendNodeToVisit);
8888
+ while (nodeToVisit.length > 0) {
8889
+ const node = nodeToVisit.shift();
8890
+ ts.forEachChild(node, appendNodeToVisit);
8891
+ if (!ts.isCallExpression(node) || !ts.isPropertyAccessExpression(node.expression)) continue;
8892
+ const method = ts.idText(node.expression.name);
8893
+ const alternative = consoleMethodAlternatives[method];
8894
+ if (!alternative) continue;
8895
+ const symbol3 = typeChecker.getSymbolAtLocation(node.expression.expression);
8896
+ if (!symbol3) continue;
8897
+ if (typeCheckerUtils.resolveToGlobalSymbol(symbol3) !== consoleSymbol) continue;
8898
+ const { inEffect } = yield* typeParser.findEnclosingScopes(node);
8899
+ if (inEffect !== checkInEffect) continue;
8900
+ report({
8901
+ location: node,
8902
+ messageText: checkInEffect ? `Prefer using ${alternative} instead of console.${method} inside Effect generators.` : `Prefer using ${alternative} instead of console.${method}.`,
8903
+ fixes: []
8904
+ });
8905
+ }
8906
+ });
8907
+ var globalConsoleInEffect = createDiagnostic({
8908
+ name: "globalConsoleInEffect",
8909
+ code: 56,
8910
+ description: "Warns when using console methods inside Effect generators instead of Effect.log/Logger",
8911
+ group: "effectNative",
8912
+ severity: "off",
8913
+ fixable: false,
8914
+ supportedEffect: ["v3", "v4"],
8915
+ apply: makeGlobalConsoleApply(true)
8916
+ });
8917
+
8918
+ // src/diagnostics/globalConsole.ts
8919
+ var globalConsole = createDiagnostic({
8920
+ name: "globalConsole",
8921
+ code: 60,
8922
+ description: "Warns when using console methods outside Effect generators instead of Effect.log/Logger",
8923
+ group: "effectNative",
8924
+ severity: "off",
8925
+ fixable: false,
8926
+ supportedEffect: ["v3", "v4"],
8927
+ apply: makeGlobalConsoleApply(false)
8928
+ });
8929
+
8930
+ // src/diagnostics/globalDateInEffect.ts
8931
+ var makeGlobalDateApply = (checkInEffect) => fn(`globalDate${checkInEffect ? "InEffect" : ""}.apply`)(function* (sourceFile, report) {
8932
+ const ts = yield* service(TypeScriptApi);
8933
+ const typeChecker = yield* service(TypeCheckerApi);
8934
+ const typeCheckerUtils = yield* service(TypeCheckerUtils);
8935
+ const typeParser = yield* service(TypeParser);
8936
+ const dateSymbol = typeChecker.resolveName("Date", void 0, ts.SymbolFlags.Value, false);
8937
+ if (!dateSymbol) return;
8938
+ const nodeToVisit = [];
8939
+ const appendNodeToVisit = (node) => {
8940
+ nodeToVisit.push(node);
8941
+ return void 0;
8942
+ };
8943
+ ts.forEachChild(sourceFile, appendNodeToVisit);
8944
+ while (nodeToVisit.length > 0) {
8945
+ const node = nodeToVisit.shift();
8946
+ ts.forEachChild(node, appendNodeToVisit);
8947
+ let messageText;
8948
+ let objectNode;
8949
+ if (ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression) && ts.idText(node.expression.name) === "now") {
8950
+ objectNode = node.expression.expression;
8951
+ messageText = checkInEffect ? "Prefer using Clock or DateTime from Effect instead of Date.now() inside Effect generators." : "Prefer using Clock or DateTime from Effect instead of Date.now().";
8952
+ } else if (ts.isNewExpression(node)) {
8953
+ objectNode = node.expression;
8954
+ messageText = checkInEffect ? "Prefer using DateTime from Effect instead of new Date() inside Effect generators." : "Prefer using DateTime from Effect instead of new Date().";
8955
+ }
8956
+ if (!messageText || !objectNode) continue;
8957
+ const symbol3 = typeChecker.getSymbolAtLocation(objectNode);
8958
+ if (!symbol3) continue;
8959
+ if (typeCheckerUtils.resolveToGlobalSymbol(symbol3) !== dateSymbol) continue;
8960
+ const { inEffect } = yield* typeParser.findEnclosingScopes(node);
8961
+ if (inEffect !== checkInEffect) continue;
8962
+ report({ location: node, messageText, fixes: [] });
8963
+ }
8964
+ });
8965
+ var globalDateInEffect = createDiagnostic({
8966
+ name: "globalDateInEffect",
8967
+ code: 55,
8968
+ description: "Warns when using Date.now() or new Date() inside Effect generators instead of Clock/DateTime",
8969
+ group: "effectNative",
8970
+ severity: "off",
8971
+ fixable: false,
8972
+ supportedEffect: ["v3", "v4"],
8973
+ apply: makeGlobalDateApply(true)
8974
+ });
8975
+
8976
+ // src/diagnostics/globalDate.ts
8977
+ var globalDate = createDiagnostic({
8978
+ name: "globalDate",
8979
+ code: 59,
8980
+ description: "Warns when using Date.now() or new Date() outside Effect generators instead of Clock/DateTime",
8981
+ group: "effectNative",
8982
+ severity: "off",
8983
+ fixable: false,
8984
+ supportedEffect: ["v3", "v4"],
8985
+ apply: makeGlobalDateApply(false)
8986
+ });
8987
+
8839
8988
  // src/diagnostics/globalErrorInEffectCatch.ts
8840
8989
  var globalErrorInEffectCatch = createDiagnostic({
8841
8990
  name: "globalErrorInEffectCatch",
@@ -8955,46 +9104,186 @@ var globalErrorInEffectFailure = createDiagnostic({
8955
9104
  })
8956
9105
  });
8957
9106
 
9107
+ // src/diagnostics/globalFetchInEffect.ts
9108
+ var makeGlobalFetchApply = (checkInEffect) => fn(`globalFetch${checkInEffect ? "InEffect" : ""}.apply`)(function* (sourceFile, report) {
9109
+ const ts = yield* service(TypeScriptApi);
9110
+ const typeChecker = yield* service(TypeCheckerApi);
9111
+ const typeCheckerUtils = yield* service(TypeCheckerUtils);
9112
+ const typeParser = yield* service(TypeParser);
9113
+ const fetchSymbol = typeChecker.resolveName("fetch", void 0, ts.SymbolFlags.Value, false);
9114
+ if (!fetchSymbol) return;
9115
+ const effectVersion = typeParser.supportedEffect();
9116
+ const packageName = effectVersion === "v3" ? "@effect/platform" : "effect/unstable/http";
9117
+ const messageText = checkInEffect ? `Prefer using HttpClient from ${packageName} instead of the global 'fetch' function inside Effect generators.` : `Prefer using HttpClient from ${packageName} instead of the global 'fetch' function.`;
9118
+ const nodeToVisit = [];
9119
+ const appendNodeToVisit = (node) => {
9120
+ nodeToVisit.push(node);
9121
+ return void 0;
9122
+ };
9123
+ ts.forEachChild(sourceFile, appendNodeToVisit);
9124
+ while (nodeToVisit.length > 0) {
9125
+ const node = nodeToVisit.shift();
9126
+ if (ts.isCallExpression(node)) {
9127
+ const symbol3 = typeChecker.getSymbolAtLocation(node.expression);
9128
+ if (symbol3 && typeCheckerUtils.resolveToGlobalSymbol(symbol3) === fetchSymbol) {
9129
+ const { inEffect } = yield* typeParser.findEnclosingScopes(node);
9130
+ if (inEffect === checkInEffect) {
9131
+ report({
9132
+ location: node.expression,
9133
+ messageText,
9134
+ fixes: []
9135
+ });
9136
+ }
9137
+ }
9138
+ }
9139
+ ts.forEachChild(node, appendNodeToVisit);
9140
+ }
9141
+ });
9142
+ var globalFetchInEffect = createDiagnostic({
9143
+ name: "globalFetchInEffect",
9144
+ code: 63,
9145
+ description: "Warns when using the global fetch function inside Effect generators instead of the Effect HTTP client",
9146
+ group: "effectNative",
9147
+ severity: "off",
9148
+ fixable: false,
9149
+ supportedEffect: ["v3", "v4"],
9150
+ apply: makeGlobalFetchApply(true)
9151
+ });
9152
+
8958
9153
  // src/diagnostics/globalFetch.ts
8959
9154
  var globalFetch = createDiagnostic({
8960
9155
  name: "globalFetch",
8961
9156
  code: 53,
8962
- description: "Warns when using the global fetch function instead of the Effect HTTP client",
9157
+ description: "Warns when using the global fetch function outside Effect generators instead of the Effect HTTP client",
8963
9158
  group: "effectNative",
8964
9159
  severity: "off",
8965
9160
  fixable: false,
8966
9161
  supportedEffect: ["v3", "v4"],
8967
- apply: fn("globalFetch.apply")(function* (sourceFile, report) {
8968
- const ts = yield* service(TypeScriptApi);
8969
- const typeChecker = yield* service(TypeCheckerApi);
8970
- const typeParser = yield* service(TypeParser);
8971
- const fetchSymbol = typeChecker.resolveName("fetch", void 0, ts.SymbolFlags.Value, false);
8972
- if (!fetchSymbol) return;
8973
- const effectVersion = typeParser.supportedEffect();
8974
- const packageName = effectVersion === "v3" ? "@effect/platform" : "effect/unstable/http";
8975
- const messageText = `Prefer using HttpClient from ${packageName} instead of the global 'fetch' function.`;
8976
- const nodeToVisit = [];
8977
- const appendNodeToVisit = (node) => {
8978
- nodeToVisit.push(node);
8979
- return void 0;
8980
- };
8981
- ts.forEachChild(sourceFile, appendNodeToVisit);
8982
- while (nodeToVisit.length > 0) {
8983
- const node = nodeToVisit.shift();
8984
- if (ts.isCallExpression(node)) {
8985
- const symbol3 = typeChecker.getSymbolAtLocation(node.expression);
8986
- const resolvedSymbol = symbol3 && symbol3.flags & ts.SymbolFlags.Alias ? typeChecker.getAliasedSymbol(symbol3) : symbol3;
8987
- if (resolvedSymbol === fetchSymbol) {
8988
- report({
8989
- location: node.expression,
8990
- messageText,
8991
- fixes: []
8992
- });
8993
- }
9162
+ apply: makeGlobalFetchApply(false)
9163
+ });
9164
+
9165
+ // src/diagnostics/globalRandomInEffect.ts
9166
+ var makeGlobalRandomApply = (checkInEffect) => fn(`globalRandom${checkInEffect ? "InEffect" : ""}.apply`)(function* (sourceFile, report) {
9167
+ const ts = yield* service(TypeScriptApi);
9168
+ const typeChecker = yield* service(TypeCheckerApi);
9169
+ const typeCheckerUtils = yield* service(TypeCheckerUtils);
9170
+ const typeParser = yield* service(TypeParser);
9171
+ const mathSymbol = typeChecker.resolveName("Math", void 0, ts.SymbolFlags.Value, false);
9172
+ if (!mathSymbol) return;
9173
+ const nodeToVisit = [];
9174
+ const appendNodeToVisit = (node) => {
9175
+ nodeToVisit.push(node);
9176
+ return void 0;
9177
+ };
9178
+ ts.forEachChild(sourceFile, appendNodeToVisit);
9179
+ while (nodeToVisit.length > 0) {
9180
+ const node = nodeToVisit.shift();
9181
+ ts.forEachChild(node, appendNodeToVisit);
9182
+ if (!ts.isCallExpression(node) || !ts.isPropertyAccessExpression(node.expression) || ts.idText(node.expression.name) !== "random") continue;
9183
+ const symbol3 = typeChecker.getSymbolAtLocation(node.expression.expression);
9184
+ if (!symbol3) continue;
9185
+ if (typeCheckerUtils.resolveToGlobalSymbol(symbol3) !== mathSymbol) continue;
9186
+ const { inEffect } = yield* typeParser.findEnclosingScopes(node);
9187
+ if (inEffect !== checkInEffect) continue;
9188
+ report({
9189
+ location: node,
9190
+ messageText: checkInEffect ? "Prefer using the Random service from Effect instead of Math.random() inside Effect generators." : "Prefer using the Random service from Effect instead of Math.random().",
9191
+ fixes: []
9192
+ });
9193
+ }
9194
+ });
9195
+ var globalRandomInEffect = createDiagnostic({
9196
+ name: "globalRandomInEffect",
9197
+ code: 57,
9198
+ description: "Warns when using Math.random() inside Effect generators instead of the Random service",
9199
+ group: "effectNative",
9200
+ severity: "off",
9201
+ fixable: false,
9202
+ supportedEffect: ["v3", "v4"],
9203
+ apply: makeGlobalRandomApply(true)
9204
+ });
9205
+
9206
+ // src/diagnostics/globalRandom.ts
9207
+ var globalRandom = createDiagnostic({
9208
+ name: "globalRandom",
9209
+ code: 61,
9210
+ description: "Warns when using Math.random() outside Effect generators instead of the Random service",
9211
+ group: "effectNative",
9212
+ severity: "off",
9213
+ fixable: false,
9214
+ supportedEffect: ["v3", "v4"],
9215
+ apply: makeGlobalRandomApply(false)
9216
+ });
9217
+
9218
+ // src/diagnostics/globalTimersInEffect.ts
9219
+ var timerAlternatives = {
9220
+ "setTimeout": {
9221
+ inEffect: "Prefer using Effect.sleep or Schedule from Effect instead of setTimeout inside Effect generators.",
9222
+ outsideEffect: "Prefer using Effect.sleep or Schedule from Effect instead of setTimeout."
9223
+ },
9224
+ "setInterval": {
9225
+ inEffect: "Prefer using Schedule or Effect.repeat from Effect instead of setInterval inside Effect generators.",
9226
+ outsideEffect: "Prefer using Schedule or Effect.repeat from Effect instead of setInterval."
9227
+ }
9228
+ };
9229
+ var makeGlobalTimersApply = (checkInEffect) => fn(`globalTimers${checkInEffect ? "InEffect" : ""}.apply`)(function* (sourceFile, report) {
9230
+ const ts = yield* service(TypeScriptApi);
9231
+ const typeChecker = yield* service(TypeCheckerApi);
9232
+ const typeCheckerUtils = yield* service(TypeCheckerUtils);
9233
+ const typeParser = yield* service(TypeParser);
9234
+ const globalSymbols = /* @__PURE__ */ new Map();
9235
+ for (const name of Object.keys(timerAlternatives)) {
9236
+ const symbol3 = typeChecker.resolveName(name, void 0, ts.SymbolFlags.Value, false);
9237
+ if (symbol3) globalSymbols.set(name, symbol3);
9238
+ }
9239
+ if (globalSymbols.size === 0) return;
9240
+ const nodeToVisit = [];
9241
+ const appendNodeToVisit = (node) => {
9242
+ nodeToVisit.push(node);
9243
+ return void 0;
9244
+ };
9245
+ ts.forEachChild(sourceFile, appendNodeToVisit);
9246
+ while (nodeToVisit.length > 0) {
9247
+ const node = nodeToVisit.shift();
9248
+ ts.forEachChild(node, appendNodeToVisit);
9249
+ if (!ts.isCallExpression(node)) continue;
9250
+ const symbol3 = typeChecker.getSymbolAtLocation(node.expression);
9251
+ if (!symbol3) continue;
9252
+ const resolvedSymbol = typeCheckerUtils.resolveToGlobalSymbol(symbol3);
9253
+ let messageText;
9254
+ for (const [name, symbol4] of globalSymbols) {
9255
+ if (resolvedSymbol === symbol4) {
9256
+ messageText = checkInEffect ? timerAlternatives[name].inEffect : timerAlternatives[name].outsideEffect;
9257
+ break;
8994
9258
  }
8995
- ts.forEachChild(node, appendNodeToVisit);
8996
9259
  }
8997
- })
9260
+ if (!messageText) continue;
9261
+ const { inEffect } = yield* typeParser.findEnclosingScopes(node);
9262
+ if (inEffect !== checkInEffect) continue;
9263
+ report({ location: node, messageText, fixes: [] });
9264
+ }
9265
+ });
9266
+ var globalTimersInEffect = createDiagnostic({
9267
+ name: "globalTimersInEffect",
9268
+ code: 58,
9269
+ description: "Warns when using setTimeout/setInterval inside Effect generators instead of Effect.sleep/Schedule",
9270
+ group: "effectNative",
9271
+ severity: "off",
9272
+ fixable: false,
9273
+ supportedEffect: ["v3", "v4"],
9274
+ apply: makeGlobalTimersApply(true)
9275
+ });
9276
+
9277
+ // src/diagnostics/globalTimers.ts
9278
+ var globalTimers = createDiagnostic({
9279
+ name: "globalTimers",
9280
+ code: 62,
9281
+ description: "Warns when using setTimeout/setInterval outside Effect generators instead of Effect.sleep/Schedule",
9282
+ group: "effectNative",
9283
+ severity: "off",
9284
+ fixable: false,
9285
+ supportedEffect: ["v3", "v4"],
9286
+ apply: makeGlobalTimersApply(false)
8998
9287
  });
8999
9288
 
9000
9289
  // src/diagnostics/importFromBarrel.ts
@@ -10059,8 +10348,8 @@ var missingReturnYieldStar = createDiagnostic({
10059
10348
  if (!type) continue;
10060
10349
  const maybeEffect = yield* option(typeParser.effectYieldableType(type, unwrapped.expression));
10061
10350
  if (!(isSome2(maybeEffect) && maybeEffect.value.A.flags & ts.TypeFlags.Never)) continue;
10062
- const { effectGen, scopeNode } = yield* typeParser.findEnclosingScopes(node);
10063
- if (!effectGen || scopeNode && scopeNode !== effectGen.generatorFunction) continue;
10351
+ const { inEffect } = yield* typeParser.findEnclosingScopes(node);
10352
+ if (!inEffect) continue;
10064
10353
  const fix = [{
10065
10354
  fixName: "missingReturnYieldStar_fix",
10066
10355
  description: "Add return statement",
@@ -11454,11 +11743,8 @@ var preferSchemaOverJson = createDiagnostic({
11454
11743
  });
11455
11744
  const jsonMethodInEffectGen = fn("preferSchemaOverJson.jsonMethodInEffectGen")(
11456
11745
  function* (jsonCall) {
11457
- const { effectGen, scopeNode } = yield* typeParser.findEnclosingScopes(jsonCall);
11458
- if (!effectGen || effectGen.body.statements.length === 0) {
11459
- return yield* TypeParserIssue.issue;
11460
- }
11461
- if (scopeNode && scopeNode !== effectGen.generatorFunction) {
11746
+ const { inEffect } = yield* typeParser.findEnclosingScopes(jsonCall);
11747
+ if (!inEffect) {
11462
11748
  return yield* TypeParserIssue.issue;
11463
11749
  }
11464
11750
  return jsonCall;
@@ -11621,16 +11907,16 @@ Nested Effect-able types may be intended if you plan to later manually flatten o
11621
11907
  var runEffectInsideEffect = createDiagnostic({
11622
11908
  name: "runEffectInsideEffect",
11623
11909
  code: 32,
11624
- description: "Suggests using Runtime methods instead of Effect.run* inside Effect contexts",
11910
+ description: "Suggests using Runtime or Effect.run*With methods instead of Effect.run* inside Effect contexts",
11625
11911
  group: "antipattern",
11626
11912
  severity: "suggestion",
11627
11913
  fixable: true,
11628
- supportedEffect: ["v3"],
11914
+ supportedEffect: ["v3", "v4"],
11629
11915
  apply: fn("runEffectInsideEffect.apply")(function* (sourceFile, report) {
11630
11916
  const ts = yield* service(TypeScriptApi);
11631
11917
  const typeParser = yield* service(TypeParser);
11632
11918
  const tsUtils = yield* service(TypeScriptUtils);
11633
- if (typeParser.supportedEffect() === "v4") return;
11919
+ const supportedEffect = typeParser.supportedEffect();
11634
11920
  const parseEffectMethod = (node, methodName) => pipe(
11635
11921
  typeParser.isNodeReferenceToEffectModuleApi(methodName)(node),
11636
11922
  map5(() => ({ node, methodName }))
@@ -11663,9 +11949,10 @@ var runEffectInsideEffect = createDiagnostic({
11663
11949
  if (scopeNode && scopeNode !== effectGen.generatorFunction) {
11664
11950
  const fixAddRuntime = gen(function* () {
11665
11951
  const changeTracker = yield* service(ChangeTracker);
11666
- const runtimeModuleIdentifier = tsUtils.findImportedModuleIdentifierByPackageAndNameOrBarrel(sourceFile, "effect", "Runtime") || "Runtime";
11667
11952
  const effectModuleIdentifier = tsUtils.findImportedModuleIdentifierByPackageAndNameOrBarrel(sourceFile, "effect", "Effect") || "Effect";
11953
+ const runtimeModuleIdentifier = tsUtils.findImportedModuleIdentifierByPackageAndNameOrBarrel(sourceFile, "effect", "Runtime") || "Runtime";
11668
11954
  let runtimeIdentifier = void 0;
11955
+ let servicesIdentifier = void 0;
11669
11956
  for (const statement of effectGen.generatorFunction.body.statements) {
11670
11957
  if (ts.isVariableStatement(statement) && statement.declarationList.declarations.length === 1) {
11671
11958
  const declaration = statement.declarationList.declarations[0];
@@ -11679,11 +11966,46 @@ var runEffectInsideEffect = createDiagnostic({
11679
11966
  if (isSome2(maybeEffectRuntime) && ts.isIdentifier(declaration.name)) {
11680
11967
  runtimeIdentifier = ts.idText(declaration.name);
11681
11968
  }
11969
+ const maybeEffectServices = yield* pipe(
11970
+ typeParser.isNodeReferenceToEffectModuleApi("services")(yieldedExpression.expression),
11971
+ option
11972
+ );
11973
+ if (isSome2(maybeEffectServices) && ts.isIdentifier(declaration.name)) {
11974
+ servicesIdentifier = ts.idText(declaration.name);
11975
+ }
11682
11976
  }
11683
11977
  }
11684
11978
  }
11685
11979
  }
11686
- if (!runtimeIdentifier) {
11980
+ if (supportedEffect === "v4" && !servicesIdentifier) {
11981
+ changeTracker.insertNodeAt(
11982
+ sourceFile,
11983
+ effectGen.body.statements[0].pos,
11984
+ ts.factory.createVariableStatement(
11985
+ void 0,
11986
+ ts.factory.createVariableDeclarationList([ts.factory.createVariableDeclaration(
11987
+ "effectServices",
11988
+ void 0,
11989
+ void 0,
11990
+ ts.factory.createYieldExpression(
11991
+ ts.factory.createToken(ts.SyntaxKind.AsteriskToken),
11992
+ ts.factory.createCallExpression(
11993
+ ts.factory.createPropertyAccessExpression(
11994
+ ts.factory.createIdentifier(effectModuleIdentifier),
11995
+ "services"
11996
+ ),
11997
+ [ts.factory.createKeywordTypeNode(ts.SyntaxKind.NeverKeyword)],
11998
+ []
11999
+ )
12000
+ )
12001
+ )], ts.NodeFlags.Const)
12002
+ ),
12003
+ {
12004
+ prefix: "\n",
12005
+ suffix: "\n"
12006
+ }
12007
+ );
12008
+ } else if (supportedEffect === "v3" && !runtimeIdentifier) {
11687
12009
  changeTracker.insertNodeAt(
11688
12010
  sourceFile,
11689
12011
  effectGen.body.statements[0].pos,
@@ -11719,16 +12041,19 @@ var runEffectInsideEffect = createDiagnostic({
11719
12041
  changeTracker.insertText(
11720
12042
  sourceFile,
11721
12043
  node.arguments[0].pos,
11722
- `${runtimeModuleIdentifier}.${isEffectRunCall.value.methodName}(${runtimeIdentifier || "effectRuntime"}, `
12044
+ supportedEffect === "v4" ? `${effectModuleIdentifier}.${isEffectRunCall.value.methodName}With(${servicesIdentifier || "effectServices"})(` : `${runtimeModuleIdentifier}.${isEffectRunCall.value.methodName}(${runtimeIdentifier || "effectRuntime"}, `
11723
12045
  );
11724
12046
  });
12047
+ const v4MethodName = `${isEffectRunCall.value.methodName}With`;
12048
+ const messageText = supportedEffect === "v4" ? `Using ${nodeText} inside an Effect is not recommended. The same services should generally be used instead to run child effects.
12049
+ 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.
12050
+ Consider extracting the Runtime by using for example Effect.runtime and then use Runtime.${isEffectRunCall.value.methodName} with the extracted runtime instead.`;
11725
12051
  report({
11726
12052
  location: node.expression,
11727
- messageText: `Using ${nodeText} inside an Effect is not recommended. The same runtime should generally be used instead to run child effects.
11728
- Consider extracting the Runtime by using for example Effect.runtime and then use Runtime.${isEffectRunCall.value.methodName} with the extracted runtime instead.`,
12053
+ messageText,
11729
12054
  fixes: [{
11730
12055
  fixName: "runEffectInsideEffect_fix",
11731
- description: "Use a runtime to run the Effect",
12056
+ description: supportedEffect === "v4" ? "Use the current services to run the Effect" : "Use a runtime to run the Effect",
11732
12057
  apply: fixAddRuntime
11733
12058
  }]
11734
12059
  });
@@ -11874,9 +12199,8 @@ var schemaSyncInEffect = createDiagnostic({
11874
12199
  option
11875
12200
  );
11876
12201
  if (isNone2(isSchemaSyncCall)) continue;
11877
- const { effectGen, scopeNode } = yield* typeParser.findEnclosingScopes(node);
11878
- if (!effectGen || effectGen.body.statements.length === 0) continue;
11879
- if (scopeNode && scopeNode !== effectGen.generatorFunction) continue;
12202
+ const { inEffect } = yield* typeParser.findEnclosingScopes(node);
12203
+ if (!inEffect) continue;
11880
12204
  const nodeText = sourceFile.text.substring(
11881
12205
  ts.getTokenPosOfNode(node.expression, sourceFile),
11882
12206
  node.expression.end
@@ -12718,6 +13042,7 @@ var diagnostics = [
12718
13042
  unnecessaryPipe,
12719
13043
  genericEffectServices,
12720
13044
  globalFetch,
13045
+ globalFetchInEffect,
12721
13046
  returnEffectInGen,
12722
13047
  tryCatchInEffectGen,
12723
13048
  importFromBarrel,
@@ -12749,7 +13074,15 @@ var diagnostics = [
12749
13074
  preferSchemaOverJson,
12750
13075
  extendsNativeError,
12751
13076
  serviceNotAsClass,
12752
- nodeBuiltinImport
13077
+ nodeBuiltinImport,
13078
+ globalDate,
13079
+ globalDateInEffect,
13080
+ globalConsole,
13081
+ globalConsoleInEffect,
13082
+ globalRandom,
13083
+ globalRandomInEffect,
13084
+ globalTimers,
13085
+ globalTimersInEffect
12753
13086
  ];
12754
13087
 
12755
13088
  // src/completions/effectDiagnosticsComment.ts