@effect/language-service 0.45.1 → 0.46.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.
package/README.md CHANGED
@@ -63,6 +63,7 @@ And you're done! You'll now be able to use a set of refactors and diagnostics th
63
63
  - Warn on missing service dependencies in `Effect.Service` declarations
64
64
  - Warn when `Effect.Service` is used with a primitive type instead of an object type
65
65
  - Warn when schema classes override the default constructor behavior
66
+ - Warn when `@effect-diagnostics-next-line` comments have no effect (i.e., they don't suppress any diagnostic)
66
67
 
67
68
  ### Completions
68
69
 
@@ -110,6 +111,7 @@ Few options can be provided alongside the initialization of the Language Service
110
111
  "floatingEffect": "warning" // example for a rule, allowed values are off,error,warning,message,suggestion
111
112
  },
112
113
  "diagnosticsName": true, // controls whether to include the rule name in diagnostic messages (default: true)
114
+ "missingDiagnosticNextLine": "warning", // controls the severity of warnings for unused @effect-diagnostics-next-line comments (default: "warning", allowed values: off,error,warning,message,suggestion)
113
115
  "quickinfo": true, // controls Effect quickinfo (default: true)
114
116
  "quickinfoEffectParameters": "whenTruncated", // (default: "whenTruncated") controls when to display effect type parameters always,never,whenTruncated
115
117
  "quickinfoMaximumLength": -1, // controls how long can be the types in the quickinfo hover (helps with very long type to improve perfs, defaults to -1 for no truncation, can be any number eg. 1000 and TS will try to fit as much as possible in that budget, higher number means more info.)
@@ -1155,6 +1155,9 @@ var all = fn("all")(
1155
1155
 
1156
1156
  // src/core/LanguageServicePluginOptions.ts
1157
1157
  var LanguageServicePluginOptions = Tag("PluginOptions");
1158
+ function isValidSeverityLevel(value) {
1159
+ return value === "off" || value === "error" || value === "warning" || value === "message" || value === "suggestion";
1160
+ }
1158
1161
  function parseDiagnosticSeverity(config) {
1159
1162
  if (!isRecord(config)) return {};
1160
1163
  return Object.fromEntries(
@@ -1162,9 +1165,7 @@ function parseDiagnosticSeverity(config) {
1162
1165
  Object.entries(config),
1163
1166
  filter(([key, value]) => isString(key) && isString(value)),
1164
1167
  map3(([key, value]) => [String(key).toLowerCase(), String(value).toLowerCase()]),
1165
- filter(
1166
- ([_, value]) => value === "off" || value === "error" || value === "warning" || value === "message" || value === "suggestion"
1167
- )
1168
+ filter(([_, value]) => isValidSeverityLevel(value))
1168
1169
  )
1169
1170
  );
1170
1171
  }
@@ -1173,6 +1174,7 @@ var defaults = {
1173
1174
  diagnostics: true,
1174
1175
  diagnosticSeverity: {},
1175
1176
  diagnosticsName: true,
1177
+ missingDiagnosticNextLine: "warning",
1176
1178
  quickinfo: true,
1177
1179
  quickinfoEffectParameters: "whentruncated",
1178
1180
  quickinfoMaximumLength: -1,
@@ -1215,6 +1217,7 @@ function parse(config) {
1215
1217
  diagnostics: isObject(config) && hasProperty(config, "diagnostics") && isBoolean(config.diagnostics) ? config.diagnostics : defaults.diagnostics,
1216
1218
  diagnosticSeverity: isObject(config) && hasProperty(config, "diagnosticSeverity") && isRecord(config.diagnosticSeverity) ? parseDiagnosticSeverity(config.diagnosticSeverity) : defaults.diagnosticSeverity,
1217
1219
  diagnosticsName: isObject(config) && hasProperty(config, "diagnosticsName") && isBoolean(config.diagnosticsName) ? config.diagnosticsName : defaults.diagnosticsName,
1220
+ missingDiagnosticNextLine: isObject(config) && hasProperty(config, "missingDiagnosticNextLine") && isString(config.missingDiagnosticNextLine) && isValidSeverityLevel(config.missingDiagnosticNextLine) ? config.missingDiagnosticNextLine : defaults.missingDiagnosticNextLine,
1218
1221
  quickinfo: isObject(config) && hasProperty(config, "quickinfo") && isBoolean(config.quickinfo) ? config.quickinfo : defaults.quickinfo,
1219
1222
  quickinfoEffectParameters: isObject(config) && hasProperty(config, "quickinfoEffectParameters") && isString(config.quickinfoEffectParameters) && ["always", "never", "whentruncated"].includes(config.quickinfoEffectParameters.toLowerCase()) ? config.quickinfoEffectParameters.toLowerCase() : defaults.quickinfoEffectParameters,
1220
1223
  quickinfoMaximumLength: isObject(config) && hasProperty(config, "quickinfoMaximumLength") && isNumber(config.quickinfoMaximumLength) ? config.quickinfoMaximumLength : defaults.quickinfoMaximumLength,
@@ -1888,7 +1891,8 @@ var createDiagnosticExecutor = fn("LSP.createCommentDirectivesProcessor")(
1888
1891
  lineOverrides[ruleName].unshift({
1889
1892
  pos: foundNode.node.pos,
1890
1893
  end: foundNode.node.end,
1891
- level: ruleLevel
1894
+ level: ruleLevel,
1895
+ commentRange: foundNode.commentRange
1892
1896
  });
1893
1897
  }
1894
1898
  } else {
@@ -1961,6 +1965,7 @@ var createDiagnosticExecutor = fn("LSP.createCommentDirectivesProcessor")(
1961
1965
  fixes: entry.fixes.concat(node ? [fixByDisableNextLine(node)] : []).concat([fixByDisableEntireFile])
1962
1966
  });
1963
1967
  });
