@effect/language-service 0.23.5 → 0.24.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@effect/language-service",
3
- "version": "0.23.5",
3
+ "version": "0.24.0",
4
4
  "description": "A Language-Service Plugin to Refactor and Diagnostic effect-ts projects",
5
5
  "main": "index.cjs",
6
6
  "repository": {
package/transform.js CHANGED
@@ -739,6 +739,7 @@ var isNonEmptyArray = (self) => self.length > 0;
739
739
 
740
740
  // node_modules/.pnpm/effect@3.16.5/node_modules/effect/dist/esm/Order.js
741
741
  var make = (compare) => (self, that) => self === that ? 0 : compare(self, that);
742
+ var string2 = /* @__PURE__ */ make((self, that) => self < that ? -1 : 1);
742
743
 
743
744
  // node_modules/.pnpm/effect@3.16.5/node_modules/effect/dist/esm/Option.js
744
745
  var none2 = () => none;
@@ -921,6 +922,7 @@ var orElse2 = (f) => (fa) => make2((ctx) => {
921
922
  if (result._tag === "Left") return f(result.value).run(ctx);
922
923
  return result;
923
924
  });
925
+ var firstSuccessOf = (arr) => arr.slice(1).reduce((arr2, fa) => orElse2(() => fa)(arr2), arr[0]);
924
926
  var service = (tag) => make2(
925
927
  (ctx) => tag.key in ctx.value ? ctx.value[tag.key] : makeInternalDefect(`Cannot find service ${tag.key}`)
926
928
  );
@@ -2027,22 +2029,16 @@ function make3(ts, typeChecker) {
2027
2029
  );
2028
2030
  const effectType = cachedBy(
2029
2031
  fn("TypeParser.effectType")(function* (type, atLocation) {
2030
- yield* pipeableType(type, atLocation);
2032
+ let result = typeParserIssue("Type has no effect variance struct", type, atLocation);
2031
2033
  const propertiesSymbols = typeChecker.getPropertiesOfType(type).filter(
2032
- (_) => _.flags & ts.SymbolFlags.Property && !(_.flags & ts.SymbolFlags.Optional)
2034
+ (_) => _.flags & ts.SymbolFlags.Property && !(_.flags & ts.SymbolFlags.Optional) && _.valueDeclaration && ts.isPropertySignature(_.valueDeclaration) && ts.isComputedPropertyName(_.valueDeclaration.name)
2033
2035
  );
2034
2036
  propertiesSymbols.sort((a, b) => b.name.indexOf("EffectTypeId") - a.name.indexOf("EffectTypeId"));
2035
2037
  for (const propertySymbol of propertiesSymbols) {
2036
2038
  const propertyType = typeChecker.getTypeOfSymbolAtLocation(propertySymbol, atLocation);
2037
- const varianceArgs = yield* option(effectVarianceStruct(
2038
- propertyType,
2039
- atLocation
2040
- ));
2041
- if (isSome2(varianceArgs)) {
2042
- return varianceArgs.value;
2043
- }
2039
+ result = pipe(result, orElse2(() => effectVarianceStruct(propertyType, atLocation)));
2044
2040
  }
2045
- return yield* typeParserIssue("Type has no effect variance struct", type, atLocation);
2041
+ return yield* result;
2046
2042
  }),
2047
2043
  "TypeParser.effectType",
2048
2044
  (type) => type
@@ -2061,7 +2057,7 @@ function make3(ts, typeChecker) {
2061
2057
  fn("TypeParser.layerType")(function* (type, atLocation) {
2062
2058
  yield* pipeableType(type, atLocation);
2063
2059
  const propertiesSymbols = typeChecker.getPropertiesOfType(type).filter(
2064
- (_) => _.flags & ts.SymbolFlags.Property && !(_.flags & ts.SymbolFlags.Optional)
2060
+ (_) => _.flags & ts.SymbolFlags.Property && !(_.flags & ts.SymbolFlags.Optional) && _.valueDeclaration && ts.isPropertySignature(_.valueDeclaration) && ts.isComputedPropertyName(_.valueDeclaration.name)
2065
2061
  );
2066
2062
  propertiesSymbols.sort((a, b) => b.name.indexOf("LayerTypeId") - a.name.indexOf("LayerTypeId"));
2067
2063
  for (const propertySymbol of propertiesSymbols) {
@@ -2345,7 +2341,7 @@ function make3(ts, typeChecker) {
2345
2341
  const ast = typeChecker.getPropertyOfType(type, "ast");
2346
2342
  if (!ast) return yield* typeParserIssue("Has no 'ast' property", type, atLocation);
2347
2343
  const propertiesSymbols = typeChecker.getPropertiesOfType(type).filter(
2348
- (_) => _.flags & ts.SymbolFlags.Property && !(_.flags & ts.SymbolFlags.Optional)
2344
+ (_) => _.flags & ts.SymbolFlags.Property && !(_.flags & ts.SymbolFlags.Optional) && _.valueDeclaration && ts.isPropertySignature(_.valueDeclaration) && ts.isComputedPropertyName(_.valueDeclaration.name)
2349
2345
  );
2350
2346
  propertiesSymbols.sort((a, b) => b.name.indexOf("TypeId") - a.name.indexOf("TypeId"));
2351
2347
  for (const propertySymbol of propertiesSymbols) {
@@ -2374,7 +2370,7 @@ function make3(ts, typeChecker) {
2374
2370
  fn("TypeParser.contextTag")(function* (type, atLocation) {
2375
2371
  yield* pipeableType(type, atLocation);
2376
2372
  const propertiesSymbols = typeChecker.getPropertiesOfType(type).filter(
2377
- (_) => _.flags & ts.SymbolFlags.Property && !(_.flags & ts.SymbolFlags.Optional)
2373
+ (_) => _.flags & ts.SymbolFlags.Property && !(_.flags & ts.SymbolFlags.Optional) && _.valueDeclaration && ts.isPropertySignature(_.valueDeclaration) && ts.isComputedPropertyName(_.valueDeclaration.name)
2378
2374
  );
2379
2375
  propertiesSymbols.sort((a, b) => b.name.indexOf("TypeId") - a.name.indexOf("TypeId"));
2380
2376
  for (const propertySymbol of propertiesSymbols) {
@@ -2406,6 +2402,25 @@ function make3(ts, typeChecker) {
2406
2402
  "TypeParser.pipeCall",
2407
2403
  (node) => node
2408
2404
  );
2405
+ const scopeType = cachedBy(
2406
+ fn("TypeParser.scopeType")(function* (type, atLocation) {
2407
+ yield* pipeableType(type, atLocation);
2408
+ const propertiesSymbols = typeChecker.getPropertiesOfType(type).filter(
2409
+ (_) => _.flags & ts.SymbolFlags.Property && !(_.flags & ts.SymbolFlags.Optional) && _.valueDeclaration && ts.isPropertySignature(_.valueDeclaration) && ts.isComputedPropertyName(_.valueDeclaration.name)
2410
+ );
2411
+ propertiesSymbols.sort((a, b) => b.name.indexOf("ScopeTypeId") - a.name.indexOf("ScopeTypeId"));
2412
+ for (const propertySymbol of propertiesSymbols) {
2413
+ const computedPropertyExpression = propertySymbol.valueDeclaration.name;
2414
+ const symbol3 = typeChecker.getSymbolAtLocation(computedPropertyExpression.expression);
2415
+ if (symbol3 && symbol3.name === "ScopeTypeId") {
2416
+ return type;
2417
+ }
2418
+ }
2419
+ return yield* typeParserIssue("Type has no scope type id", type, atLocation);
2420
+ }),
2421
+ "TypeParser.scopeType",
2422
+ (type) => type
2423
+ );
2409
2424
  return {
2410
2425
  effectType,
2411
2426
  strictEffectType,
@@ -2419,7 +2434,8 @@ function make3(ts, typeChecker) {
2419
2434
  unnecessaryEffectGen: unnecessaryEffectGen2,
2420
2435
  effectSchemaType,
2421
2436
  contextTag,
2422
- pipeCall
2437
+ pipeCall,
2438
+ scopeType
2423
2439
  };
2424
2440
  }
2425
2441
 
@@ -2860,18 +2876,39 @@ var missingEffectError = createDiagnostic({
2860
2876
  code: 1,
2861
2877
  severity: "error",
2862
2878
  apply: fn("missingEffectError.apply")(function* (sourceFile, report) {
2879
+ const ts = yield* service(TypeScriptApi);
2863
2880
  const typeChecker = yield* service(TypeCheckerApi);
2864
2881
  const typeParser = yield* service(TypeParser);
2865
2882
  const typeOrder = yield* deterministicTypeOrder;
2883
+ const effectModuleIdentifier = yield* pipe(
2884
+ findImportedModuleIdentifierByPackageAndNameOrBarrel(
2885
+ sourceFile,
2886
+ "effect",
2887
+ "Effect"
2888
+ ),
2889
+ map3((_) => _.text),
2890
+ orElse2(() => succeed("Effect"))
2891
+ );
2892
+ const createDieMessage = (message) => ts.factory.createCallExpression(
2893
+ ts.factory.createPropertyAccessExpression(
2894
+ ts.factory.createIdentifier(effectModuleIdentifier),
2895
+ "dieMessage"
2896
+ ),
2897
+ void 0,
2898
+ [ts.factory.createStringLiteral(message)]
2899
+ );
2866
2900
  const checkForMissingErrorTypes = (node, expectedType, valueNode, realType) => pipe(
2867
2901
  all(
2868
2902
  typeParser.effectType(expectedType, node),
2869
2903
  typeParser.effectType(realType, valueNode)
2870
2904
  ),
2871
2905
  flatMap2(
2872
- ([expectedEffect, realEffect]) => getMissingTypeEntriesInTargetType(
2873
- realEffect.E,
2874
- expectedEffect.E
2906
+ ([expectedEffect, realEffect]) => pipe(
2907
+ getMissingTypeEntriesInTargetType(
2908
+ realEffect.E,
2909
+ expectedEffect.E
2910
+ ),
2911
+ map3((missingErrorTypes) => ({ missingErrorTypes, expectedErrorType: expectedEffect.E }))
2875
2912
  )
2876
2913
  )
2877
2914
  );
@@ -2886,15 +2923,79 @@ var missingEffectError = createDiagnostic({
2886
2923
  valueNode,
2887
2924
  realType
2888
2925
  ),
2889
- map3(
2890
- (missingTypes) => missingTypes.length > 0 ? report(
2926
+ map3((result) => {
2927
+ if (result.missingErrorTypes.length === 0) return;
2928
+ const fixes = [];
2929
+ if (ts.isExpression(valueNode) && result.expectedErrorType.flags & ts.TypeFlags.Never) {
2930
+ fixes.push({
2931
+ fixName: "missingEffectError_catchAll",
2932
+ description: "Catch all errors with Effect.catchAll",
2933
+ apply: gen(function* () {
2934
+ const changeTracker = yield* service(ChangeTracker);
2935
+ changeTracker.insertText(sourceFile, valueNode.getStart(), effectModuleIdentifier + ".catchAll(");
2936
+ changeTracker.insertText(sourceFile, valueNode.getEnd(), ", () => ");
2937
+ changeTracker.insertNodeAt(
2938
+ sourceFile,
2939
+ valueNode.getEnd(),
2940
+ createDieMessage("TODO: catchAll not implemented")
2941
+ );
2942
+ changeTracker.insertText(sourceFile, valueNode.getEnd(), ")");
2943
+ })
2944
+ });
2945
+ }
2946
+ if (ts.isExpression(valueNode)) {
2947
+ const propertyAssignments = pipe(
2948
+ result.missingErrorTypes,
2949
+ map2((_) => typeChecker.getPropertyOfType(_, "_tag")),
2950
+ filter((_) => !!_),
2951
+ map2((_) => typeChecker.getTypeOfSymbolAtLocation(_, valueNode)),
2952
+ filter((_) => !!(_.flags & ts.TypeFlags.Literal)),
2953
+ map2((_) => typeChecker.typeToTypeNode(_, void 0, ts.NodeBuilderFlags.NoTruncation)),
2954
+ filter((_) => !!_ && ts.isLiteralTypeNode(_)),
2955
+ map2((_) => _.literal),
2956
+ filter((_) => ts.isLiteralExpression(_)),
2957
+ map2((_) => _.text),
2958
+ sort(string2),
2959
+ map2(
2960
+ (_) => ts.factory.createPropertyAssignment(
2961
+ ts.factory.createIdentifier(_),
2962
+ ts.factory.createArrowFunction(
2963
+ void 0,
2964
+ void 0,
2965
+ [],
2966
+ void 0,
2967
+ void 0,
2968
+ createDieMessage(`TODO: catchTags() not implemented for ${_}`)
2969
+ )
2970
+ )
2971
+ )
2972
+ );
2973
+ if (propertyAssignments.length === result.missingErrorTypes.length) {
2974
+ fixes.push({
2975
+ fixName: "missingEffectError_tagged",
2976
+ description: "Catch unexpected errors with Effect.catchTag",
2977
+ apply: gen(function* () {
2978
+ const changeTracker = yield* service(ChangeTracker);
2979
+ changeTracker.insertText(sourceFile, valueNode.getStart(), effectModuleIdentifier + ".catchTags(");
2980
+ changeTracker.insertText(sourceFile, valueNode.getEnd(), ", ");
2981
+ changeTracker.insertNodeAt(
2982
+ sourceFile,
2983
+ valueNode.getEnd(),
2984
+ ts.factory.createObjectLiteralExpression(propertyAssignments)
2985
+ );
2986
+ changeTracker.insertText(sourceFile, valueNode.getEnd(), ")");
2987
+ })
2988
+ });
2989
+ }
2990
+ }
2991
+ report(
2891
2992
  {
2892
2993
  node,
2893
- messageText: `Missing '${sortTypes(missingTypes).map((_) => typeChecker.typeToString(_)).join(" | ")}' in the expected Effect errors.`,
2894
- fixes: []
2994
+ messageText: `Missing '${sortTypes(result.missingErrorTypes).map((_) => typeChecker.typeToString(_)).join(" | ")}' in the expected Effect errors.`,
2995
+ fixes
2895
2996
  }
2896
- ) : void 0
2897
- ),
2997
+ );
2998
+ }),
2898
2999
  ignore
2899
3000
  );
2900
3001
  }
@@ -3106,6 +3207,108 @@ Nested Effect-able types may be intended if you plan to later manually flatten o
3106
3207
  })
3107
3208
  });
3108
3209
 
3210
+ // src/diagnostics/scopeInLayerEffect.ts
3211
+ var scopeInLayerEffect = createDiagnostic({
3212
+ name: "scopeInLayerEffect",
3213
+ code: 13,
3214
+ severity: "warning",
3215
+ apply: fn("scopeInLayerEffect.apply")(function* (sourceFile, report) {
3216
+ const ts = yield* service(TypeScriptApi);
3217
+ const typeChecker = yield* service(TypeCheckerApi);
3218
+ const typeParser = yield* service(TypeParser);
3219
+ const layerModuleIdentifier = yield* pipe(
3220
+ findImportedModuleIdentifierByPackageAndNameOrBarrel(
3221
+ sourceFile,
3222
+ "effect",
3223
+ "Layer"
3224
+ ),
3225
+ map3((_) => _.text),
3226
+ orElse2(() => succeed("Layer"))
3227
+ );
3228
+ function parseLayerEffectApiCall(node) {
3229
+ if (!ts.isCallExpression(node)) return;
3230
+ const expression = node.expression;
3231
+ if (!ts.isPropertyAccessExpression(expression)) return;
3232
+ const calledModule = expression.expression;
3233
+ if (!(ts.isIdentifier(calledModule) && calledModule.text === layerModuleIdentifier)) return;
3234
+ const methodIdentifier = expression.name;
3235
+ if (!(ts.isIdentifier(methodIdentifier) && methodIdentifier.text.toLowerCase().startsWith("effect"))) return;
3236
+ return { methodIdentifier };
3237
+ }
3238
+ const reportIfLayerRequireScope = (type, node, methodIdentifier) => {
3239
+ let toCheck = [type];
3240
+ const entries = [];
3241
+ while (toCheck.length > 0) {
3242
+ const type2 = toCheck.pop();
3243
+ if (type2.isUnion()) {
3244
+ toCheck = toCheck.concat(type2.types);
3245
+ } else {
3246
+ entries.push(type2);
3247
+ }
3248
+ }
3249
+ return pipe(
3250
+ firstSuccessOf(entries.map((type2) => typeParser.scopeType(type2, node))),
3251
+ map3(
3252
+ () => report({
3253
+ node,
3254
+ messageText: `Seems like you are constructing a layer with a scope in the requirements.
3255
+ Consider using "scoped" instead to get ride of the scope in the requirements.`,
3256
+ fixes: methodIdentifier ? [{
3257
+ fixName: "scopeInLayerEffect_scoped",
3258
+ description: "Use scoped for Layer creation",
3259
+ apply: gen(function* () {
3260
+ const changeTracker = yield* service(ChangeTracker);
3261
+ changeTracker.replaceNode(
3262
+ sourceFile,
3263
+ methodIdentifier,
3264
+ ts.factory.createIdentifier("scoped")
3265
+ );
3266
+ })
3267
+ }] : []
3268
+ })
3269
+ ),
3270
+ ignore
3271
+ );
3272
+ };
3273
+ const nodeToVisit = [];
3274
+ const appendNodeToVisit = (node) => {
3275
+ nodeToVisit.push(node);
3276
+ return void 0;
3277
+ };
3278
+ ts.forEachChild(sourceFile, appendNodeToVisit);
3279
+ while (nodeToVisit.length > 0) {
3280
+ const node = nodeToVisit.shift();
3281
+ const layerEffectApiCall = parseLayerEffectApiCall(node);
3282
+ if (layerEffectApiCall) {
3283
+ const type = typeChecker.getTypeAtLocation(node);
3284
+ yield* pipe(
3285
+ typeParser.layerType(type, node),
3286
+ flatMap2(({ RIn }) => reportIfLayerRequireScope(RIn, node, layerEffectApiCall.methodIdentifier)),
3287
+ ignore
3288
+ );
3289
+ continue;
3290
+ }
3291
+ if (ts.isClassDeclaration(node) && node.name && node.heritageClauses) {
3292
+ const classSym = typeChecker.getSymbolAtLocation(node.name);
3293
+ if (classSym) {
3294
+ const classType = typeChecker.getTypeOfSymbol(classSym);
3295
+ const defaultLayer = typeChecker.getPropertyOfType(classType, "Default");
3296
+ if (defaultLayer) {
3297
+ const type = typeChecker.getTypeOfSymbolAtLocation(defaultLayer, node);
3298
+ yield* pipe(
3299
+ typeParser.layerType(type, node),
3300
+ flatMap2(({ RIn }) => reportIfLayerRequireScope(RIn, node, void 0)),
3301
+ ignore
3302
+ );
3303
+ continue;
3304
+ }
3305
+ }
3306
+ }
3307
+ ts.forEachChild(node, appendNodeToVisit);
3308
+ }
3309
+ })
3310
+ });
3311
+
3109
3312
  // src/diagnostics/unnecessaryEffectGen.ts
3110
3313
  var unnecessaryEffectGen = createDiagnostic({
3111
3314
  name: "unnecessaryEffectGen",
@@ -3207,7 +3410,8 @@ var diagnostics = [
3207
3410
  unnecessaryPipe,
3208
3411
  genericEffectServices,
3209
3412
  returnEffectInGen,
3210
- importFromBarrel
3413
+ importFromBarrel,
3414
+ scopeInLayerEffect
3211
3415
  ];
3212
3416
 
3213
3417
  // src/transform.ts