@effect/language-service 0.83.1 → 0.84.0

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.
@@ -3126,6 +3126,25 @@ function makeTypeCheckerUtils(ts, typeChecker, tsUtils) {
3126
3126
  return typeChecker.getTypeAtLocation(node);
3127
3127
  }
3128
3128
  }
3129
+ function resolveToGlobalSymbol(symbol3) {
3130
+ if (symbol3.flags & ts.SymbolFlags.Alias) {
3131
+ symbol3 = typeChecker.getAliasedSymbol(symbol3);
3132
+ }
3133
+ let depth = 0;
3134
+ while (depth < 2 && symbol3.valueDeclaration && ts.isVariableDeclaration(symbol3.valueDeclaration)) {
3135
+ const initializer = symbol3.valueDeclaration.initializer;
3136
+ if (!initializer) break;
3137
+ let nextSymbol = typeChecker.getSymbolAtLocation(initializer);
3138
+ if (!nextSymbol) break;
3139
+ if (nextSymbol.flags & ts.SymbolFlags.Alias) {
3140
+ nextSymbol = typeChecker.getAliasedSymbol(nextSymbol);
3141
+ }
3142
+ if (nextSymbol === symbol3) break;
3143
+ symbol3 = nextSymbol;
3144
+ depth++;
3145
+ }
3146
+ return symbol3;
3147
+ }
3129
3148
  return {
3130
3149
  isUnion,
3131
3150
  isReadonlyArrayType,
@@ -3139,7 +3158,8 @@ function makeTypeCheckerUtils(ts, typeChecker, tsUtils) {
3139
3158
  expectedAndRealType,
3140
3159
  typeToSimplifiedTypeNode,
3141
3160
  isGlobalErrorType,
3142
- getTypeAtLocation
3161
+ getTypeAtLocation,
3162
+ resolveToGlobalSymbol
3143
3163
  };
3144
3164
  }
3145
3165
 
@@ -3827,7 +3847,11 @@ function make2(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
3827
3847
  }
3828
3848
  currentParent = nodeToCheck.parent;
3829
3849
  }
3830
- return { scopeNode, effectGen: effectGenResult };
3850
+ return {
3851
+ inEffect: effectGenResult !== void 0 && effectGenResult.body.statements.length > 0 && scopeNode === effectGenResult.generatorFunction,
3852
+ scopeNode,
3853
+ effectGen: effectGenResult
3854
+ };
3831
3855
  });
3832
3856
  const effectFn = cachedBy(
3833
3857
  function(node) {
@@ -7022,6 +7046,128 @@ var genericEffectServices = createDiagnostic({
7022
7046
  })
7023
7047
  });
7024
7048
 
