@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/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.83.1",
25945
+ version: "0.84.1",
25946
25946
  publishConfig: {
25947
25947
  access: "public",
25948
25948
  directory: "dist"
@@ -28669,6 +28669,25 @@ function makeTypeCheckerUtils(ts, typeChecker, tsUtils) {
28669
28669
  return typeChecker.getTypeAtLocation(node);
28670
28670
  }
28671
28671
  }
28672
+ function resolveToGlobalSymbol(symbol4) {
28673
+ if (symbol4.flags & ts.SymbolFlags.Alias) {
28674
+ symbol4 = typeChecker.getAliasedSymbol(symbol4);
28675
+ }
28676
+ let depth = 0;
28677
+ while (depth < 2 && symbol4.valueDeclaration && ts.isVariableDeclaration(symbol4.valueDeclaration)) {
28678
+ const initializer = symbol4.valueDeclaration.initializer;
28679
+ if (!initializer) break;
28680
+ let nextSymbol = typeChecker.getSymbolAtLocation(initializer);
28681
+ if (!nextSymbol) break;
28682
+ if (nextSymbol.flags & ts.SymbolFlags.Alias) {
28683
+ nextSymbol = typeChecker.getAliasedSymbol(nextSymbol);
28684
+ }
28685
+ if (nextSymbol === symbol4) break;
28686
+ symbol4 = nextSymbol;
28687
+ depth++;
28688
+ }
28689
+ return symbol4;
28690
+ }
28672
28691
  return {
28673
28692
  isUnion: isUnion2,
28674
28693
  isReadonlyArrayType,
@@ -28682,7 +28701,8 @@ function makeTypeCheckerUtils(ts, typeChecker, tsUtils) {
28682
28701
  expectedAndRealType,
28683
28702
  typeToSimplifiedTypeNode,
28684
28703
  isGlobalErrorType,
28685
- getTypeAtLocation
28704
+ getTypeAtLocation,
28705
+ resolveToGlobalSymbol
28686
28706
  };
28687
28707
  }
28688
28708
 
@@ -29370,7 +29390,11 @@ function make26(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
29370
29390
  }
29371
29391
  currentParent = nodeToCheck.parent;
29372
29392
  }
29373
- return { scopeNode, effectGen: effectGenResult };
29393
+ return {
29394
+ inEffect: effectGenResult !== void 0 && effectGenResult.body.statements.length > 0 && scopeNode === effectGenResult.generatorFunction,
29395
+ scopeNode,
29396
+ effectGen: effectGenResult
29397
+ };
29374
29398
  });