1968
+ const unusedLineOverrides = new Set(lineOverrides[ruleNameLowered] || []);
1964
1969
  for (const emitted of applicableDiagnostics.slice(0)) {
1965
1970
  let newLevel = defaultLevel;
1966
1971
  const lineOverride = (lineOverrides[ruleNameLowered] || []).find(
@@ -1968,6 +1973,7 @@ var createDiagnosticExecutor = fn("LSP.createCommentDirectivesProcessor")(
1968
1973
  );
1969
1974
  if (lineOverride) {
1970
1975
  newLevel = lineOverride.level;
1976
+ unusedLineOverrides.delete(lineOverride);
1971
1977
  } else {
1972
1978
  const sectionOverride = (sectionOverrides[ruleNameLowered] || []).find((_) => _.pos < emitted.range.pos);
1973
1979
  if (sectionOverride) newLevel = sectionOverride.level;
@@ -1991,6 +1997,19 @@ var createDiagnosticExecutor = fn("LSP.createCommentDirectivesProcessor")(
1991
1997
  });
1992
1998
  }
1993
1999
  }
2000
+ if (pluginOptions.missingDiagnosticNextLine !== "off" && unusedLineOverrides.size > 0) {
2001
+ for (const unusedLineOverride of unusedLineOverrides) {
2002
+ diagnostics2.push({
2003
+ file: sourceFile,
2004
+ start: unusedLineOverride.commentRange.pos,
2005
+ length: unusedLineOverride.commentRange.end - unusedLineOverride.commentRange.pos,
2006
+ messageText: `@effect-diagnostics-next-line ${rule.name}:${unusedLineOverride.level} has no effect, make sure you are suppressing the right rule.`,
2007
+ category: levelToDiagnosticCategory[pluginOptions.missingDiagnosticNextLine],
2008
+ code: -1,
2009
+ source: "effect"
2010
+ });
2011
+ }
2012
+ }
1994
2013
  return { diagnostics: diagnostics2, codeFixes };
1995
2014
  });
1996
2015
  return { execute };
@@ -4044,6 +4063,7 @@ var leakingRequirements = createDiagnostic({
4044
4063
  const typeChecker = yield* service(TypeCheckerApi);
4045
4064
  const typeCheckerUtils = yield* service(TypeCheckerUtils);
4046
4065
  const typeParser = yield* service(TypeParser);
4066
+ const tsUtils = yield* service(TypeScriptUtils);
4047
4067
  const parseLeakedRequirements = cachedBy(
4048
4068
  fn("leakingServices.checkServiceLeaking")(
4049
4069
  function* (service2, atLocation) {
@@ -4101,7 +4121,21 @@ var leakingRequirements = createDiagnostic({
4101
4121
  }
4102
4122
  }
4103
4123
  if (sharedRequirementsKeys && sharedRequirementsKeys.length > 0 && effectMembers >= 2) {
4104
- return sharedRequirementsKeys.map((key) => memory.get(key));
4124
+ return sharedRequirementsKeys.map((key) => memory.get(key)).filter(
4125
+ (type) => {
4126
+ let symbol3 = type.symbol;
4127
+ if (symbol3 && symbol3.flags & ts.SymbolFlags.Alias) {
4128
+ symbol3 = typeChecker.getAliasedSymbol(symbol3);
4129
+ }
4130
+ return !(symbol3.declarations || []).some((declaration) => {
4131
+ const declarationSource = tsUtils.getSourceFileOfNode(declaration);
4132
+ if (!declarationSource) return false;
4133
+ return declarationSource.text.substring(declaration.pos, declaration.end).toLowerCase().indexOf(
4134
+ "@effect-leakable-service"
4135
+ ) > -1;
4136
+ });
4137
+ }
4138
+ );
4105
4139
  }
4106
4140
  return [];
4107
4141
  }
@@ -4114,7 +4148,7 @@ var leakingRequirements = createDiagnostic({
4114
4148
  report({
4115
4149
  location: node,
4116
4150
  messageText: `This Service is leaking the ${requirements.map((_) => typeChecker.typeToString(_)).join(" | ")} requirement.
4117
- If these requirements cannot be cached and are expected to be provided per method invocation (e.g. HttpServerRequest), you can safely disable this diagnostic for this line through quickfixes.
4151
+ If these requirements cannot be cached and are expected to be provided per method invocation (e.g. HttpServerRequest), you can either safely disable this diagnostic for this line through quickfixes or mark the service declaration with a JSDoc @effect-leakable-service.
4118
4152
  More info at https://effect.website/docs/requirements-management/layers/#avoiding-requirement-leakage`,
4119
4153
  fixes: []
4120
4154
  });