7049
+ // src/diagnostics/globalConsoleInEffect.ts
7050
+ var consoleMethodAlternatives = {
7051
+ "log": "Effect.log or Logger",
7052
+ "warn": "Effect.logWarning or Logger",
7053
+ "error": "Effect.logError or Logger",
7054
+ "info": "Effect.logInfo or Logger",
7055
+ "debug": "Effect.logDebug or Logger",
7056
+ "trace": "Effect.logTrace or Logger"
7057
+ };
7058
+ var makeGlobalConsoleApply = (checkInEffect) => fn(`globalConsole${checkInEffect ? "InEffect" : ""}.apply`)(function* (sourceFile, report) {
7059
+ const ts = yield* service(TypeScriptApi);
7060
+ const typeChecker = yield* service(TypeCheckerApi);
7061
+ const typeCheckerUtils = yield* service(TypeCheckerUtils);
7062
+ const typeParser = yield* service(TypeParser);
7063
+ const consoleSymbol = typeChecker.resolveName("console", void 0, ts.SymbolFlags.Value, false);
7064
+ if (!consoleSymbol) return;
7065
+ const nodeToVisit = [];
7066
+ const appendNodeToVisit = (node) => {
7067
+ nodeToVisit.push(node);
7068
+ return void 0;
7069
+ };
7070
+ ts.forEachChild(sourceFile, appendNodeToVisit);
7071
+ while (nodeToVisit.length > 0) {
7072
+ const node = nodeToVisit.shift();
7073
+ ts.forEachChild(node, appendNodeToVisit);
7074
+ if (!ts.isCallExpression(node) || !ts.isPropertyAccessExpression(node.expression)) continue;
7075
+ const method = ts.idText(node.expression.name);
7076
+ const alternative = consoleMethodAlternatives[method];
7077
+ if (!alternative) continue;
7078
+ const symbol3 = typeChecker.getSymbolAtLocation(node.expression.expression);
7079
+ if (!symbol3) continue;
7080
+ if (typeCheckerUtils.resolveToGlobalSymbol(symbol3) !== consoleSymbol) continue;
7081
+ const { inEffect } = yield* typeParser.findEnclosingScopes(node);
7082
+ if (inEffect !== checkInEffect) continue;
7083
+ report({
7084
+ location: node,
7085
+ messageText: checkInEffect ? `Prefer using ${alternative} instead of console.${method} inside Effect generators.` : `Prefer using ${alternative} instead of console.${method}.`,
7086
+ fixes: []
7087
+ });
7088
+ }
7089
+ });
7090
+ var globalConsoleInEffect = createDiagnostic({
7091
+ name: "globalConsoleInEffect",
7092
+ code: 56,
7093
+ description: "Warns when using console methods inside Effect generators instead of Effect.log/Logger",
7094
+ group: "effectNative",
7095
+ severity: "off",
7096
+ fixable: false,
7097
+ supportedEffect: ["v3", "v4"],
7098
+ apply: makeGlobalConsoleApply(true)
7099
+ });
7100
+
7101
+ // src/diagnostics/globalConsole.ts
7102
+ var globalConsole = createDiagnostic({
7103
+ name: "globalConsole",
7104
+ code: 60,
7105
+ description: "Warns when using console methods outside Effect generators instead of Effect.log/Logger",
7106
+ group: "effectNative",
7107
+ severity: "off",
7108
+ fixable: false,
7109
+ supportedEffect: ["v3", "v4"],
7110
+ apply: makeGlobalConsoleApply(false)
7111
+ });
7112
+
7113
+ // src/diagnostics/globalDateInEffect.ts
7114
+ var makeGlobalDateApply = (checkInEffect) => fn(`globalDate${checkInEffect ? "InEffect" : ""}.apply`)(function* (sourceFile, report) {
7115
+ const ts = yield* service(TypeScriptApi);
7116
+ const typeChecker = yield* service(TypeCheckerApi);
7117
+ const typeCheckerUtils = yield* service(TypeCheckerUtils);
7118
+ const typeParser = yield* service(TypeParser);
7119
+ const dateSymbol = typeChecker.resolveName("Date", void 0, ts.SymbolFlags.Value, false);
7120
+ if (!dateSymbol) return;
7121
+ const nodeToVisit = [];
7122
+ const appendNodeToVisit = (node) => {
7123
+ nodeToVisit.push(node);
7124
+ return void 0;
7125
+ };
7126
+ ts.forEachChild(sourceFile, appendNodeToVisit);
7127
+ while (nodeToVisit.length > 0) {
7128
+ const node = nodeToVisit.shift();
7129
+ ts.forEachChild(node, appendNodeToVisit);
7130
+ let messageText;
7131
+ let objectNode;
7132
+ if (ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression) && ts.idText(node.expression.name) === "now") {
7133
+ objectNode = node.expression.expression;
7134
+ 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().";
7135
+ } else if (ts.isNewExpression(node)) {
7136
+ objectNode = node.expression;
7137
+ messageText = checkInEffect ? "Prefer using DateTime from Effect instead of new Date() inside Effect generators." : "Prefer using DateTime from Effect instead of new Date().";
7138
+ }
7139
+ if (!messageText || !objectNode) continue;
7140
+ const symbol3 = typeChecker.getSymbolAtLocation(objectNode);
7141
+ if (!symbol3) continue;
7142
+ if (typeCheckerUtils.resolveToGlobalSymbol(symbol3) !== dateSymbol) continue;
7143
+ const { inEffect } = yield* typeParser.findEnclosingScopes(node);
7144
+ if (inEffect !== checkInEffect) continue;
7145
+ report({ location: node, messageText, fixes: [] });
7146
+ }
7147
+ });
7148
+ var globalDateInEffect = createDiagnostic({
7149
+ name: "globalDateInEffect",
7150
+ code: 55,
7151
+ description: "Warns when using Date.now() or new Date() inside Effect generators instead of Clock/DateTime",
7152
+ group: "effectNative",
7153
+ severity: "off",
7154
+ fixable: false,
7155
+ supportedEffect: ["v3", "v4"],
7156
+ apply: makeGlobalDateApply(true)
7157
+ });
7158
+
7159
+ // src/diagnostics/globalDate.ts
7160
+ var globalDate = createDiagnostic({
7161
+ name: "globalDate",
7162
+ code: 59,
7163
+ description: "Warns when using Date.now() or new Date() outside Effect generators instead of Clock/DateTime",
7164
+ group: "effectNative",
7165
+ severity: "off",
7166
+ fixable: false,
7167
+ supportedEffect: ["v3", "v4"],
7168
+ apply: makeGlobalDateApply(false)
7169
+ });
7170
+
7025
7171
  // src/diagnostics/globalErrorInEffectCatch.ts
