@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/transform.js CHANGED
@@ -3122,6 +3122,25 @@ function makeTypeCheckerUtils(ts, typeChecker, tsUtils) {
3122
3122
  return typeChecker.getTypeAtLocation(node);
3123
3123
  }
3124
3124
  }
3125
+ function resolveToGlobalSymbol(symbol3) {
3126
+ if (symbol3.flags & ts.SymbolFlags.Alias) {
3127
+ symbol3 = typeChecker.getAliasedSymbol(symbol3);
3128
+ }
3129
+ let depth = 0;
3130
+ while (depth < 2 && symbol3.valueDeclaration && ts.isVariableDeclaration(symbol3.valueDeclaration)) {
3131
+ const initializer = symbol3.valueDeclaration.initializer;
3132
+ if (!initializer) break;
3133
+ let nextSymbol = typeChecker.getSymbolAtLocation(initializer);
3134
+ if (!nextSymbol) break;
3135
+ if (nextSymbol.flags & ts.SymbolFlags.Alias) {
3136
+ nextSymbol = typeChecker.getAliasedSymbol(nextSymbol);
3137
+ }
3138
+ if (nextSymbol === symbol3) break;
3139
+ symbol3 = nextSymbol;
3140
+ depth++;
3141
+ }
3142
+ return symbol3;
3143
+ }
3125
3144
  return {
3126
3145
  isUnion,
3127
3146
  isReadonlyArrayType,
@@ -3135,7 +3154,8 @@ function makeTypeCheckerUtils(ts, typeChecker, tsUtils) {
3135
3154
  expectedAndRealType,
3136
3155
  typeToSimplifiedTypeNode,
3137
3156
  isGlobalErrorType,
3138
- getTypeAtLocation
3157
+ getTypeAtLocation,
3158
+ resolveToGlobalSymbol
3139
3159
  };
3140
3160
  }
3141
3161
 
@@ -3823,7 +3843,11 @@ function make2(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
3823
3843
  }
3824
3844
  currentParent = nodeToCheck.parent;
3825
3845
  }
3826
- return { scopeNode, effectGen: effectGenResult };
3846
+ return {
3847
+ inEffect: effectGenResult !== void 0 && effectGenResult.body.statements.length > 0 && scopeNode === effectGenResult.generatorFunction,
3848
+ scopeNode,
3849
+ effectGen: effectGenResult
3850
+ };
3827
3851
  });