29375
29399
  const effectFn = cachedBy(
29376
29400
  function(node) {
@@ -33911,12 +33935,14 @@ var getParameterName = (typescript, name) => {
33911
33935
  }
33912
33936
  return "parameter";
33913
33937
  };
33914
- var hasOuterContextualFunctionType = (typescript, typeChecker, node) => {
33938
+ var hasOuterContextualFunctionType = (typescript, typeChecker, typeCheckerUtils, node) => {
33915
33939
  const contextualType = typeChecker.getContextualType(node);
33916
33940
  if (!contextualType) {
33917
33941
  return false;
33918
33942
  }
33919
- return typeChecker.getSignaturesOfType(contextualType, typescript.SignatureKind.Call).length > 0;
33943
+ return typeCheckerUtils.unrollUnionMembers(contextualType).some(
33944
+ (type) => typeChecker.getSignaturesOfType(type, typescript.SignatureKind.Call).length > 0
33945
+ );
33920
33946
  };
33921
33947
  var effectFnImplicitAny = createDiagnostic({
33922
33948
  name: "effectFnImplicitAny",
@@ -33930,6 +33956,7 @@ var effectFnImplicitAny = createDiagnostic({
33930
33956
  const ts = yield* service2(TypeScriptApi);
33931
33957
  const program = yield* service2(TypeScriptProgram);
33932
33958
  const typeChecker = yield* service2(TypeCheckerApi);
33959
+ const typeCheckerUtils = yield* service2(TypeCheckerUtils);
33933
33960
  const typeParser = yield* service2(TypeParser);
33934
33961
  const noImplicitAny = program.getCompilerOptions().noImplicitAny ?? program.getCompilerOptions().strict ?? false;
33935
33962
  if (!noImplicitAny) {
@@ -33969,7 +33996,7 @@ var effectFnImplicitAny = createDiagnostic({
33969
33996
  ),
33970
33997
  orUndefined
33971
33998
  );
33972
- if (!parsed || hasOuterContextualFunctionType(ts, typeChecker, parsed.call)) {
33999
+ if (!parsed || hasOuterContextualFunctionType(ts, typeChecker, typeCheckerUtils, parsed.call)) {
33973
34000
  continue;
33974
34001
  }
33975
34002
  for (const parameter of parsed.fn.parameters) {
@@ -35020,6 +35047,128 @@ var genericEffectServices = createDiagnostic({
35020
35047
  })
35021
35048
  });
35022
35049
 
35050
+ // src/diagnostics/globalConsoleInEffect.ts
35051
+ var consoleMethodAlternatives = {
35052
+ "log": "Effect.log or Logger",
35053
+ "warn": "Effect.logWarning or Logger",
35054
+ "error": "Effect.logError or Logger",
35055
+ "info": "Effect.logInfo or Logger",
35056
+ "debug": "Effect.logDebug or Logger",
35057
+ "trace": "Effect.logTrace or Logger"
35058
+ };
35059
+ var makeGlobalConsoleApply = (checkInEffect) => fn3(`globalConsole${checkInEffect ? "InEffect" : ""}.apply`)(function* (sourceFile, report) {
35060
+ const ts = yield* service2(TypeScriptApi);
35061
+ const typeChecker = yield* service2(TypeCheckerApi);
35062
+ const typeCheckerUtils = yield* service2(TypeCheckerUtils);
35063
+ const typeParser = yield* service2(TypeParser);
35064
+ const consoleSymbol = typeChecker.resolveName("console", void 0, ts.SymbolFlags.Value, false);
35065
+ if (!consoleSymbol) return;
35066
+ const nodeToVisit = [];
35067
+ const appendNodeToVisit = (node) => {
35068
+ nodeToVisit.push(node);
35069
+ return void 0;
35070
+ };
35071
+ ts.forEachChild(sourceFile, appendNodeToVisit);
35072
+ while (nodeToVisit.length > 0) {
35073
+ const node = nodeToVisit.shift();
35074
+ ts.forEachChild(node, appendNodeToVisit);
35075
+ if (!ts.isCallExpression(node) || !ts.isPropertyAccessExpression(node.expression)) continue;
35076
+ const method = ts.idText(node.expression.name);
35077
+ const alternative = consoleMethodAlternatives[method];
35078
+ if (!alternative) continue;
35079
+ const symbol4 = typeChecker.getSymbolAtLocation(node.expression.expression);
35080
+ if (!symbol4) continue;
35081
+ if (typeCheckerUtils.resolveToGlobalSymbol(symbol4) !== consoleSymbol) continue;
35082
+ const { inEffect } = yield* typeParser.findEnclosingScopes(node);
35083
+ if (inEffect !== checkInEffect) continue;
35084
+ report({
35085
+ location: node,
35086
+ messageText: checkInEffect ? `Prefer using ${alternative} instead of console.${method} inside Effect generators.` : `Prefer using ${alternative} instead of console.${method}.`,
35087
+ fixes: []
35088
+ });
35089
+ }
35090
+ });
35091
+ var globalConsoleInEffect = createDiagnostic({
35092
+ name: "globalConsoleInEffect",
35093
+ code: 56,
35094
+ description: "Warns when using console methods inside Effect generators instead of Effect.log/Logger",
35095
+ group: "effectNative",
35096
+ severity: "off",
35097
+ fixable: false,
35098
+ supportedEffect: ["v3", "v4"],
35099
+ apply: makeGlobalConsoleApply(true)
35100
+ });
35101
+
35102
+ // src/diagnostics/globalConsole.ts
35103
+ var globalConsole = createDiagnostic({
35104
+ name: "globalConsole",
35105
+ code: 60,
35106
+ description: "Warns when using console methods outside Effect generators instead of Effect.log/Logger",
35107
+ group: "effectNative",
35108
+ severity: "off",
35109
+ fixable: false,
35110
+ supportedEffect: ["v3", "v4"],
35111
+ apply: makeGlobalConsoleApply(false)
35112
+ });
35113
+
35114
+ // src/diagnostics/globalDateInEffect.ts
35115
+ var makeGlobalDateApply = (checkInEffect) => fn3(`globalDate${checkInEffect ? "InEffect" : ""}.apply`)(function* (sourceFile, report) {
35116
+ const ts = yield* service2(TypeScriptApi);
35117
+ const typeChecker = yield* service2(TypeCheckerApi);
35118
+ const typeCheckerUtils = yield* service2(TypeCheckerUtils);
35119
+ const typeParser = yield* service2(TypeParser);
35120
+ const dateSymbol = typeChecker.resolveName("Date", void 0, ts.SymbolFlags.Value, false);
35121
+ if (!dateSymbol) return;
35122
+ const nodeToVisit = [];
35123
+ const appendNodeToVisit = (node) => {
35124
+ nodeToVisit.push(node);
35125
+ return void 0;
35126
+ };
35127
+ ts.forEachChild(sourceFile, appendNodeToVisit);
35128
+ while (nodeToVisit.length > 0) {
35129
+ const node = nodeToVisit.shift();
35130
+ ts.forEachChild(node, appendNodeToVisit);
35131
+ let messageText;
35132
+ let objectNode;
35133
+ if (ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression) && ts.idText(node.expression.name) === "now") {
35134
+ objectNode = node.expression.expression;
35135
+ 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().";
35136
+ } else if (ts.isNewExpression(node)) {
35137
+ objectNode = node.expression;
35138
+ messageText = checkInEffect ? "Prefer using DateTime from Effect instead of new Date() inside Effect generators." : "Prefer using DateTime from Effect instead of new Date().";
35139
+ }
35140
+ if (!messageText || !objectNode) continue;
35141
+ const symbol4 = typeChecker.getSymbolAtLocation(objectNode);
35142
+ if (!symbol4) continue;
35143
+ if (typeCheckerUtils.resolveToGlobalSymbol(symbol4) !== dateSymbol) continue;
35144
+ const { inEffect } = yield* typeParser.findEnclosingScopes(node);
35145
+ if (inEffect !== checkInEffect) continue;
35146
+ report({ location: node, messageText, fixes: [] });
35147
+ }
35148
+ });
35149
+ var globalDateInEffect = createDiagnostic({
35150
+ name: "globalDateInEffect",
35151
+ code: 55,
35152
+ description: "Warns when using Date.now() or new Date() inside Effect generators instead of Clock/DateTime",
35153
+ group: "effectNative",
35154
+ severity: "off",
35155
+ fixable: false,
35156
+ supportedEffect: ["v3", "v4"],
35157
+ apply: makeGlobalDateApply(true)
35158
+ });
35159
+
35160
+ // src/diagnostics/globalDate.ts
35161
+ var globalDate = createDiagnostic({
35162
+ name: "globalDate",
35163
+ code: 59,
35164
+ description: "Warns when using Date.now() or new Date() outside Effect generators instead of Clock/DateTime",
35165
+ group: "effectNative",
35166
+ severity: "off",
35167
+ fixable: false,
35168
+ supportedEffect: ["v3", "v4"],
35169
+ apply: makeGlobalDateApply(false)
35170
+ });
35171
+
35023
35172
  // src/diagnostics/globalErrorInEffectCatch.ts
35024
35173
  var globalErrorInEffectCatch = createDiagnostic({
35025
35174
  name: "globalErrorInEffectCatch",
@@ -35139,46 +35288,186 @@ var globalErrorInEffectFailure = createDiagnostic({
35139
35288
  })
35140
35289
  });
35141
35290
 
35291
+ // src/diagnostics/globalFetchInEffect.ts
35292
+ var makeGlobalFetchApply = (checkInEffect) => fn3(`globalFetch${checkInEffect ? "InEffect" : ""}.apply`)(function* (sourceFile, report) {
35293
+ const ts = yield* service2(TypeScriptApi);
35294
+ const typeChecker = yield* service2(TypeCheckerApi);
35295
+ const typeCheckerUtils = yield* service2(TypeCheckerUtils);
35296
+ const typeParser = yield* service2(TypeParser);
35297
+ const fetchSymbol = typeChecker.resolveName("fetch", void 0, ts.SymbolFlags.Value, false);
35298
+ if (!fetchSymbol) return;
35299
+ const effectVersion = typeParser.supportedEffect();
35300
+ const packageName = effectVersion === "v3" ? "@effect/platform" : "effect/unstable/http";
35301
+ 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.`;
35302
+ const nodeToVisit = [];
35303
+ const appendNodeToVisit = (node) => {
35304
+ nodeToVisit.push(node);
35305
+ return void 0;
35306
+ };
35307
+ ts.forEachChild(sourceFile, appendNodeToVisit);
35308
+ while (nodeToVisit.length > 0) {
35309
+ const node = nodeToVisit.shift();
35310
+ if (ts.isCallExpression(node)) {
35311
+ const symbol4 = typeChecker.getSymbolAtLocation(node.expression);
35312
+ if (symbol4 && typeCheckerUtils.resolveToGlobalSymbol(symbol4) === fetchSymbol) {
35313
+ const { inEffect } = yield* typeParser.findEnclosingScopes(node);
35314
+ if (inEffect === checkInEffect) {
35315
+ report({
35316
+ location: node.expression,
35317
+ messageText,
35318
+ fixes: []
35319
+ });
35320
+ }
35321
+ }
35322
+ }
35323
+ ts.forEachChild(node, appendNodeToVisit);
35324
+ }
35325
+ });
35326
+ var globalFetchInEffect = createDiagnostic({
35327
+ name: "globalFetchInEffect",
35328
+ code: 63,
35329
+ description: "Warns when using the global fetch function inside Effect generators instead of the Effect HTTP client",
35330
+ group: "effectNative",
35331
+ severity: "off",
35332
+ fixable: false,
35333
+ supportedEffect: ["v3", "v4"],
35334
+ apply: makeGlobalFetchApply(true)
35335
+ });
35336
+
35142
35337
  // src/diagnostics/globalFetch.ts
35143
35338
  var globalFetch = createDiagnostic({
35144
35339
  name: "globalFetch",
35145
35340
  code: 53,
35146
- description: "Warns when using the global fetch function instead of the Effect HTTP client",
35341
+ description: "Warns when using the global fetch function outside Effect generators instead of the Effect HTTP client",
35147
35342
  group: "effectNative",
35148
35343
  severity: "off",
35149
35344
  fixable: false,
35150
35345
  supportedEffect: ["v3", "v4"],
35151
- apply: fn3("globalFetch.apply")(function* (sourceFile, report) {
35152
- const ts = yield* service2(TypeScriptApi);
35153
- const typeChecker = yield* service2(TypeCheckerApi);
35154
- const typeParser = yield* service2(TypeParser);
35155
- const fetchSymbol = typeChecker.resolveName("fetch", void 0, ts.SymbolFlags.Value, false);
35156
- if (!fetchSymbol) return;
35157
- const effectVersion = typeParser.supportedEffect();
35158
- const packageName = effectVersion === "v3" ? "@effect/platform" : "effect/unstable/http";
35159
- const messageText = `Prefer using HttpClient from ${packageName} instead of the global 'fetch' function.`;
35160
- const nodeToVisit = [];
35161
- const appendNodeToVisit = (node) => {
35162
- nodeToVisit.push(node);
35163
- return void 0;
35164
- };
35165
- ts.forEachChild(sourceFile, appendNodeToVisit);
35166
- while (nodeToVisit.length > 0) {
35167
- const node = nodeToVisit.shift();
35168
- if (ts.isCallExpression(node)) {
35169
- const symbol4 = typeChecker.getSymbolAtLocation(node.expression);
35170
- const resolvedSymbol = symbol4 && symbol4.flags & ts.SymbolFlags.Alias ? typeChecker.getAliasedSymbol(symbol4) : symbol4;
35171
- if (resolvedSymbol === fetchSymbol) {
35172
- report({
35173
- location: node.expression,
35174
- messageText,
35175
- fixes: []
35176
- });
35177
- }
35346
+ apply: makeGlobalFetchApply(false)
35347
+ });
35348
+
35349
+ // src/diagnostics/globalRandomInEffect.ts
35350
+ var makeGlobalRandomApply = (checkInEffect) => fn3(`globalRandom${checkInEffect ? "InEffect" : ""}.apply`)(function* (sourceFile, report) {
35351
+ const ts = yield* service2(TypeScriptApi);
35352
+ const typeChecker = yield* service2(TypeCheckerApi);
35353
+ const typeCheckerUtils = yield* service2(TypeCheckerUtils);
35354
+ const typeParser = yield* service2(TypeParser);
35355
+ const mathSymbol = typeChecker.resolveName("Math", void 0, ts.SymbolFlags.Value, false);
35356
+ if (!mathSymbol) return;
35357
+ const nodeToVisit = [];
35358
+ const appendNodeToVisit = (node) => {
35359
+ nodeToVisit.push(node);
35360
+ return void 0;
35361
+ };
35362
+ ts.forEachChild(sourceFile, appendNodeToVisit);
35363
+ while (nodeToVisit.length > 0) {
35364
+ const node = nodeToVisit.shift();
35365
+ ts.forEachChild(node, appendNodeToVisit);
35366
+ if (!ts.isCallExpression(node) || !ts.isPropertyAccessExpression(node.expression) || ts.idText(node.expression.name) !== "random") continue;
35367
+ const symbol4 = typeChecker.getSymbolAtLocation(node.expression.expression);
35368
+ if (!symbol4) continue;
35369
+ if (typeCheckerUtils.resolveToGlobalSymbol(symbol4) !== mathSymbol) continue;
35370
+ const { inEffect } = yield* typeParser.findEnclosingScopes(node);
35371
+ if (inEffect !== checkInEffect) continue;
35372
+ report({
35373
+ location: node,
35374
+ 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().",
35375
+ fixes: []
35376
+ });
35377
+ }
35378
+ });
35379
+ var globalRandomInEffect = createDiagnostic({
35380
+ name: "globalRandomInEffect",
35381
+ code: 57,
35382
+ description: "Warns when using Math.random() inside Effect generators instead of the Random service",
35383
+ group: "effectNative",
35384
+ severity: "off",
35385
+ fixable: false,
35386
+ supportedEffect: ["v3", "v4"],
35387
+ apply: makeGlobalRandomApply(true)
35388
+ });
35389
+
35390
+ // src/diagnostics/globalRandom.ts
35391
+ var globalRandom = createDiagnostic({
35392
+ name: "globalRandom",
35393
+ code: 61,
35394
+ description: "Warns when using Math.random() outside Effect generators instead of the Random service",
35395
+ group: "effectNative",
35396
+ severity: "off",
35397
+ fixable: false,
35398
+ supportedEffect: ["v3", "v4"],
35399
+ apply: makeGlobalRandomApply(false)
35400
+ });
35401
+
35402
+ // src/diagnostics/globalTimersInEffect.ts
35403
+ var timerAlternatives = {
35404
+ "setTimeout": {
35405
+ inEffect: "Prefer using Effect.sleep or Schedule from Effect instead of setTimeout inside Effect generators.",
35406
+ outsideEffect: "Prefer using Effect.sleep or Schedule from Effect instead of setTimeout."
35407
+ },
35408
+ "setInterval": {
35409
+ inEffect: "Prefer using Schedule or Effect.repeat from Effect instead of setInterval inside Effect generators.",
35410
+ outsideEffect: "Prefer using Schedule or Effect.repeat from Effect instead of setInterval."
35411
+ }
35412
+ };
35413
+ var makeGlobalTimersApply = (checkInEffect) => fn3(`globalTimers${checkInEffect ? "InEffect" : ""}.apply`)(function* (sourceFile, report) {
35414
+ const ts = yield* service2(TypeScriptApi);
35415
+ const typeChecker = yield* service2(TypeCheckerApi);
35416
+ const typeCheckerUtils = yield* service2(TypeCheckerUtils);
35417
+ const typeParser = yield* service2(TypeParser);
35418
+ const globalSymbols = /* @__PURE__ */ new Map();
35419
+ for (const name of Object.keys(timerAlternatives)) {
35420
+ const symbol4 = typeChecker.resolveName(name, void 0, ts.SymbolFlags.Value, false);
35421
+ if (symbol4) globalSymbols.set(name, symbol4);
35422
+ }
35423
+ if (globalSymbols.size === 0) return;
35424
+ const nodeToVisit = [];
35425
+ const appendNodeToVisit = (node) => {
35426
+ nodeToVisit.push(node);
35427
+ return void 0;
35428
+ };
35429
+ ts.forEachChild(sourceFile, appendNodeToVisit);
35430
+ while (nodeToVisit.length > 0) {
35431
+ const node = nodeToVisit.shift();
35432
+ ts.forEachChild(node, appendNodeToVisit);
35433
+ if (!ts.isCallExpression(node)) continue;
35434
+ const symbol4 = typeChecker.getSymbolAtLocation(node.expression);
35435
+ if (!symbol4) continue;
35436
+ const resolvedSymbol = typeCheckerUtils.resolveToGlobalSymbol(symbol4);
35437
+ let messageText;
35438
+ for (const [name, symbol5] of globalSymbols) {
35439
+ if (resolvedSymbol === symbol5) {
35440
+ messageText = checkInEffect ? timerAlternatives[name].inEffect : timerAlternatives[name].outsideEffect;
35441
+ break;
35178
35442
  }
35179
- ts.forEachChild(node, appendNodeToVisit);
35180
35443
  }
35181
- })
35444
+ if (!messageText) continue;
35445
+ const { inEffect } = yield* typeParser.findEnclosingScopes(node);
35446
+ if (inEffect !== checkInEffect) continue;
35447
+ report({ location: node, messageText, fixes: [] });
35448
+ }
35449
+ });
35450
+ var globalTimersInEffect = createDiagnostic({
35451
+ name: "globalTimersInEffect",
35452
+ code: 58,
35453
+ description: "Warns when using setTimeout/setInterval inside Effect generators instead of Effect.sleep/Schedule",
35454
+ group: "effectNative",
35455
+ severity: "off",
35456
+ fixable: false,
35457
+ supportedEffect: ["v3", "v4"],
35458
+ apply: makeGlobalTimersApply(true)
35459
+ });
35460
+
35461
+ // src/diagnostics/globalTimers.ts
35462
+ var globalTimers = createDiagnostic({
35463
+ name: "globalTimers",
35464
+ code: 62,
35465
+ description: "Warns when using setTimeout/setInterval outside Effect generators instead of Effect.sleep/Schedule",
35466
+ group: "effectNative",
35467
+ severity: "off",
35468
+ fixable: false,
35469
+ supportedEffect: ["v3", "v4"],
35470
+ apply: makeGlobalTimersApply(false)
35182
35471
  });
35183
35472
 
35184
35473
  // src/diagnostics/importFromBarrel.ts
@@ -36243,8 +36532,8 @@ var missingReturnYieldStar = createDiagnostic({
36243
36532
  if (!type) continue;
36244
36533
  const maybeEffect = yield* option4(typeParser.effectYieldableType(type, unwrapped.expression));
36245
36534
  if (!(isSome2(maybeEffect) && maybeEffect.value.A.flags & ts.TypeFlags.Never)) continue;
36246
- const { effectGen, scopeNode } = yield* typeParser.findEnclosingScopes(node);
36247
- if (!effectGen || scopeNode && scopeNode !== effectGen.generatorFunction) continue;
36535
+ const { inEffect } = yield* typeParser.findEnclosingScopes(node);
36536
+ if (!inEffect) continue;
36248
36537
  const fix = [{
36249
36538
  fixName: "missingReturnYieldStar_fix",
36250
36539
  description: "Add return statement",
@@ -37638,11 +37927,8 @@ var preferSchemaOverJson = createDiagnostic({
37638
37927
  });
37639
37928
  const jsonMethodInEffectGen = fn3("preferSchemaOverJson.jsonMethodInEffectGen")(
37640
37929
  function* (jsonCall) {
37641
- const { effectGen, scopeNode } = yield* typeParser.findEnclosingScopes(jsonCall);
37642
- if (!effectGen || effectGen.body.statements.length === 0) {
37643
- return yield* TypeParserIssue.issue;
37644
- }
37645
- if (scopeNode && scopeNode !== effectGen.generatorFunction) {
37930
+ const { inEffect } = yield* typeParser.findEnclosingScopes(jsonCall);
37931
+ if (!inEffect) {
37646
37932
  return yield* TypeParserIssue.issue;
37647
37933
  }
37648
37934
  return jsonCall;
@@ -37805,16 +38091,16 @@ Nested Effect-able types may be intended if you plan to later manually flatten o
37805
38091
  var runEffectInsideEffect = createDiagnostic({
37806
38092
  name: "runEffectInsideEffect",
37807
38093
  code: 32,
37808
- description: "Suggests using Runtime methods instead of Effect.run* inside Effect contexts",
38094
+ description: "Suggests using Runtime or Effect.run*With methods instead of Effect.run* inside Effect contexts",
37809
38095
  group: "antipattern",
37810
38096
  severity: "suggestion",
37811
38097
  fixable: true,
37812
- supportedEffect: ["v3"],
38098
+ supportedEffect: ["v3", "v4"],
37813
38099
  apply: fn3("runEffectInsideEffect.apply")(function* (sourceFile, report) {
37814
38100
  const ts = yield* service2(TypeScriptApi);
37815
38101
  const typeParser = yield* service2(TypeParser);
37816
38102
  const tsUtils = yield* service2(TypeScriptUtils);
37817
- if (typeParser.supportedEffect() === "v4") return;
38103
+ const supportedEffect = typeParser.supportedEffect();
37818
38104
  const parseEffectMethod = (node, methodName) => pipe(
37819
38105
  typeParser.isNodeReferenceToEffectModuleApi(methodName)(node),
37820
38106
  map12(() => ({ node, methodName }))
@@ -37847,9 +38133,10 @@ var runEffectInsideEffect = createDiagnostic({
37847
38133
  if (scopeNode && scopeNode !== effectGen.generatorFunction) {
37848
38134
  const fixAddRuntime = gen3(function* () {
37849
38135
  const changeTracker = yield* service2(ChangeTracker);
37850
- const runtimeModuleIdentifier = tsUtils.findImportedModuleIdentifierByPackageAndNameOrBarrel(sourceFile, "effect", "Runtime") || "Runtime";
37851
38136
  const effectModuleIdentifier = tsUtils.findImportedModuleIdentifierByPackageAndNameOrBarrel(sourceFile, "effect", "Effect") || "Effect";
38137
+ const runtimeModuleIdentifier = tsUtils.findImportedModuleIdentifierByPackageAndNameOrBarrel(sourceFile, "effect", "Runtime") || "Runtime";
37852
38138
  let runtimeIdentifier = void 0;
38139
+ let servicesIdentifier = void 0;
37853
38140
  for (const statement of effectGen.generatorFunction.body.statements) {
37854
38141
  if (ts.isVariableStatement(statement) && statement.declarationList.declarations.length === 1) {
37855
38142
  const declaration = statement.declarationList.declarations[0];
@@ -37863,11 +38150,46 @@ var runEffectInsideEffect = createDiagnostic({
37863
38150
  if (isSome2(maybeEffectRuntime) && ts.isIdentifier(declaration.name)) {
37864
38151
  runtimeIdentifier = ts.idText(declaration.name);
37865
38152
  }
38153
+ const maybeEffectServices = yield* pipe(
38154
+ typeParser.isNodeReferenceToEffectModuleApi("services")(yieldedExpression.expression),
38155
+ option4
38156
+ );
38157
+ if (isSome2(maybeEffectServices) && ts.isIdentifier(declaration.name)) {
38158
+ servicesIdentifier = ts.idText(declaration.name);
38159
+ }
37866
38160
  }
37867
38161
  }
37868
38162
  }
37869
38163
  }
37870
- if (!runtimeIdentifier) {
38164
+ if (supportedEffect === "v4" && !servicesIdentifier) {
38165
+ changeTracker.insertNodeAt(
38166
+ sourceFile,
38167
+ effectGen.body.statements[0].pos,
38168
+ ts.factory.createVariableStatement(
38169
+ void 0,
38170
+ ts.factory.createVariableDeclarationList([ts.factory.createVariableDeclaration(
38171
+ "effectServices",
38172
+ void 0,
38173
+ void 0,
38174
+ ts.factory.createYieldExpression(
38175
+ ts.factory.createToken(ts.SyntaxKind.AsteriskToken),
38176
+ ts.factory.createCallExpression(
38177
+ ts.factory.createPropertyAccessExpression(
38178
+ ts.factory.createIdentifier(effectModuleIdentifier),
38179
+ "services"
38180
+ ),
38181
+ [ts.factory.createKeywordTypeNode(ts.SyntaxKind.NeverKeyword)],
38182
+ []
38183
+ )
38184
+ )
38185
+ )], ts.NodeFlags.Const)
38186
+ ),
38187
+ {
38188
+ prefix: "\n",
38189
+ suffix: "\n"
38190
+ }
38191
+ );
38192
+ } else if (supportedEffect === "v3" && !runtimeIdentifier) {
37871
38193
  changeTracker.insertNodeAt(
37872
38194
  sourceFile,
37873
38195
  effectGen.body.statements[0].pos,
@@ -37903,16 +38225,19 @@ var runEffectInsideEffect = createDiagnostic({
37903
38225
  changeTracker.insertText(
37904
38226
  sourceFile,
37905
38227
  node.arguments[0].pos,
37906
- `${runtimeModuleIdentifier}.${isEffectRunCall.value.methodName}(${runtimeIdentifier || "effectRuntime"}, `
38228
+ supportedEffect === "v4" ? `${effectModuleIdentifier}.${isEffectRunCall.value.methodName}With(${servicesIdentifier || "effectServices"})(` : `${runtimeModuleIdentifier}.${isEffectRunCall.value.methodName}(${runtimeIdentifier || "effectRuntime"}, `
37907
38229
  );
37908
38230
  });
38231
+ const v4MethodName = `${isEffectRunCall.value.methodName}With`;
38232
+ const messageText = supportedEffect === "v4" ? `Using ${nodeText} inside an Effect is not recommended. The same services should generally be used instead to run child effects.
38233
+ 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.
38234
+ Consider extracting the Runtime by using for example Effect.runtime and then use Runtime.${isEffectRunCall.value.methodName} with the extracted runtime instead.`;
37909
38235
  report({
37910
38236
  location: node.expression,
37911
- messageText: `Using ${nodeText} inside an Effect is not recommended. The same runtime should generally be used instead to run child effects.
37912
- Consider extracting the Runtime by using for example Effect.runtime and then use Runtime.${isEffectRunCall.value.methodName} with the extracted runtime instead.`,
38237
+ messageText,
37913
38238
  fixes: [{
37914
38239
  fixName: "runEffectInsideEffect_fix",
37915
- description: "Use a runtime to run the Effect",
38240
+ description: supportedEffect === "v4" ? "Use the current services to run the Effect" : "Use a runtime to run the Effect",
37916
38241
  apply: fixAddRuntime
37917
38242
  }]
37918
38243
  });
@@ -38058,9 +38383,8 @@ var schemaSyncInEffect = createDiagnostic({
38058
38383
  option4
38059
38384
  );
38060
38385
  if (isNone2(isSchemaSyncCall)) continue;
38061
- const { effectGen, scopeNode } = yield* typeParser.findEnclosingScopes(node);
38062
- if (!effectGen || effectGen.body.statements.length === 0) continue;
38063
- if (scopeNode && scopeNode !== effectGen.generatorFunction) continue;
38386
+ const { inEffect } = yield* typeParser.findEnclosingScopes(node);
38387
+ if (!inEffect) continue;
38064
38388
  const nodeText = sourceFile.text.substring(
38065
38389
  ts.getTokenPosOfNode(node.expression, sourceFile),
38066
38390
  node.expression.end
@@ -38902,6 +39226,7 @@ var diagnostics = [
38902
39226
  unnecessaryPipe,
38903
39227
  genericEffectServices,
38904
39228
  globalFetch,
39229
+ globalFetchInEffect,
38905
39230
  returnEffectInGen,
38906
39231
  tryCatchInEffectGen,
38907
39232
  importFromBarrel,
@@ -38933,7 +39258,15 @@ var diagnostics = [
38933
39258
  preferSchemaOverJson,
38934
39259
  extendsNativeError,
38935
39260
  serviceNotAsClass,
38936
- nodeBuiltinImport
39261
+ nodeBuiltinImport,
39262
+ globalDate,
39263
+ globalDateInEffect,
39264
+ globalConsole,
39265
+ globalConsoleInEffect,
39266
+ globalRandom,
39267
+ globalRandomInEffect,
39268
+ globalTimers,
39269
+ globalTimersInEffect
38937
39270
  ];
38938
39271
 
38939
39272
  // src/metadata.json
@@ -38967,9 +39300,18 @@ var metadata_default = {
38967
39300
  diagnosticSeverity: {
38968
39301
  instanceOfSchema: "warning",
38969
39302
  globalFetch: "warning",
39303
+ globalFetchInEffect: "warning",
38970
39304
  preferSchemaOverJson: "warning",
38971
39305
  extendsNativeError: "warning",
38972
- nodeBuiltinImport: "warning"
39306
+ nodeBuiltinImport: "warning",
39307
+ globalDate: "warning",
39308
+ globalDateInEffect: "warning",
39309
+ globalConsole: "warning",
39310
+ globalConsoleInEffect: "warning",
39311
+ globalRandom: "warning",
39312
+ globalRandomInEffect: "warning",
39313
+ globalTimers: "warning",
39314
+ globalTimersInEffect: "warning"
38973
39315
  }
38974
39316
  }
38975
39317
  ],
@@ -39561,11 +39903,12 @@ var metadata_default = {
39561
39903
  {
39562
39904
  name: "runEffectInsideEffect",
39563
39905
  group: "antipattern",
39564
- description: "Suggests using Runtime methods instead of Effect.run* inside Effect contexts",
39906
+ description: "Suggests using Runtime or Effect.run*With methods instead of Effect.run* inside Effect contexts",
39565
39907
  defaultSeverity: "suggestion",
39566
39908
  fixable: true,
39567
39909
  supportedEffect: [
39568
- "v3"
39910
+ "v3",
39911
+ "v4"
39569
39912
  ],
39570
39913
  preview: {
39571
39914
  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',
@@ -39573,7 +39916,7 @@ var metadata_default = {
39573
39916
  {
39574
39917
  start: 101,
39575
39918
  end: 115,
39576
- 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)"
39919
+ 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)"
39577
39920
  }
39578
39921
  ]
39579
39922
  }
@@ -39702,10 +40045,94 @@ var metadata_default = {
39702
40045
  ]
39703
40046
  }
39704
40047
  },
40048
+ {
40049
+ name: "globalConsole",
40050
+ group: "effectNative",
40051
+ description: "Warns when using console methods outside Effect generators instead of Effect.log/Logger",
40052
+ defaultSeverity: "off",
40053
+ fixable: false,
40054
+ supportedEffect: [
40055
+ "v3",
40056
+ "v4"
40057
+ ],
40058
+ preview: {
40059
+ sourceText: '\nconsole.log("preview")\n',
40060
+ diagnostics: [
40061
+ {
40062
+ start: 1,
40063
+ end: 23,
40064
+ text: "Prefer using Effect.log or Logger instead of console.log. effect(globalConsole)"
40065
+ }
40066
+ ]
40067
+ }
40068
+ },
40069
+ {
40070
+ name: "globalConsoleInEffect",
40071
+ group: "effectNative",
40072
+ description: "Warns when using console methods inside Effect generators instead of Effect.log/Logger",
40073
+ defaultSeverity: "off",
40074
+ fixable: false,
40075
+ supportedEffect: [
40076
+ "v3",
40077
+ "v4"
40078
+ ],
40079
+ preview: {
40080
+ sourceText: 'import { Effect } from "effect"\n\nexport const preview = Effect.gen(function*() {\n console.log("hello")\n})\n',
40081
+ diagnostics: [
40082
+ {
40083
+ start: 83,
40084
+ end: 103,
40085
+ text: "Prefer using Effect.log or Logger instead of console.log inside Effect generators. effect(globalConsoleInEffect)"
40086
+ }
40087
+ ]
40088
+ }
40089
+ },
40090
+ {
40091
+ name: "globalDate",
40092
+ group: "effectNative",
40093
+ description: "Warns when using Date.now() or new Date() outside Effect generators instead of Clock/DateTime",
40094
+ defaultSeverity: "off",
40095
+ fixable: false,
40096
+ supportedEffect: [
40097
+ "v3",
40098
+ "v4"
40099
+ ],
40100
+ preview: {
40101
+ sourceText: "\nexport const preview = Date.now()\n",
40102
+ diagnostics: [
40103
+ {
40104
+ start: 24,
40105
+ end: 34,
40106
+ text: "Prefer using Clock or DateTime from Effect instead of Date.now(). effect(globalDate)"
40107
+ }
40108
+ ]
40109
+ }
40110
+ },
40111
+ {
40112
+ name: "globalDateInEffect",
40113
+ group: "effectNative",
40114
+ description: "Warns when using Date.now() or new Date() inside Effect generators instead of Clock/DateTime",
40115
+ defaultSeverity: "off",
40116
+ fixable: false,
40117
+ supportedEffect: [
40118
+ "v3",
40119
+ "v4"
40120
+ ],
40121
+ preview: {
40122
+ sourceText: 'import { Effect } from "effect"\n\nexport const preview = Effect.gen(function*() {\n const now = Date.now()\n return now\n})\n',
40123
+ diagnostics: [
40124
+ {
40125
+ start: 95,
40126
+ end: 105,
40127
+ text: "Prefer using Clock or DateTime from Effect instead of Date.now() inside Effect generators. effect(globalDateInEffect)"
40128
+ }
40129
+ ]
40130
+ }
40131
+ },
39705
40132
  {
39706
40133
  name: "globalFetch",
39707
40134
  group: "effectNative",
39708
- description: "Warns when using the global fetch function instead of the Effect HTTP client",
40135
+ description: "Warns when using the global fetch function outside Effect generators instead of the Effect HTTP client",
39709
40136
  defaultSeverity: "off",
39710
40137
  fixable: false,
39711
40138
  supportedEffect: [
@@ -39723,6 +40150,105 @@ var metadata_default = {
39723
40150
  ]
39724
40151
  }
39725
40152
  },
40153
+ {
40154
+ name: "globalFetchInEffect",
40155
+ group: "effectNative",
40156
+ description: "Warns when using the global fetch function inside Effect generators instead of the Effect HTTP client",
40157
+ defaultSeverity: "off",
40158
+ fixable: false,
40159
+ supportedEffect: [
40160
+ "v3",
40161
+ "v4"
40162
+ ],
40163
+ preview: {
40164
+ sourceText: 'import { Effect } from "effect"\n\nexport const preview = Effect.gen(function*() {\n return yield* Effect.promise(() => fetch("https://example.com"))\n})\n',
40165
+ diagnostics: []
40166
+ }
40167
+ },
40168
+ {
40169
+ name: "globalRandom",
40170
+ group: "effectNative",
40171
+ description: "Warns when using Math.random() outside Effect generators instead of the Random service",
40172
+ defaultSeverity: "off",
40173
+ fixable: false,
40174
+ supportedEffect: [
40175
+ "v3",
40176
+ "v4"
40177
+ ],
40178
+ preview: {
40179
+ sourceText: "\nexport const preview = Math.random()\n",
40180
+ diagnostics: [
40181
+ {
40182
+ start: 24,
40183
+ end: 37,
40184
+ text: "Prefer using the Random service from Effect instead of Math.random(). effect(globalRandom)"
40185
+ }
40186
+ ]
40187
+ }
40188
+ },
40189
+ {
40190
+ name: "globalRandomInEffect",
40191
+ group: "effectNative",
40192
+ description: "Warns when using Math.random() inside Effect generators instead of the Random service",
40193
+ defaultSeverity: "off",
40194
+ fixable: false,
40195
+ supportedEffect: [
40196
+ "v3",
40197
+ "v4"
40198
+ ],
40199
+ preview: {
40200
+ sourceText: 'import { Effect } from "effect"\n\nexport const preview = Effect.gen(function*() {\n const r = Math.random()\n return r\n})\n',
40201
+ diagnostics: [
40202
+ {
40203
+ start: 93,
40204
+ end: 106,
40205
+ text: "Prefer using the Random service from Effect instead of Math.random() inside Effect generators. effect(globalRandomInEffect)"
40206
+ }
40207
+ ]
40208
+ }
40209
+ },
40210
+ {
40211
+ name: "globalTimers",
40212
+ group: "effectNative",
40213
+ description: "Warns when using setTimeout/setInterval outside Effect generators instead of Effect.sleep/Schedule",
40214
+ defaultSeverity: "off",
40215
+ fixable: false,
40216
+ supportedEffect: [
40217
+ "v3",
40218
+ "v4"
40219
+ ],
40220
+ preview: {
40221
+ sourceText: "\nsetTimeout(() => {}, 100)\n",
40222
+ diagnostics: [
40223
+ {
40224
+ start: 1,
40225
+ end: 26,
40226
+ text: "Prefer using Effect.sleep or Schedule from Effect instead of setTimeout. effect(globalTimers)"
40227
+ }
40228
+ ]
40229
+ }
40230
+ },
40231
+ {
40232
+ name: "globalTimersInEffect",
40233
+ group: "effectNative",
40234
+ description: "Warns when using setTimeout/setInterval inside Effect generators instead of Effect.sleep/Schedule",
40235
+ defaultSeverity: "off",
40236
+ fixable: false,
40237
+ supportedEffect: [
40238
+ "v3",
40239
+ "v4"
40240
+ ],
40241
+ preview: {
40242
+ sourceText: 'import { Effect } from "effect"\n\nexport const preview = Effect.gen(function*() {\n setTimeout(() => {}, 100)\n})\n',
40243
+ diagnostics: [
40244
+ {
40245
+ start: 83,
40246
+ end: 108,
40247
+ text: "Prefer using Effect.sleep or Schedule from Effect instead of setTimeout inside Effect generators. effect(globalTimersInEffect)"
40248
+ }
40249
+ ]
40250
+ }
40251
+ },
39726
40252
  {
39727
40253
  name: "instanceOfSchema",
39728
40254
  group: "effectNative",