7026
7172
  var globalErrorInEffectCatch = createDiagnostic({
7027
7173
  name: "globalErrorInEffectCatch",
@@ -7141,46 +7287,186 @@ var globalErrorInEffectFailure = createDiagnostic({
7141
7287
  })
7142
7288
  });
7143
7289
 
7290
+ // src/diagnostics/globalFetchInEffect.ts
7291
+ var makeGlobalFetchApply = (checkInEffect) => fn(`globalFetch${checkInEffect ? "InEffect" : ""}.apply`)(function* (sourceFile, report) {
7292
+ const ts = yield* service(TypeScriptApi);
7293
+ const typeChecker = yield* service(TypeCheckerApi);
7294
+ const typeCheckerUtils = yield* service(TypeCheckerUtils);
7295
+ const typeParser = yield* service(TypeParser);
7296
+ const fetchSymbol = typeChecker.resolveName("fetch", void 0, ts.SymbolFlags.Value, false);
7297
+ if (!fetchSymbol) return;
7298
+ const effectVersion = typeParser.supportedEffect();
7299
+ const packageName = effectVersion === "v3" ? "@effect/platform" : "effect/unstable/http";
7300
+ 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.`;
7301
+ const nodeToVisit = [];
7302
+ const appendNodeToVisit = (node) => {
7303
+ nodeToVisit.push(node);
7304
+ return void 0;
7305
+ };
7306
+ ts.forEachChild(sourceFile, appendNodeToVisit);
7307
+ while (nodeToVisit.length > 0) {
7308
+ const node = nodeToVisit.shift();
7309
+ if (ts.isCallExpression(node)) {
7310
+ const symbol3 = typeChecker.getSymbolAtLocation(node.expression);
7311
+ if (symbol3 && typeCheckerUtils.resolveToGlobalSymbol(symbol3) === fetchSymbol) {
7312
+ const { inEffect } = yield* typeParser.findEnclosingScopes(node);
7313
+ if (inEffect === checkInEffect) {
7314
+ report({
7315
+ location: node.expression,
7316
+ messageText,
7317
+ fixes: []
7318
+ });
7319
+ }
7320
+ }
7321
+ }
7322
+ ts.forEachChild(node, appendNodeToVisit);
7323
+ }
7324
+ });
7325
+ var globalFetchInEffect = createDiagnostic({
7326
+ name: "globalFetchInEffect",
7327
+ code: 63,
7328
+ description: "Warns when using the global fetch function inside Effect generators instead of the Effect HTTP client",
7329
+ group: "effectNative",
7330
+ severity: "off",
7331
+ fixable: false,
7332
+ supportedEffect: ["v3", "v4"],
7333
+ apply: makeGlobalFetchApply(true)
7334
+ });
7335
+
7144
7336
  // src/diagnostics/globalFetch.ts
7145
7337
  var globalFetch = createDiagnostic({
7146
7338
  name: "globalFetch",
7147
7339
  code: 53,
7148
- description: "Warns when using the global fetch function instead of the Effect HTTP client",
7340
+ description: "Warns when using the global fetch function outside Effect generators instead of the Effect HTTP client",
7149
7341
  group: "effectNative",
7150
7342
  severity: "off",
7151
7343
  fixable: false,
7152
7344
  supportedEffect: ["v3", "v4"],
7153
- apply: fn("globalFetch.apply")(function* (sourceFile, report) {
7154
- const ts = yield* service(TypeScriptApi);
7155
- const typeChecker = yield* service(TypeCheckerApi);
7156
- const typeParser = yield* service(TypeParser);
7157
- const fetchSymbol = typeChecker.resolveName("fetch", void 0, ts.SymbolFlags.Value, false);
7158
- if (!fetchSymbol) return;
7159
- const effectVersion = typeParser.supportedEffect();
7160
- const packageName = effectVersion === "v3" ? "@effect/platform" : "effect/unstable/http";
7161
- const messageText = `Prefer using HttpClient from ${packageName} instead of the global 'fetch' function.`;
7162
- const nodeToVisit = [];
7163
- const appendNodeToVisit = (node) => {
7164
- nodeToVisit.push(node);
7165
- return void 0;
7166
- };
7167
- ts.forEachChild(sourceFile, appendNodeToVisit);
7168
- while (nodeToVisit.length > 0) {
7169
- const node = nodeToVisit.shift();
7170
- if (ts.isCallExpression(node)) {
7171
- const symbol3 = typeChecker.getSymbolAtLocation(node.expression);
7172
- const resolvedSymbol = symbol3 && symbol3.flags & ts.SymbolFlags.Alias ? typeChecker.getAliasedSymbol(symbol3) : symbol3;
7173
- if (resolvedSymbol === fetchSymbol) {
7174
- report({
7175
- location: node.expression,
7176
- messageText,
7177
- fixes: []
7178
- });
7179
- }
7345
+ apply: makeGlobalFetchApply(false)
7346
+ });
7347
+
7348
+ // src/diagnostics/globalRandomInEffect.ts
7349
+ var makeGlobalRandomApply = (checkInEffect) => fn(`globalRandom${checkInEffect ? "InEffect" : ""}.apply`)(function* (sourceFile, report) {
7350
+ const ts = yield* service(TypeScriptApi);
7351
+ const typeChecker = yield* service(TypeCheckerApi);
7352
+ const typeCheckerUtils = yield* service(TypeCheckerUtils);
7353
+ const typeParser = yield* service(TypeParser);
7354
+ const mathSymbol = typeChecker.resolveName("Math", void 0, ts.SymbolFlags.Value, false);
7355
+ if (!mathSymbol) return;
7356
+ const nodeToVisit = [];
7357
+ const appendNodeToVisit = (node) => {
7358
+ nodeToVisit.push(node);
7359
+ return void 0;
7360
+ };
7361
+ ts.forEachChild(sourceFile, appendNodeToVisit);
7362
+ while (nodeToVisit.length > 0) {
7363
+ const node = nodeToVisit.shift();
7364
+ ts.forEachChild(node, appendNodeToVisit);
7365
+ if (!ts.isCallExpression(node) || !ts.isPropertyAccessExpression(node.expression) || ts.idText(node.expression.name) !== "random") continue;
7366
+ const symbol3 = typeChecker.getSymbolAtLocation(node.expression.expression);
7367
+ if (!symbol3) continue;
7368
+ if (typeCheckerUtils.resolveToGlobalSymbol(symbol3) !== mathSymbol) continue;
7369
+ const { inEffect } = yield* typeParser.findEnclosingScopes(node);
7370
+ if (inEffect !== checkInEffect) continue;
7371
+ report({
7372
+ location: node,
7373
+ 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().",
7374
+ fixes: []
7375
+ });
7376
+ }
7377
+ });
7378
+ var globalRandomInEffect = createDiagnostic({
7379
+ name: "globalRandomInEffect",
7380
+ code: 57,
7381
+ description: "Warns when using Math.random() inside Effect generators instead of the Random service",
7382
+ group: "effectNative",
7383
+ severity: "off",
7384
+ fixable: false,
7385
+ supportedEffect: ["v3", "v4"],
7386
+ apply: makeGlobalRandomApply(true)
7387
+ });
7388
+
7389
+ // src/diagnostics/globalRandom.ts
7390
+ var globalRandom = createDiagnostic({
7391
+ name: "globalRandom",
7392
+ code: 61,
7393
+ description: "Warns when using Math.random() outside Effect generators instead of the Random service",
7394
+ group: "effectNative",
7395
+ severity: "off",
7396
+ fixable: false,
7397
+ supportedEffect: ["v3", "v4"],
7398
+ apply: makeGlobalRandomApply(false)
7399
+ });
7400
+
7401
+ // src/diagnostics/globalTimersInEffect.ts
7402
+ var timerAlternatives = {
7403
+ "setTimeout": {
7404
+ inEffect: "Prefer using Effect.sleep or Schedule from Effect instead of setTimeout inside Effect generators.",
7405
+ outsideEffect: "Prefer using Effect.sleep or Schedule from Effect instead of setTimeout."
7406
+ },
7407
+ "setInterval": {
7408
+ inEffect: "Prefer using Schedule or Effect.repeat from Effect instead of setInterval inside Effect generators.",
7409
+ outsideEffect: "Prefer using Schedule or Effect.repeat from Effect instead of setInterval."
7410
+ }
7411
+ };
7412
+ var makeGlobalTimersApply = (checkInEffect) => fn(`globalTimers${checkInEffect ? "InEffect" : ""}.apply`)(function* (sourceFile, report) {
7413
+ const ts = yield* service(TypeScriptApi);
7414
+ const typeChecker = yield* service(TypeCheckerApi);
7415
+ const typeCheckerUtils = yield* service(TypeCheckerUtils);
7416
+ const typeParser = yield* service(TypeParser);
7417
+ const globalSymbols = /* @__PURE__ */ new Map();
7418
+ for (const name of Object.keys(timerAlternatives)) {
7419
+ const symbol3 = typeChecker.resolveName(name, void 0, ts.SymbolFlags.Value, false);
7420
+ if (symbol3) globalSymbols.set(name, symbol3);
7421
+ }
7422
+ if (globalSymbols.size === 0) return;
7423
+ const nodeToVisit = [];
7424
+ const appendNodeToVisit = (node) => {
7425
+ nodeToVisit.push(node);
7426
+ return void 0;
7427
+ };
7428
+ ts.forEachChild(sourceFile, appendNodeToVisit);
7429
+ while (nodeToVisit.length > 0) {
7430
+ const node = nodeToVisit.shift();
7431
+ ts.forEachChild(node, appendNodeToVisit);
7432
+ if (!ts.isCallExpression(node)) continue;
7433
+ const symbol3 = typeChecker.getSymbolAtLocation(node.expression);
7434
+ if (!symbol3) continue;
7435
+ const resolvedSymbol = typeCheckerUtils.resolveToGlobalSymbol(symbol3);
7436
+ let messageText;
7437
+ for (const [name, symbol4] of globalSymbols) {
7438
+ if (resolvedSymbol === symbol4) {
7439
+ messageText = checkInEffect ? timerAlternatives[name].inEffect : timerAlternatives[name].outsideEffect;
7440
+ break;
7180
7441
  }
7181
- ts.forEachChild(node, appendNodeToVisit);
7182
7442
  }
7183
- })
7443
+ if (!messageText) continue;
7444
+ const { inEffect } = yield* typeParser.findEnclosingScopes(node);
7445
+ if (inEffect !== checkInEffect) continue;
7446
+ report({ location: node, messageText, fixes: [] });
7447
+ }
7448
+ });
7449
+ var globalTimersInEffect = createDiagnostic({
7450
+ name: "globalTimersInEffect",
7451
+ code: 58,
7452
+ description: "Warns when using setTimeout/setInterval inside Effect generators instead of Effect.sleep/Schedule",
7453
+ group: "effectNative",
7454
+ severity: "off",
7455
+ fixable: false,
7456
+ supportedEffect: ["v3", "v4"],
7457
+ apply: makeGlobalTimersApply(true)
7458
+ });
7459
+
7460
+ // src/diagnostics/globalTimers.ts
7461
+ var globalTimers = createDiagnostic({
7462
+ name: "globalTimers",
7463
+ code: 62,
7464
+ description: "Warns when using setTimeout/setInterval outside Effect generators instead of Effect.sleep/Schedule",
7465
+ group: "effectNative",
7466
+ severity: "off",
7467
+ fixable: false,
7468
+ supportedEffect: ["v3", "v4"],
7469
+ apply: makeGlobalTimersApply(false)
7184
7470
  });
7185
7471
 
7186
7472
  // src/diagnostics/importFromBarrel.ts
@@ -8245,8 +8531,8 @@ var missingReturnYieldStar = createDiagnostic({
8245
8531
  if (!type) continue;
8246
8532
  const maybeEffect = yield* option(typeParser.effectYieldableType(type, unwrapped.expression));
8247
8533
  if (!(isSome2(maybeEffect) && maybeEffect.value.A.flags & ts.TypeFlags.Never)) continue;
8248
- const { effectGen, scopeNode } = yield* typeParser.findEnclosingScopes(node);
8249
- if (!effectGen || scopeNode && scopeNode !== effectGen.generatorFunction) continue;
8534
+ const { inEffect } = yield* typeParser.findEnclosingScopes(node);
8535
+ if (!inEffect) continue;
8250
8536
  const fix = [{
8251
8537
  fixName: "missingReturnYieldStar_fix",
8252
8538
  description: "Add return statement",
@@ -10709,11 +10995,8 @@ var preferSchemaOverJson = createDiagnostic({
10709
10995
  });
10710
10996
  const jsonMethodInEffectGen = fn("preferSchemaOverJson.jsonMethodInEffectGen")(
10711
10997
  function* (jsonCall) {
10712
- const { effectGen, scopeNode } = yield* typeParser.findEnclosingScopes(jsonCall);
10713
- if (!effectGen || effectGen.body.statements.length === 0) {
10714
- return yield* TypeParserIssue.issue;
10715
- }
10716
- if (scopeNode && scopeNode !== effectGen.generatorFunction) {
10998
+ const { inEffect } = yield* typeParser.findEnclosingScopes(jsonCall);
10999
+ if (!inEffect) {
10717
11000
  return yield* TypeParserIssue.issue;
10718
11001
  }
10719
11002
  return jsonCall;
@@ -11129,9 +11412,8 @@ var schemaSyncInEffect = createDiagnostic({
11129
11412
  option
11130
11413
  );
11131
11414
  if (isNone2(isSchemaSyncCall)) continue;
11132
- const { effectGen, scopeNode } = yield* typeParser.findEnclosingScopes(node);
11133
- if (!effectGen || effectGen.body.statements.length === 0) continue;
11134
- if (scopeNode && scopeNode !== effectGen.generatorFunction) continue;
11415
+ const { inEffect } = yield* typeParser.findEnclosingScopes(node);
11416
+ if (!inEffect) continue;
11135
11417
  const nodeText = sourceFile.text.substring(
11136
11418
  ts.getTokenPosOfNode(node.expression, sourceFile),
11137
11419
  node.expression.end
@@ -11973,6 +12255,7 @@ var diagnostics = [
11973
12255
  unnecessaryPipe,
11974
12256
  genericEffectServices,
11975
12257
  globalFetch,
12258
+ globalFetchInEffect,
11976
12259
  returnEffectInGen,
11977
12260
  tryCatchInEffectGen,
11978
12261
  importFromBarrel,
@@ -12004,7 +12287,15 @@ var diagnostics = [
12004
12287
  preferSchemaOverJson,
12005
12288
  extendsNativeError,
12006
12289
  serviceNotAsClass,
12007
- nodeBuiltinImport
12290
+ nodeBuiltinImport,
12291
+ globalDate,
12292
+ globalDateInEffect,
12293
+ globalConsole,
12294
+ globalConsoleInEffect,
12295
+ globalRandom,
12296
+ globalRandomInEffect,
12297
+ globalTimers,
12298
+ globalTimersInEffect
12008
12299
  ];
12009
12300
 
12010
12301
  // src/effect-lsp-patch-utils.ts