3828
3852
  const effectFn = cachedBy(
3829
3853
  function(node) {
@@ -5909,12 +5933,14 @@ var getParameterName = (typescript, name) => {
5909
5933
  }
5910
5934
  return "parameter";
5911
5935
  };
5912
- var hasOuterContextualFunctionType = (typescript, typeChecker, node) => {
5936
+ var hasOuterContextualFunctionType = (typescript, typeChecker, typeCheckerUtils, node) => {
5913
5937
  const contextualType = typeChecker.getContextualType(node);
5914
5938
  if (!contextualType) {
5915
5939
  return false;
5916
5940
  }
5917
- return typeChecker.getSignaturesOfType(contextualType, typescript.SignatureKind.Call).length > 0;
5941
+ return typeCheckerUtils.unrollUnionMembers(contextualType).some(
5942
+ (type) => typeChecker.getSignaturesOfType(type, typescript.SignatureKind.Call).length > 0
5943
+ );
5918
5944
  };
5919
5945
  var effectFnImplicitAny = createDiagnostic({
5920
5946
  name: "effectFnImplicitAny",
@@ -5928,6 +5954,7 @@ var effectFnImplicitAny = createDiagnostic({
5928
5954
  const ts = yield* service(TypeScriptApi);
5929
5955
  const program = yield* service(TypeScriptProgram);
5930
5956
  const typeChecker = yield* service(TypeCheckerApi);
5957
+ const typeCheckerUtils = yield* service(TypeCheckerUtils);
5931
5958
  const typeParser = yield* service(TypeParser);
5932
5959
  const noImplicitAny = program.getCompilerOptions().noImplicitAny ?? program.getCompilerOptions().strict ?? false;
5933
5960
  if (!noImplicitAny) {
@@ -5967,7 +5994,7 @@ var effectFnImplicitAny = createDiagnostic({
5967
5994
  ),
5968
5995
  orUndefined
5969
5996
  );
5970
- if (!parsed || hasOuterContextualFunctionType(ts, typeChecker, parsed.call)) {
5997
+ if (!parsed || hasOuterContextualFunctionType(ts, typeChecker, typeCheckerUtils, parsed.call)) {
5971
5998
  continue;
5972
5999
  }
5973
6000
  for (const parameter of parsed.fn.parameters) {
@@ -7018,6 +7045,128 @@ var genericEffectServices = createDiagnostic({
7018
7045
  })
7019
7046
  });
7020
7047
 
7048
+ // src/diagnostics/globalConsoleInEffect.ts
7049
+ var consoleMethodAlternatives = {
7050
+ "log": "Effect.log or Logger",
7051
+ "warn": "Effect.logWarning or Logger",
7052
+ "error": "Effect.logError or Logger",
7053
+ "info": "Effect.logInfo or Logger",
7054
+ "debug": "Effect.logDebug or Logger",
7055
+ "trace": "Effect.logTrace or Logger"
7056
+ };
7057
+ var makeGlobalConsoleApply = (checkInEffect) => fn(`globalConsole${checkInEffect ? "InEffect" : ""}.apply`)(function* (sourceFile, report) {
7058
+ const ts = yield* service(TypeScriptApi);
7059
+ const typeChecker = yield* service(TypeCheckerApi);
7060
+ const typeCheckerUtils = yield* service(TypeCheckerUtils);
7061
+ const typeParser = yield* service(TypeParser);
7062
+ const consoleSymbol = typeChecker.resolveName("console", void 0, ts.SymbolFlags.Value, false);
7063
+ if (!consoleSymbol) return;
7064
+ const nodeToVisit = [];
7065
+ const appendNodeToVisit = (node) => {
7066
+ nodeToVisit.push(node);
7067
+ return void 0;
7068
+ };
7069
+ ts.forEachChild(sourceFile, appendNodeToVisit);
7070
+ while (nodeToVisit.length > 0) {
7071
+ const node = nodeToVisit.shift();
7072
+ ts.forEachChild(node, appendNodeToVisit);
7073
+ if (!ts.isCallExpression(node) || !ts.isPropertyAccessExpression(node.expression)) continue;
7074
+ const method = ts.idText(node.expression.name);
7075
+ const alternative = consoleMethodAlternatives[method];
7076
+ if (!alternative) continue;
7077
+ const symbol3 = typeChecker.getSymbolAtLocation(node.expression.expression);
7078
+ if (!symbol3) continue;
7079
+ if (typeCheckerUtils.resolveToGlobalSymbol(symbol3) !== consoleSymbol) continue;
7080
+ const { inEffect } = yield* typeParser.findEnclosingScopes(node);
7081
+ if (inEffect !== checkInEffect) continue;
7082
+ report({
7083
+ location: node,
7084
+ messageText: checkInEffect ? `Prefer using ${alternative} instead of console.${method} inside Effect generators.` : `Prefer using ${alternative} instead of console.${method}.`,
7085
+ fixes: []
7086
+ });
7087
+ }
7088
+ });
7089
+ var globalConsoleInEffect = createDiagnostic({
7090
+ name: "globalConsoleInEffect",
7091
+ code: 56,
7092
+ description: "Warns when using console methods inside Effect generators instead of Effect.log/Logger",
7093
+ group: "effectNative",
7094
+ severity: "off",
7095
+ fixable: false,
7096
+ supportedEffect: ["v3", "v4"],
7097
+ apply: makeGlobalConsoleApply(true)
7098
+ });
7099
+
7100
+ // src/diagnostics/globalConsole.ts
7101
+ var globalConsole = createDiagnostic({
7102
+ name: "globalConsole",
7103
+ code: 60,
7104
+ description: "Warns when using console methods outside Effect generators instead of Effect.log/Logger",
7105
+ group: "effectNative",
7106
+ severity: "off",
7107
+ fixable: false,
7108
+ supportedEffect: ["v3", "v4"],
7109
+ apply: makeGlobalConsoleApply(false)
7110
+ });
7111
+
7112
+ // src/diagnostics/globalDateInEffect.ts
7113
+ var makeGlobalDateApply = (checkInEffect) => fn(`globalDate${checkInEffect ? "InEffect" : ""}.apply`)(function* (sourceFile, report) {
7114
+ const ts = yield* service(TypeScriptApi);
7115
+ const typeChecker = yield* service(TypeCheckerApi);
7116
+ const typeCheckerUtils = yield* service(TypeCheckerUtils);
7117
+ const typeParser = yield* service(TypeParser);
7118
+ const dateSymbol = typeChecker.resolveName("Date", void 0, ts.SymbolFlags.Value, false);
7119
+ if (!dateSymbol) return;
7120
+ const nodeToVisit = [];
7121
+ const appendNodeToVisit = (node) => {
7122
+ nodeToVisit.push(node);
7123
+ return void 0;
7124
+ };
7125
+ ts.forEachChild(sourceFile, appendNodeToVisit);
7126
+ while (nodeToVisit.length > 0) {
7127
+ const node = nodeToVisit.shift();
7128
+ ts.forEachChild(node, appendNodeToVisit);
7129
+ let messageText;
7130
+ let objectNode;
7131
+ if (ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression) && ts.idText(node.expression.name) === "now") {
7132
+ objectNode = node.expression.expression;
7133
+ 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().";
7134
+ } else if (ts.isNewExpression(node)) {
7135
+ objectNode = node.expression;
7136
+ messageText = checkInEffect ? "Prefer using DateTime from Effect instead of new Date() inside Effect generators." : "Prefer using DateTime from Effect instead of new Date().";
7137
+ }
7138
+ if (!messageText || !objectNode) continue;
7139
+ const symbol3 = typeChecker.getSymbolAtLocation(objectNode);
7140
+ if (!symbol3) continue;
7141
+ if (typeCheckerUtils.resolveToGlobalSymbol(symbol3) !== dateSymbol) continue;
7142
+ const { inEffect } = yield* typeParser.findEnclosingScopes(node);
7143
+ if (inEffect !== checkInEffect) continue;
7144
+ report({ location: node, messageText, fixes: [] });
7145
+ }
7146
+ });
7147
+ var globalDateInEffect = createDiagnostic({
7148
+ name: "globalDateInEffect",
7149
+ code: 55,
7150
+ description: "Warns when using Date.now() or new Date() inside Effect generators instead of Clock/DateTime",
7151
+ group: "effectNative",
7152
+ severity: "off",
7153
+ fixable: false,
7154
+ supportedEffect: ["v3", "v4"],
7155
+ apply: makeGlobalDateApply(true)
7156
+ });
7157
+
7158
+ // src/diagnostics/globalDate.ts
7159
+ var globalDate = createDiagnostic({
7160
+ name: "globalDate",
7161
+ code: 59,
7162
+ description: "Warns when using Date.now() or new Date() outside Effect generators instead of Clock/DateTime",
7163
+ group: "effectNative",
7164
+ severity: "off",
7165
+ fixable: false,
7166
+ supportedEffect: ["v3", "v4"],
7167
+ apply: makeGlobalDateApply(false)
7168
+ });
7169
+
7021
7170
  // src/diagnostics/globalErrorInEffectCatch.ts
7022
7171
  var globalErrorInEffectCatch = createDiagnostic({
7023
7172
  name: "globalErrorInEffectCatch",
@@ -7137,46 +7286,186 @@ var globalErrorInEffectFailure = createDiagnostic({
7137
7286
  })
7138
7287
  });
7139
7288
 
7289
+ // src/diagnostics/globalFetchInEffect.ts
7290
+ var makeGlobalFetchApply = (checkInEffect) => fn(`globalFetch${checkInEffect ? "InEffect" : ""}.apply`)(function* (sourceFile, report) {
7291
+ const ts = yield* service(TypeScriptApi);
7292
+ const typeChecker = yield* service(TypeCheckerApi);
7293
+ const typeCheckerUtils = yield* service(TypeCheckerUtils);
7294
+ const typeParser = yield* service(TypeParser);
7295
+ const fetchSymbol = typeChecker.resolveName("fetch", void 0, ts.SymbolFlags.Value, false);
7296
+ if (!fetchSymbol) return;
7297
+ const effectVersion = typeParser.supportedEffect();
7298
+ const packageName = effectVersion === "v3" ? "@effect/platform" : "effect/unstable/http";
7299
+ 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.`;
7300
+ const nodeToVisit = [];
7301
+ const appendNodeToVisit = (node) => {
7302
+ nodeToVisit.push(node);
7303
+ return void 0;
7304
+ };
7305
+ ts.forEachChild(sourceFile, appendNodeToVisit);
7306
+ while (nodeToVisit.length > 0) {
7307
+ const node = nodeToVisit.shift();
7308
+ if (ts.isCallExpression(node)) {
7309
+ const symbol3 = typeChecker.getSymbolAtLocation(node.expression);
7310
+ if (symbol3 && typeCheckerUtils.resolveToGlobalSymbol(symbol3) === fetchSymbol) {
7311
+ const { inEffect } = yield* typeParser.findEnclosingScopes(node);
7312
+ if (inEffect === checkInEffect) {
7313
+ report({
7314
+ location: node.expression,
7315
+ messageText,
7316
+ fixes: []
7317
+ });
7318
+ }
7319
+ }
7320
+ }
7321
+ ts.forEachChild(node, appendNodeToVisit);
7322
+ }
7323
+ });
7324
+ var globalFetchInEffect = createDiagnostic({
7325
+ name: "globalFetchInEffect",
7326
+ code: 63,
7327
+ description: "Warns when using the global fetch function inside Effect generators instead of the Effect HTTP client",
7328
+ group: "effectNative",
7329
+ severity: "off",
7330
+ fixable: false,
7331
+ supportedEffect: ["v3", "v4"],
7332
+ apply: makeGlobalFetchApply(true)
7333
+ });
7334
+
7140
7335
  // src/diagnostics/globalFetch.ts
7141
7336
  var globalFetch = createDiagnostic({
7142
7337
  name: "globalFetch",
7143
7338
  code: 53,
7144
- description: "Warns when using the global fetch function instead of the Effect HTTP client",
7339
+ description: "Warns when using the global fetch function outside Effect generators instead of the Effect HTTP client",
7145
7340
  group: "effectNative",
7146
7341
  severity: "off",
7147
7342
  fixable: false,
7148
7343
  supportedEffect: ["v3", "v4"],
7149
- apply: fn("globalFetch.apply")(function* (sourceFile, report) {
7150
- const ts = yield* service(TypeScriptApi);
7151
- const typeChecker = yield* service(TypeCheckerApi);
7152
- const typeParser = yield* service(TypeParser);
7153
- const fetchSymbol = typeChecker.resolveName("fetch", void 0, ts.SymbolFlags.Value, false);
7154
- if (!fetchSymbol) return;
7155
- const effectVersion = typeParser.supportedEffect();
7156
- const packageName = effectVersion === "v3" ? "@effect/platform" : "effect/unstable/http";
7157
- const messageText = `Prefer using HttpClient from ${packageName} instead of the global 'fetch' function.`;
7158
- const nodeToVisit = [];
7159
- const appendNodeToVisit = (node) => {
7160
- nodeToVisit.push(node);
7161
- return void 0;
7162
- };
7163
- ts.forEachChild(sourceFile, appendNodeToVisit);
7164
- while (nodeToVisit.length > 0) {
7165
- const node = nodeToVisit.shift();
7166
- if (ts.isCallExpression(node)) {
7167
- const symbol3 = typeChecker.getSymbolAtLocation(node.expression);
7168
- const resolvedSymbol = symbol3 && symbol3.flags & ts.SymbolFlags.Alias ? typeChecker.getAliasedSymbol(symbol3) : symbol3;
7169
- if (resolvedSymbol === fetchSymbol) {
7170
- report({
7171
- location: node.expression,
7172
- messageText,
7173
- fixes: []
7174
- });
7175
- }
7344
+ apply: makeGlobalFetchApply(false)
7345
+ });
7346
+
7347
+ // src/diagnostics/globalRandomInEffect.ts
7348
+ var makeGlobalRandomApply = (checkInEffect) => fn(`globalRandom${checkInEffect ? "InEffect" : ""}.apply`)(function* (sourceFile, report) {
7349
+ const ts = yield* service(TypeScriptApi);
7350
+ const typeChecker = yield* service(TypeCheckerApi);
7351
+ const typeCheckerUtils = yield* service(TypeCheckerUtils);
7352
+ const typeParser = yield* service(TypeParser);
7353
+ const mathSymbol = typeChecker.resolveName("Math", void 0, ts.SymbolFlags.Value, false);
7354
+ if (!mathSymbol) return;
7355
+ const nodeToVisit = [];
7356
+ const appendNodeToVisit = (node) => {
7357
+ nodeToVisit.push(node);
7358
+ return void 0;
7359
+ };
7360
+ ts.forEachChild(sourceFile, appendNodeToVisit);
7361
+ while (nodeToVisit.length > 0) {
7362
+ const node = nodeToVisit.shift();
7363
+ ts.forEachChild(node, appendNodeToVisit);
7364
+ if (!ts.isCallExpression(node) || !ts.isPropertyAccessExpression(node.expression) || ts.idText(node.expression.name) !== "random") continue;
7365
+ const symbol3 = typeChecker.getSymbolAtLocation(node.expression.expression);
7366
+ if (!symbol3) continue;
7367
+ if (typeCheckerUtils.resolveToGlobalSymbol(symbol3) !== mathSymbol) continue;
7368
+ const { inEffect } = yield* typeParser.findEnclosingScopes(node);
7369
+ if (inEffect !== checkInEffect) continue;
7370
+ report({
7371
+ location: node,
7372
+ 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().",
7373
+ fixes: []
7374
+ });
7375
+ }
7376
+ });
7377
+ var globalRandomInEffect = createDiagnostic({
7378
+ name: "globalRandomInEffect",
7379
+ code: 57,
7380
+ description: "Warns when using Math.random() inside Effect generators instead of the Random service",
7381
+ group: "effectNative",
7382
+ severity: "off",
7383
+ fixable: false,
7384
+ supportedEffect: ["v3", "v4"],
7385
+ apply: makeGlobalRandomApply(true)
7386
+ });
7387
+
7388
+ // src/diagnostics/globalRandom.ts
7389
+ var globalRandom = createDiagnostic({
7390
+ name: "globalRandom",
7391
+ code: 61,
7392
+ description: "Warns when using Math.random() outside Effect generators instead of the Random service",
7393
+ group: "effectNative",
7394
+ severity: "off",
7395
+ fixable: false,
7396
+ supportedEffect: ["v3", "v4"],
7397
+ apply: makeGlobalRandomApply(false)
7398
+ });
7399
+
7400
+ // src/diagnostics/globalTimersInEffect.ts
7401
+ var timerAlternatives = {
7402
+ "setTimeout": {
7403
+ inEffect: "Prefer using Effect.sleep or Schedule from Effect instead of setTimeout inside Effect generators.",
7404
+ outsideEffect: "Prefer using Effect.sleep or Schedule from Effect instead of setTimeout."
7405
+ },
7406
+ "setInterval": {
7407
+ inEffect: "Prefer using Schedule or Effect.repeat from Effect instead of setInterval inside Effect generators.",
7408
+ outsideEffect: "Prefer using Schedule or Effect.repeat from Effect instead of setInterval."
7409
+ }
7410
+ };
7411
+ var makeGlobalTimersApply = (checkInEffect) => fn(`globalTimers${checkInEffect ? "InEffect" : ""}.apply`)(function* (sourceFile, report) {
7412
+ const ts = yield* service(TypeScriptApi);
7413
+ const typeChecker = yield* service(TypeCheckerApi);
7414
+ const typeCheckerUtils = yield* service(TypeCheckerUtils);
7415
+ const typeParser = yield* service(TypeParser);
7416
+ const globalSymbols = /* @__PURE__ */ new Map();
7417
+ for (const name of Object.keys(timerAlternatives)) {
7418
+ const symbol3 = typeChecker.resolveName(name, void 0, ts.SymbolFlags.Value, false);
7419
+ if (symbol3) globalSymbols.set(name, symbol3);
7420
+ }
7421
+ if (globalSymbols.size === 0) return;
7422
+ const nodeToVisit = [];
7423
+ const appendNodeToVisit = (node) => {
7424
+ nodeToVisit.push(node);
7425
+ return void 0;
7426
+ };
7427
+ ts.forEachChild(sourceFile, appendNodeToVisit);
7428
+ while (nodeToVisit.length > 0) {
7429
+ const node = nodeToVisit.shift();
7430
+ ts.forEachChild(node, appendNodeToVisit);
7431
+ if (!ts.isCallExpression(node)) continue;
7432
+ const symbol3 = typeChecker.getSymbolAtLocation(node.expression);
7433
+ if (!symbol3) continue;
7434
+ const resolvedSymbol = typeCheckerUtils.resolveToGlobalSymbol(symbol3);
7435
+ let messageText;
7436
+ for (const [name, symbol4] of globalSymbols) {
7437
+ if (resolvedSymbol === symbol4) {
7438
+ messageText = checkInEffect ? timerAlternatives[name].inEffect : timerAlternatives[name].outsideEffect;
7439
+ break;
7176
7440
  }
7177
- ts.forEachChild(node, appendNodeToVisit);
7178
7441
  }
7179
- })
7442
+ if (!messageText) continue;
7443
+ const { inEffect } = yield* typeParser.findEnclosingScopes(node);
7444
+ if (inEffect !== checkInEffect) continue;
7445
+ report({ location: node, messageText, fixes: [] });
7446
+ }
7447
+ });
7448
+ var globalTimersInEffect = createDiagnostic({
7449
+ name: "globalTimersInEffect",
7450
+ code: 58,
7451
+ description: "Warns when using setTimeout/setInterval inside Effect generators instead of Effect.sleep/Schedule",
7452
+ group: "effectNative",
7453
+ severity: "off",
7454
+ fixable: false,
7455
+ supportedEffect: ["v3", "v4"],
7456
+ apply: makeGlobalTimersApply(true)
7457
+ });
7458
+
7459
+ // src/diagnostics/globalTimers.ts
7460
+ var globalTimers = createDiagnostic({
7461
+ name: "globalTimers",
7462
+ code: 62,
7463
+ description: "Warns when using setTimeout/setInterval outside Effect generators instead of Effect.sleep/Schedule",
7464
+ group: "effectNative",
7465
+ severity: "off",
7466
+ fixable: false,
7467
+ supportedEffect: ["v3", "v4"],
7468
+ apply: makeGlobalTimersApply(false)
7180
7469
  });
7181
7470
 
7182
7471
  // src/diagnostics/importFromBarrel.ts
@@ -8241,8 +8530,8 @@ var missingReturnYieldStar = createDiagnostic({
8241
8530
  if (!type) continue;
8242
8531
  const maybeEffect = yield* option(typeParser.effectYieldableType(type, unwrapped.expression));
8243
8532
  if (!(isSome2(maybeEffect) && maybeEffect.value.A.flags & ts.TypeFlags.Never)) continue;
8244
- const { effectGen, scopeNode } = yield* typeParser.findEnclosingScopes(node);
8245
- if (!effectGen || scopeNode && scopeNode !== effectGen.generatorFunction) continue;
8533
+ const { inEffect } = yield* typeParser.findEnclosingScopes(node);
8534
+ if (!inEffect) continue;
8246
8535
  const fix = [{
8247
8536
  fixName: "missingReturnYieldStar_fix",
8248
8537
  description: "Add return statement",
@@ -10705,11 +10994,8 @@ var preferSchemaOverJson = createDiagnostic({
10705
10994
  });
10706
10995
  const jsonMethodInEffectGen = fn("preferSchemaOverJson.jsonMethodInEffectGen")(
10707
10996
  function* (jsonCall) {
10708
- const { effectGen, scopeNode } = yield* typeParser.findEnclosingScopes(jsonCall);
10709
- if (!effectGen || effectGen.body.statements.length === 0) {
10710
- return yield* TypeParserIssue.issue;
10711
- }
10712
- if (scopeNode && scopeNode !== effectGen.generatorFunction) {
10997
+ const { inEffect } = yield* typeParser.findEnclosingScopes(jsonCall);
10998
+ if (!inEffect) {
10713
10999
  return yield* TypeParserIssue.issue;
10714
11000
  }
10715
11001
  return jsonCall;
@@ -10872,16 +11158,16 @@ Nested Effect-able types may be intended if you plan to later manually flatten o
10872
11158
  var runEffectInsideEffect = createDiagnostic({
10873
11159
  name: "runEffectInsideEffect",
10874
11160
  code: 32,
10875
- description: "Suggests using Runtime methods instead of Effect.run* inside Effect contexts",
11161
+ description: "Suggests using Runtime or Effect.run*With methods instead of Effect.run* inside Effect contexts",
10876
11162
  group: "antipattern",
10877
11163
  severity: "suggestion",
10878
11164
  fixable: true,
10879
- supportedEffect: ["v3"],
11165
+ supportedEffect: ["v3", "v4"],
10880
11166
  apply: fn("runEffectInsideEffect.apply")(function* (sourceFile, report) {
10881
11167
  const ts = yield* service(TypeScriptApi);
10882
11168
  const typeParser = yield* service(TypeParser);
10883
11169
  const tsUtils = yield* service(TypeScriptUtils);
10884
- if (typeParser.supportedEffect() === "v4") return;
11170
+ const supportedEffect = typeParser.supportedEffect();
10885
11171
  const parseEffectMethod = (node, methodName) => pipe(
10886
11172
  typeParser.isNodeReferenceToEffectModuleApi(methodName)(node),
10887
11173
  map4(() => ({ node, methodName }))
@@ -10914,9 +11200,10 @@ var runEffectInsideEffect = createDiagnostic({
10914
11200
  if (scopeNode && scopeNode !== effectGen.generatorFunction) {
10915
11201
  const fixAddRuntime = gen(function* () {
10916
11202
  const changeTracker = yield* service(ChangeTracker);
10917
- const runtimeModuleIdentifier = tsUtils.findImportedModuleIdentifierByPackageAndNameOrBarrel(sourceFile, "effect", "Runtime") || "Runtime";
10918
11203
  const effectModuleIdentifier = tsUtils.findImportedModuleIdentifierByPackageAndNameOrBarrel(sourceFile, "effect", "Effect") || "Effect";
11204
+ const runtimeModuleIdentifier = tsUtils.findImportedModuleIdentifierByPackageAndNameOrBarrel(sourceFile, "effect", "Runtime") || "Runtime";
10919
11205
  let runtimeIdentifier = void 0;
11206
+ let servicesIdentifier = void 0;
10920
11207
  for (const statement of effectGen.generatorFunction.body.statements) {
10921
11208
  if (ts.isVariableStatement(statement) && statement.declarationList.declarations.length === 1) {
10922
11209
  const declaration = statement.declarationList.declarations[0];
@@ -10930,11 +11217,46 @@ var runEffectInsideEffect = createDiagnostic({
10930
11217
  if (isSome2(maybeEffectRuntime) && ts.isIdentifier(declaration.name)) {
10931
11218
  runtimeIdentifier = ts.idText(declaration.name);
10932
11219
  }
11220
+ const maybeEffectServices = yield* pipe(
11221
+ typeParser.isNodeReferenceToEffectModuleApi("services")(yieldedExpression.expression),
11222
+ option
11223
+ );
11224
+ if (isSome2(maybeEffectServices) && ts.isIdentifier(declaration.name)) {
11225
+ servicesIdentifier = ts.idText(declaration.name);
11226
+ }
10933
11227
  }
10934
11228
  }
10935
11229
  }
10936
11230
  }
10937
- if (!runtimeIdentifier) {
11231
+ if (supportedEffect === "v4" && !servicesIdentifier) {
11232
+ changeTracker.insertNodeAt(
11233
+ sourceFile,
11234
+ effectGen.body.statements[0].pos,
11235
+ ts.factory.createVariableStatement(
11236
+ void 0,
11237
+ ts.factory.createVariableDeclarationList([ts.factory.createVariableDeclaration(
11238
+ "effectServices",
11239
+ void 0,
11240
+ void 0,
11241
+ ts.factory.createYieldExpression(
11242
+ ts.factory.createToken(ts.SyntaxKind.AsteriskToken),
11243
+ ts.factory.createCallExpression(
11244
+ ts.factory.createPropertyAccessExpression(
11245
+ ts.factory.createIdentifier(effectModuleIdentifier),
11246
+ "services"
11247
+ ),
11248
+ [ts.factory.createKeywordTypeNode(ts.SyntaxKind.NeverKeyword)],
11249
+ []
11250
+ )
11251
+ )
11252
+ )], ts.NodeFlags.Const)
11253
+ ),
11254
+ {
11255
+ prefix: "\n",
11256
+ suffix: "\n"
11257
+ }
11258
+ );
11259
+ } else if (supportedEffect === "v3" && !runtimeIdentifier) {
10938
11260
  changeTracker.insertNodeAt(
10939
11261
  sourceFile,
10940
11262
  effectGen.body.statements[0].pos,
@@ -10970,16 +11292,19 @@ var runEffectInsideEffect = createDiagnostic({
10970
11292
  changeTracker.insertText(
10971
11293
  sourceFile,
10972
11294
  node.arguments[0].pos,
10973
- `${runtimeModuleIdentifier}.${isEffectRunCall.value.methodName}(${runtimeIdentifier || "effectRuntime"}, `
11295
+ supportedEffect === "v4" ? `${effectModuleIdentifier}.${isEffectRunCall.value.methodName}With(${servicesIdentifier || "effectServices"})(` : `${runtimeModuleIdentifier}.${isEffectRunCall.value.methodName}(${runtimeIdentifier || "effectRuntime"}, `
10974
11296
  );
10975
11297
  });
11298
+ const v4MethodName = `${isEffectRunCall.value.methodName}With`;
11299
+ const messageText = supportedEffect === "v4" ? `Using ${nodeText} inside an Effect is not recommended. The same services should generally be used instead to run child effects.
11300
+ 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.
11301
+ Consider extracting the Runtime by using for example Effect.runtime and then use Runtime.${isEffectRunCall.value.methodName} with the extracted runtime instead.`;
10976
11302
  report({
10977
11303
  location: node.expression,
10978
- messageText: `Using ${nodeText} inside an Effect is not recommended. The same runtime should generally be used instead to run child effects.
10979
- Consider extracting the Runtime by using for example Effect.runtime and then use Runtime.${isEffectRunCall.value.methodName} with the extracted runtime instead.`,
11304
+ messageText,
10980
11305
  fixes: [{
10981
11306
  fixName: "runEffectInsideEffect_fix",
10982
- description: "Use a runtime to run the Effect",
11307
+ description: supportedEffect === "v4" ? "Use the current services to run the Effect" : "Use a runtime to run the Effect",
10983
11308
  apply: fixAddRuntime
10984
11309
  }]
10985
11310
  });
@@ -11125,9 +11450,8 @@ var schemaSyncInEffect = createDiagnostic({
11125
11450
  option
11126
11451
  );
11127
11452
  if (isNone2(isSchemaSyncCall)) continue;
11128
- const { effectGen, scopeNode } = yield* typeParser.findEnclosingScopes(node);
11129
- if (!effectGen || effectGen.body.statements.length === 0) continue;
11130
- if (scopeNode && scopeNode !== effectGen.generatorFunction) continue;
11453
+ const { inEffect } = yield* typeParser.findEnclosingScopes(node);
11454
+ if (!inEffect) continue;
11131
11455
  const nodeText = sourceFile.text.substring(
11132
11456
  ts.getTokenPosOfNode(node.expression, sourceFile),
11133
11457
  node.expression.end
@@ -11969,6 +12293,7 @@ var diagnostics = [
11969
12293
  unnecessaryPipe,
11970
12294
  genericEffectServices,
11971
12295
  globalFetch,
12296
+ globalFetchInEffect,
11972
12297
  returnEffectInGen,
11973
12298
  tryCatchInEffectGen,
11974
12299
  importFromBarrel,
@@ -12000,7 +12325,15 @@ var diagnostics = [
12000
12325
  preferSchemaOverJson,
12001
12326
  extendsNativeError,
12002
12327
  serviceNotAsClass,
12003
- nodeBuiltinImport
12328
+ nodeBuiltinImport,
12329
+ globalDate,
12330
+ globalDateInEffect,
12331
+ globalConsole,
12332
+ globalConsoleInEffect,
12333
+ globalRandom,
12334
+ globalRandomInEffect,
12335
+ globalTimers,
12336
+ globalTimersInEffect
12004
12337
  ];
12005
12338
 
12006
12339
  // src/transform.ts