@effect/language-service 0.84.2 → 0.85.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 +11 -0
- package/cli.js +945 -270
- package/cli.js.map +1 -1
- package/effect-lsp-patch-utils.js +529 -103
- package/effect-lsp-patch-utils.js.map +1 -1
- package/index.js +539 -113
- package/index.js.map +1 -1
- package/package.json +1 -1
- package/schema.json +132 -0
- package/transform.js +529 -103
- package/transform.js.map +1 -1
|
@@ -27,7 +27,7 @@ __export(effect_lsp_patch_utils_exports, {
|
|
|
27
27
|
});
|
|
28
28
|
module.exports = __toCommonJS(effect_lsp_patch_utils_exports);
|
|
29
29
|
|
|
30
|
-
// ../../node_modules/.pnpm/effect@4.0.0-beta.
|
|
30
|
+
// ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/Pipeable.js
|
|
31
31
|
var pipeArguments = (self, args3) => {
|
|
32
32
|
switch (args3.length) {
|
|
33
33
|
case 0:
|
|
@@ -60,7 +60,7 @@ var pipeArguments = (self, args3) => {
|
|
|
60
60
|
}
|
|
61
61
|
};
|
|
62
62
|
|
|
63
|
-
// ../../node_modules/.pnpm/effect@4.0.0-beta.
|
|
63
|
+
// ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/Function.js
|
|
64
64
|
var dual = function(arity, body) {
|
|
65
65
|
if (typeof arity === "function") {
|
|
66
66
|
return function() {
|
|
@@ -108,7 +108,7 @@ function pipe(a, ...args3) {
|
|
|
108
108
|
return pipeArguments(a, args3);
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
-
// ../../node_modules/.pnpm/effect@4.0.0-beta.
|
|
111
|
+
// ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/internal/equal.js
|
|
112
112
|
var getAllObjectKeys = (obj) => {
|
|
113
113
|
const keys2 = new Set(Reflect.ownKeys(obj));
|
|
114
114
|
if (obj.constructor === Object) return keys2;
|
|
@@ -131,7 +131,7 @@ var getAllObjectKeys = (obj) => {
|
|
|
131
131
|
};
|
|
132
132
|
var byReferenceInstances = /* @__PURE__ */ new WeakSet();
|
|
133
133
|
|
|
134
|
-
// ../../node_modules/.pnpm/effect@4.0.0-beta.
|
|
134
|
+
// ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/Predicate.js
|
|
135
135
|
function isString(input) {
|
|
136
136
|
return typeof input === "string";
|
|
137
137
|
}
|
|
@@ -152,7 +152,7 @@ function isObjectKeyword(input) {
|
|
|
152
152
|
}
|
|
153
153
|
var hasProperty = /* @__PURE__ */ dual(2, (self, property) => isObjectKeyword(self) && property in self);
|
|
154
154
|
|
|
155
|
-
// ../../node_modules/.pnpm/effect@4.0.0-beta.
|
|
155
|
+
// ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/Hash.js
|
|
156
156
|
var symbol = "~effect/interfaces/Hash";
|
|
157
157
|
var hash = (self) => {
|
|
158
158
|
switch (typeof self) {
|
|
@@ -271,7 +271,7 @@ function withVisitedTracking(obj, fn2) {
|
|
|
271
271
|
return result;
|
|
272
272
|
}
|
|
273
273
|
|
|
274
|
-
// ../../node_modules/.pnpm/effect@4.0.0-beta.
|
|
274
|
+
// ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/Equal.js
|
|
275
275
|
var symbol2 = "~effect/interfaces/Equal";
|
|
276
276
|
function equals() {
|
|
277
277
|
if (arguments.length === 1) {
|
|
@@ -433,10 +433,10 @@ var compareSets = /* @__PURE__ */ makeCompareSet(compareBoth);
|
|
|
433
433
|
var isEqual = (u) => hasProperty(u, symbol2);
|
|
434
434
|
var asEquivalence = () => equals;
|
|
435
435
|
|
|
436
|
-
// ../../node_modules/.pnpm/effect@4.0.0-beta.
|
|
436
|
+
// ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/internal/array.js
|
|
437
437
|
var isArrayNonEmpty = (self) => self.length > 0;
|
|
438
438
|
|
|
439
|
-
// ../../node_modules/.pnpm/effect@4.0.0-beta.
|
|
439
|
+
// ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/Redactable.js
|
|
440
440
|
var symbolRedactable = /* @__PURE__ */ Symbol.for("~effect/Inspectable/redactable");
|
|
441
441
|
var isRedactable = (u) => hasProperty(u, symbolRedactable);
|
|
442
442
|
function redact(u) {
|
|
@@ -455,7 +455,7 @@ var emptyServiceMap = {
|
|
|
455
455
|
}
|
|
456
456
|
};
|
|
457
457
|
|
|
458
|
-
// ../../node_modules/.pnpm/effect@4.0.0-beta.
|
|
458
|
+
// ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/Formatter.js
|
|
459
459
|
function format(input, options) {
|
|
460
460
|
const space = options?.space ?? 0;
|
|
461
461
|
const seen = /* @__PURE__ */ new WeakSet();
|
|
@@ -534,7 +534,7 @@ function safeToString(input) {
|
|
|
534
534
|
}
|
|
535
535
|
}
|
|
536
536
|
|
|
537
|
-
// ../../node_modules/.pnpm/effect@4.0.0-beta.
|
|
537
|
+
// ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/Inspectable.js
|
|
538
538
|
var NodeInspectSymbol = /* @__PURE__ */ Symbol.for("nodejs.util.inspect.custom");
|
|
539
539
|
var toJson = (input) => {
|
|
540
540
|
try {
|
|
@@ -578,7 +578,7 @@ var Class = class {
|
|
|
578
578
|
}
|
|
579
579
|
};
|
|
580
580
|
|
|
581
|
-
// ../../node_modules/.pnpm/effect@4.0.0-beta.
|
|
581
|
+
// ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/Utils.js
|
|
582
582
|
var SingleShotGen = class _SingleShotGen {
|
|
583
583
|
called = false;
|
|
584
584
|
self;
|
|
@@ -621,7 +621,7 @@ var forced = {
|
|
|
621
621
|
var isNotOptimizedAway = /* @__PURE__ */ standard[InternalTypeId](() => new Error().stack)?.includes(InternalTypeId) === true;
|
|
622
622
|
var internalCall = isNotOptimizedAway ? standard[InternalTypeId] : forced[InternalTypeId];
|
|
623
623
|
|
|
624
|
-
// ../../node_modules/.pnpm/effect@4.0.0-beta.
|
|
624
|
+
// ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/internal/core.js
|
|
625
625
|
var EffectTypeId = `~effect/Effect`;
|
|
626
626
|
var ExitTypeId = `~effect/Exit`;
|
|
627
627
|
var effectVariance = {
|
|
@@ -968,7 +968,7 @@ var DoneVoid = {
|
|
|
968
968
|
value: void 0
|
|
969
969
|
};
|
|
970
970
|
|
|
971
|
-
// ../../node_modules/.pnpm/effect@4.0.0-beta.
|
|
971
|
+
// ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/internal/option.js
|
|
972
972
|
var TypeId = "~effect/data/Option";
|
|
973
973
|
var CommonProto = {
|
|
974
974
|
[TypeId]: {
|
|
@@ -1039,7 +1039,7 @@ var some = (value) => {
|
|
|
1039
1039
|
return a;
|
|
1040
1040
|
};
|
|
1041
1041
|
|
|
1042
|
-
// ../../node_modules/.pnpm/effect@4.0.0-beta.
|
|
1042
|
+
// ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/internal/result.js
|
|
1043
1043
|
var TypeId2 = "~effect/data/Result";
|
|
1044
1044
|
var CommonProto2 = {
|
|
1045
1045
|
[TypeId2]: {
|
|
@@ -1110,13 +1110,13 @@ var succeed = (success) => {
|
|
|
1110
1110
|
return a;
|
|
1111
1111
|
};
|
|
1112
1112
|
|
|
1113
|
-
// ../../node_modules/.pnpm/effect@4.0.0-beta.
|
|
1113
|
+
// ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/Order.js
|
|
1114
1114
|
function make(compare) {
|
|
1115
1115
|
return (self, that) => self === that ? 0 : compare(self, that);
|
|
1116
1116
|
}
|
|
1117
1117
|
var String2 = /* @__PURE__ */ make((self, that) => self < that ? -1 : 1);
|
|
1118
1118
|
|
|
1119
|
-
// ../../node_modules/.pnpm/effect@4.0.0-beta.
|
|
1119
|
+
// ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/Option.js
|
|
1120
1120
|
var none2 = () => none;
|
|
1121
1121
|
var some2 = some;
|
|
1122
1122
|
var isNone2 = isNone;
|
|
@@ -1126,7 +1126,7 @@ var orElse = /* @__PURE__ */ dual(2, (self, that) => isNone2(self) ? that() : se
|
|
|
1126
1126
|
var fromNullishOr = (a) => a == null ? none2() : some2(a);
|
|
1127
1127
|
var getOrUndefined = /* @__PURE__ */ getOrElse(constUndefined);
|
|
1128
1128
|
|
|
1129
|
-
// ../../node_modules/.pnpm/effect@4.0.0-beta.
|
|
1129
|
+
// ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/Result.js
|
|
1130
1130
|
var succeed2 = succeed;
|
|
1131
1131
|
var fail2 = fail;
|
|
1132
1132
|
var isFailure2 = isFailure;
|
|
@@ -1134,7 +1134,7 @@ var isSuccess2 = isSuccess;
|
|
|
1134
1134
|
var map = /* @__PURE__ */ dual(2, (self, f) => isSuccess2(self) ? succeed2(f(self.success)) : fail2(self.failure));
|
|
1135
1135
|
var getOrElse2 = /* @__PURE__ */ dual(2, (self, onFailure) => isFailure2(self) ? onFailure(self.failure) : self.success);
|
|
1136
1136
|
|
|
1137
|
-
// ../../node_modules/.pnpm/effect@4.0.0-beta.
|
|
1137
|
+
// ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/Record.js
|
|
1138
1138
|
var map2 = /* @__PURE__ */ dual(2, (self, f) => {
|
|
1139
1139
|
const out = {
|
|
1140
1140
|
...self
|
|
@@ -1146,7 +1146,7 @@ var map2 = /* @__PURE__ */ dual(2, (self, f) => {
|
|
|
1146
1146
|
});
|
|
1147
1147
|
var keys = (self) => Object.keys(self);
|
|
1148
1148
|
|
|
1149
|
-
// ../../node_modules/.pnpm/effect@4.0.0-beta.
|
|
1149
|
+
// ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/Array.js
|
|
1150
1150
|
var Array2 = globalThis.Array;
|
|
1151
1151
|
var fromIterable = (collection) => Array2.isArray(collection) ? collection : Array2.from(collection);
|
|
1152
1152
|
var append = /* @__PURE__ */ dual(2, (self, last) => [...self, last]);
|
|
@@ -3481,6 +3481,14 @@ function make2(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
|
|
|
3481
3481
|
),
|
|
3482
3482
|
([A, E, R]) => ({ A, E, R })
|
|
3483
3483
|
);
|
|
3484
|
+
const streamVarianceStruct = (type, atLocation) => map4(
|
|
3485
|
+
all(
|
|
3486
|
+
varianceStructCovariantType(type, atLocation, "_A"),
|
|
3487
|
+
varianceStructCovariantType(type, atLocation, "_E"),
|
|
3488
|
+
varianceStructCovariantType(type, atLocation, "_R")
|
|
3489
|
+
),
|
|
3490
|
+
([A, E, R]) => ({ A, E, R })
|
|
3491
|
+
);
|
|
3484
3492
|
const layerVarianceStruct = (type, atLocation) => map4(
|
|
3485
3493
|
all(
|
|
3486
3494
|
varianceStructContravariantType(type, atLocation, "_ROut"),
|
|
@@ -3527,6 +3535,21 @@ function make2(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
|
|
|
3527
3535
|
"TypeParser.strictEffectType",
|
|
3528
3536
|
(type) => type
|
|
3529
3537
|
);
|
|
3538
|
+
const streamType = cachedBy(
|
|
3539
|
+
fn("TypeParser.streamType")(function* (type, atLocation) {
|
|
3540
|
+
if (supportedEffect() === "v3") {
|
|
3541
|
+
return yield* effectType(type, atLocation);
|
|
3542
|
+
}
|
|
3543
|
+
const typeIdSymbol = typeChecker.getPropertyOfType(type, "~effect/Stream");
|
|
3544
|
+
if (!typeIdSymbol) {
|
|
3545
|
+
return yield* typeParserIssue("Type is not a stream", type, atLocation);
|
|
3546
|
+
}
|
|
3547
|
+
const typeIdType = typeChecker.getTypeOfSymbolAtLocation(typeIdSymbol, atLocation);
|
|
3548
|
+
return yield* streamVarianceStruct(typeIdType, atLocation);
|
|
3549
|
+
}),
|
|
3550
|
+
"TypeParser.streamType",
|
|
3551
|
+
(type) => type
|
|
3552
|
+
);
|
|
3530
3553
|
const isEffectTypeSourceFile = cachedBy(
|
|
3531
3554
|
fn("TypeParser.isEffectTypeSourceFile")(function* (sourceFile) {
|
|
3532
3555
|
const moduleSymbol = typeChecker.getSymbolAtLocation(sourceFile);
|
|
@@ -4098,33 +4121,33 @@ function make2(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
|
|
|
4098
4121
|
),
|
|
4099
4122
|
([Identifier, Service]) => ({ Identifier, Service })
|
|
4100
4123
|
);
|
|
4101
|
-
const serviceVarianceStruct = (type, atLocation) => map4(
|
|
4102
|
-
all(
|
|
4103
|
-
varianceStructInvariantType(type, atLocation, "_Identifier"),
|
|
4104
|
-
varianceStructInvariantType(type, atLocation, "_Service")
|
|
4105
|
-
),
|
|
4106
|
-
([Identifier, Service]) => ({ Identifier, Service })
|
|
4107
|
-
);
|
|
4108
4124
|
const serviceType = cachedBy(
|
|
4109
4125
|
fn("TypeParser.serviceType")(function* (type, atLocation) {
|
|
4126
|
+
if (supportedEffect() !== "v4") return yield* typeParserIssue("v4 only");
|
|
4110
4127
|
yield* pipeableType(type, atLocation);
|
|
4111
|
-
const
|
|
4112
|
-
|
|
4113
|
-
|
|
4114
|
-
if (propertiesSymbols.length === 0) {
|
|
4115
|
-
return yield* typeParserIssue("Type has no tag variance struct", type, atLocation);
|
|
4128
|
+
const typeIdSymbol = typeChecker.getPropertyOfType(type, "~effect/ServiceMap/Service");
|
|
4129
|
+
if (!typeIdSymbol) {
|
|
4130
|
+
return yield* typeParserIssue("Type has no service key type id", type, atLocation);
|
|
4116
4131
|
}
|
|
4117
|
-
|
|
4118
|
-
|
|
4119
|
-
|
|
4120
|
-
|
|
4121
|
-
|
|
4132
|
+
const identifierSymbol = typeChecker.getPropertyOfType(type, "Identifier");
|
|
4133
|
+
if (!identifierSymbol) {
|
|
4134
|
+
return yield* typeParserIssue("Type has no 'Identifier' property", type, atLocation);
|
|
4135
|
+
}
|
|
4136
|
+
const serviceSymbol = typeChecker.getPropertyOfType(type, "Service");
|
|
4137
|
+
if (!serviceSymbol) {
|
|
4138
|
+
return yield* typeParserIssue("Type has no 'Service' property", type, atLocation);
|
|
4139
|
+
}
|
|
4140
|
+
return {
|
|
4141
|
+
Identifier: typeChecker.getTypeOfSymbolAtLocation(identifierSymbol, atLocation),
|
|
4142
|
+
Service: typeChecker.getTypeOfSymbolAtLocation(serviceSymbol, atLocation)
|
|
4143
|
+
};
|
|
4122
4144
|
}),
|
|
4123
4145
|
"TypeParser.serviceType",
|
|
4124
4146
|
(type) => type
|
|
4125
4147
|
);
|
|
4126
4148
|
const contextTag = cachedBy(
|
|
4127
4149
|
fn("TypeParser.contextTag")(function* (type, atLocation) {
|
|
4150
|
+
if (supportedEffect() !== "v3") return yield* typeParserIssue("v3 only");
|
|
4128
4151
|
yield* pipeableType(type, atLocation);
|
|
4129
4152
|
const propertiesSymbols = typeChecker.getPropertiesOfType(type).filter(
|
|
4130
4153
|
(_) => _.flags & ts.SymbolFlags.Property && !(_.flags & ts.SymbolFlags.Optional) && _.valueDeclaration
|
|
@@ -4265,6 +4288,19 @@ function make2(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
|
|
|
4265
4288
|
"TypeParser.promiseLike",
|
|
4266
4289
|
(type) => type
|
|
4267
4290
|
);
|
|
4291
|
+
const promiseType = cachedBy(
|
|
4292
|
+
function(type, atLocation) {
|
|
4293
|
+
const promiseSymbol = typeChecker.resolveName("Promise", void 0, ts.SymbolFlags.Type, false);
|
|
4294
|
+
if (!promiseSymbol) return typeParserIssue("global Promise type not found", type, atLocation);
|
|
4295
|
+
const globalPromiseType = typeChecker.getDeclaredTypeOfSymbol(promiseSymbol);
|
|
4296
|
+
if (type === globalPromiseType || "target" in type && type.target === globalPromiseType || typeChecker.isTypeAssignableTo(type, globalPromiseType)) {
|
|
4297
|
+
return succeed3(type);
|
|
4298
|
+
}
|
|
4299
|
+
return typeParserIssue("type is not a Promise", type, atLocation);
|
|
4300
|
+
},
|
|
4301
|
+
"TypeParser.promiseType",
|
|
4302
|
+
(type) => type
|
|
4303
|
+
);
|
|
4268
4304
|
const extendsSchemaClass = cachedBy(
|
|
4269
4305
|
fn("TypeParser.extendsSchemaClass")(function* (atLocation) {
|
|
4270
4306
|
if (!atLocation.name) {
|
|
@@ -5262,6 +5298,7 @@ function make2(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
|
|
|
5262
5298
|
isServiceMapTypeSourceFile,
|
|
5263
5299
|
isNodeReferenceToServiceMapModuleApi,
|
|
5264
5300
|
effectType,
|
|
5301
|
+
streamType,
|
|
5265
5302
|
strictEffectType,
|
|
5266
5303
|
layerType,
|
|
5267
5304
|
fiberType,
|
|
@@ -5283,6 +5320,7 @@ function make2(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
|
|
|
5283
5320
|
singleArgCall,
|
|
5284
5321
|
scopeType,
|
|
5285
5322
|
promiseLike,
|
|
5323
|
+
promiseType,
|
|
5286
5324
|
extendsEffectTag,
|
|
5287
5325
|
extendsEffectService,
|
|
5288
5326
|
extendsServiceMapService,
|
|
@@ -5411,6 +5449,37 @@ var anyUnknownInErrorContext = createDiagnostic({
|
|
|
5411
5449
|
})
|
|
5412
5450
|
});
|
|
5413
5451
|
|
|
5452
|
+
// src/diagnostics/asyncFunction.ts
|
|
5453
|
+
var asyncFunction = createDiagnostic({
|
|
5454
|
+
name: "asyncFunction",
|
|
5455
|
+
code: 69,
|
|
5456
|
+
description: "Warns when declaring async functions and suggests using Effect values and Effect.gen for async control flow",
|
|
5457
|
+
group: "effectNative",
|
|
5458
|
+
severity: "off",
|
|
5459
|
+
fixable: false,
|
|
5460
|
+
supportedEffect: ["v3", "v4"],
|
|
5461
|
+
apply: fn("asyncFunction.apply")(function* (sourceFile, report) {
|
|
5462
|
+
const ts = yield* service(TypeScriptApi);
|
|
5463
|
+
const hasAsyncModifier = (node) => ts.getModifiers(node)?.some((modifier) => modifier.kind === ts.SyntaxKind.AsyncKeyword) === true;
|
|
5464
|
+
const visit = (node) => {
|
|
5465
|
+
if (!ts.isFunctionDeclaration(node) && !ts.isFunctionExpression(node) && !ts.isArrowFunction(node) && !ts.isMethodDeclaration(node)) {
|
|
5466
|
+
ts.forEachChild(node, visit);
|
|
5467
|
+
return void 0;
|
|
5468
|
+
}
|
|
5469
|
+
if (hasAsyncModifier(node)) {
|
|
5470
|
+
report({
|
|
5471
|
+
location: node,
|
|
5472
|
+
messageText: "This code declares an async function, consider representing this async control flow with Effect values and `Effect.gen`.",
|
|
5473
|
+
fixes: []
|
|
5474
|
+
});
|
|
5475
|
+
}
|
|
5476
|
+
ts.forEachChild(node, visit);
|
|
5477
|
+
return void 0;
|
|
5478
|
+
};
|
|
5479
|
+
ts.forEachChild(sourceFile, visit);
|
|
5480
|
+
})
|
|
5481
|
+
});
|
|
5482
|
+
|
|
5414
5483
|
// src/diagnostics/catchAllToMapError.ts
|
|
5415
5484
|
var catchAllToMapError = createDiagnostic({
|
|
5416
5485
|
name: "catchAllToMapError",
|
|
@@ -5489,7 +5558,7 @@ var catchAllToMapError = createDiagnostic({
|
|
|
5489
5558
|
const { failArg, failCall } = failCallInfo;
|
|
5490
5559
|
report({
|
|
5491
5560
|
location: transformation.callee,
|
|
5492
|
-
messageText:
|
|
5561
|
+
messageText: `\`Effect.mapError\` expresses the same error-type transformation more directly than \`Effect.${catchAllName}\` followed by \`Effect.fail\`.`,
|
|
5493
5562
|
fixes: [{
|
|
5494
5563
|
fixName: "catchAllToMapError_fix",
|
|
5495
5564
|
description: "Replace with Effect.mapError",
|
|
@@ -5553,7 +5622,7 @@ var catchUnfailableEffect = createDiagnostic({
|
|
|
5553
5622
|
if (E.flags & ts.TypeFlags.Never) {
|
|
5554
5623
|
report({
|
|
5555
5624
|
location: transformation.callee,
|
|
5556
|
-
messageText:
|
|
5625
|
+
messageText: "The previous Effect does not fail, so this error-handling branch will never run.",
|
|
5557
5626
|
fixes: []
|
|
5558
5627
|
});
|
|
5559
5628
|
}
|
|
@@ -5616,7 +5685,7 @@ var classSelfMismatch = createDiagnostic({
|
|
|
5616
5685
|
if (actualName !== expectedName) {
|
|
5617
5686
|
report({
|
|
5618
5687
|
location: selfTypeNode,
|
|
5619
|
-
messageText: `Self type parameter should be
|
|
5688
|
+
messageText: `The \`Self\` type parameter for this class should be \`${expectedName}\`.`,
|
|
5620
5689
|
fixes: [{
|
|
5621
5690
|
fixName: "classSelfMismatch_fix",
|
|
5622
5691
|
description: `Replace '${actualName}' with '${expectedName}'`,
|
|
@@ -5639,6 +5708,59 @@ var classSelfMismatch = createDiagnostic({
|
|
|
5639
5708
|
})
|
|
5640
5709
|
});
|
|
5641
5710
|
|
|
5711
|
+
// src/diagnostics/cryptoRandomUUIDInEffect.ts
|
|
5712
|
+
var makeCryptoRandomUUIDApply = (checkInEffect) => fn(`cryptoRandomUUID${checkInEffect ? "InEffect" : ""}.apply`)(function* (sourceFile, report) {
|
|
5713
|
+
const ts = yield* service(TypeScriptApi);
|
|
5714
|
+
const typeChecker = yield* service(TypeCheckerApi);
|
|
5715
|
+
const typeCheckerUtils = yield* service(TypeCheckerUtils);
|
|
5716
|
+
const typeParser = yield* service(TypeParser);
|
|
5717
|
+
const cryptoSymbol = typeChecker.resolveName("crypto", void 0, ts.SymbolFlags.Value, false);
|
|
5718
|
+
if (!cryptoSymbol) return;
|
|
5719
|
+
const nodeToVisit = [];
|
|
5720
|
+
const appendNodeToVisit = (node) => {
|
|
5721
|
+
nodeToVisit.push(node);
|
|
5722
|
+
return void 0;
|
|
5723
|
+
};
|
|
5724
|
+
ts.forEachChild(sourceFile, appendNodeToVisit);
|
|
5725
|
+
while (nodeToVisit.length > 0) {
|
|
5726
|
+
const node = nodeToVisit.shift();
|
|
5727
|
+
ts.forEachChild(node, appendNodeToVisit);
|
|
5728
|
+
if (!ts.isCallExpression(node) || !ts.isPropertyAccessExpression(node.expression) || ts.idText(node.expression.name) !== "randomUUID") continue;
|
|
5729
|
+
const symbol3 = typeChecker.getSymbolAtLocation(node.expression.expression);
|
|
5730
|
+
if (!symbol3) continue;
|
|
5731
|
+
if (typeCheckerUtils.resolveToGlobalSymbol(symbol3) !== cryptoSymbol) continue;
|
|
5732
|
+
const { inEffect } = yield* typeParser.findEnclosingScopes(node);
|
|
5733
|
+
if (inEffect !== checkInEffect) continue;
|
|
5734
|
+
report({
|
|
5735
|
+
location: node,
|
|
5736
|
+
messageText: checkInEffect ? "This Effect code uses `crypto.randomUUID()`, prefer the Effect `Random` module instead because it uses Effect-injected randomness rather than the `crypto` module behind the scenes." : "This code uses `crypto.randomUUID()`, prefer the Effect `Random` module instead because it uses Effect-injected randomness rather than the `crypto` module behind the scenes.",
|
|
5737
|
+
fixes: []
|
|
5738
|
+
});
|
|
5739
|
+
}
|
|
5740
|
+
});
|
|
5741
|
+
var cryptoRandomUUIDInEffect = createDiagnostic({
|
|
5742
|
+
name: "cryptoRandomUUIDInEffect",
|
|
5743
|
+
code: 67,
|
|
5744
|
+
description: "Warns when using crypto.randomUUID() inside Effect generators instead of the Effect Random module, which uses Effect-injected randomness rather than the crypto module behind the scenes",
|
|
5745
|
+
group: "effectNative",
|
|
5746
|
+
severity: "off",
|
|
5747
|
+
fixable: false,
|
|
5748
|
+
supportedEffect: ["v4"],
|
|
5749
|
+
apply: makeCryptoRandomUUIDApply(true)
|
|
5750
|
+
});
|
|
5751
|
+
|
|
5752
|
+
// src/diagnostics/cryptoRandomUUID.ts
|
|
5753
|
+
var cryptoRandomUUID = createDiagnostic({
|
|
5754
|
+
name: "cryptoRandomUUID",
|
|
5755
|
+
code: 66,
|
|
5756
|
+
description: "Warns when using crypto.randomUUID() outside Effect generators instead of the Effect Random module, which uses Effect-injected randomness rather than the crypto module behind the scenes",
|
|
5757
|
+
group: "effectNative",
|
|
5758
|
+
severity: "off",
|
|
5759
|
+
fixable: false,
|
|
5760
|
+
supportedEffect: ["v4"],
|
|
5761
|
+
apply: makeCryptoRandomUUIDApply(false)
|
|
5762
|
+
});
|
|
5763
|
+
|
|
5642
5764
|
// src/core/KeyBuilder.ts
|
|
5643
5765
|
var makeKeyBuilder = fn("KeyBuilder")(
|
|
5644
5766
|
function* (sourceFile) {
|
|
@@ -5806,7 +5928,7 @@ var deterministicKeys = createDiagnostic({
|
|
|
5806
5928
|
if (actualIdentifier !== expectedKey) {
|
|
5807
5929
|
report({
|
|
5808
5930
|
location: keyStringLiteral,
|
|
5809
|
-
messageText: `
|
|
5931
|
+
messageText: `This key does not match the deterministic key for this declaration. The expected key is \`${expectedKey}\`.`,
|
|
5810
5932
|
fixes: [{
|
|
5811
5933
|
fixName: "deterministicKeys_fix",
|
|
5812
5934
|
description: `Replace '${actualIdentifier}' with '${expectedKey}'`,
|
|
@@ -5845,9 +5967,8 @@ var duplicatePackage = createDiagnostic({
|
|
|
5845
5967
|
const versions = Object.keys(resolvedPackages[packageName]);
|
|
5846
5968
|
report({
|
|
5847
5969
|
location: sourceFile.statements[0],
|
|
5848
|
-
messageText: `
|
|
5849
|
-
|
|
5850
|
-
If this is intended set the LSP config "allowedDuplicatedPackages" to ${JSON.stringify(options.allowedDuplicatedPackages.concat([packageName]))}.
|
|
5970
|
+
messageText: `Multiple versions of package \`${packageName}\` were detected: ${versions.join(", ")}. Package duplication can change runtime identity and type equality across Effect modules.
|
|
5971
|
+
If this is intentional, set the LSP config \`allowedDuplicatedPackages\` to ${JSON.stringify(options.allowedDuplicatedPackages.concat([packageName]))}.
|
|
5851
5972
|
|
|
5852
5973
|
${versions.map((version) => `- found ${version} at ${resolvedPackages[packageName][version]}`).join("\n")}`,
|
|
5853
5974
|
fixes: []
|
|
@@ -5857,6 +5978,40 @@ ${versions.map((version) => `- found ${version} at ${resolvedPackages[packageNam
|
|
|
5857
5978
|
})
|
|
5858
5979
|
});
|
|
5859
5980
|
|
|
5981
|
+
// src/diagnostics/effectDoNotation.ts
|
|
5982
|
+
var effectDoNotation = createDiagnostic({
|
|
5983
|
+
name: "effectDoNotation",
|
|
5984
|
+
code: 73,
|
|
5985
|
+
description: "Suggests using Effect.gen or Effect.fn instead of the Effect.Do notation helpers",
|
|
5986
|
+
group: "style",
|
|
5987
|
+
severity: "off",
|
|
5988
|
+
fixable: false,
|
|
5989
|
+
supportedEffect: ["v3", "v4"],
|
|
5990
|
+
apply: fn("effectDoNotation.apply")(function* (sourceFile, report) {
|
|
5991
|
+
const ts = yield* service(TypeScriptApi);
|
|
5992
|
+
const typeParser = yield* service(TypeParser);
|
|
5993
|
+
const nodeToVisit = [];
|
|
5994
|
+
const appendNodeToVisit = (node) => {
|
|
5995
|
+
nodeToVisit.push(node);
|
|
5996
|
+
return void 0;
|
|
5997
|
+
};
|
|
5998
|
+
ts.forEachChild(sourceFile, appendNodeToVisit);
|
|
5999
|
+
while (nodeToVisit.length > 0) {
|
|
6000
|
+
const node = nodeToVisit.shift();
|
|
6001
|
+
const isReference = yield* orUndefined(typeParser.isNodeReferenceToEffectModuleApi("Do")(node));
|
|
6002
|
+
if (isReference) {
|
|
6003
|
+
report({
|
|
6004
|
+
location: node,
|
|
6005
|
+
messageText: "This uses the Effect do emulation. `Effect.gen` or `Effect.fn` achieve the same result with native JS scopes.",
|
|
6006
|
+
fixes: []
|
|
6007
|
+
});
|
|
6008
|
+
continue;
|
|
6009
|
+
}
|
|
6010
|
+
ts.forEachChild(node, appendNodeToVisit);
|
|
6011
|
+
}
|
|
6012
|
+
})
|
|
6013
|
+
});
|
|
6014
|
+
|
|
5860
6015
|
// src/diagnostics/effectFnIife.ts
|
|
5861
6016
|
var effectFnIife = createDiagnostic({
|
|
5862
6017
|
name: "effectFnIife",
|
|
@@ -5955,7 +6110,7 @@ var effectFnIife = createDiagnostic({
|
|
|
5955
6110
|
const traceExpressionText = traceExpression ? sourceFile.text.slice(traceExpression.pos, traceExpression.end) : void 0;
|
|
5956
6111
|
report({
|
|
5957
6112
|
location: node,
|
|
5958
|
-
messageText:
|
|
6113
|
+
messageText: `\`${effectModuleName}.${kind}\` returns a reusable function that can take arguments, but it is invoked immediately here. \`Effect.gen\` represents the immediate-use form for this pattern${traceExpressionText ? ` with \`Effect.withSpan(${traceExpressionText})\` piped at the end to maintain tracing spans` : ``}.`,
|
|
5959
6114
|
fixes
|
|
5960
6115
|
});
|
|
5961
6116
|
}
|
|
@@ -6040,7 +6195,7 @@ var effectFnImplicitAny = createDiagnostic({
|
|
|
6040
6195
|
const parameterName = getParameterName(ts, parameter.name);
|
|
6041
6196
|
report({
|
|
6042
6197
|
location: parameter.name,
|
|
6043
|
-
messageText: `Parameter
|
|
6198
|
+
messageText: `Parameter \`${parameterName}\` implicitly has type \`any\` in \`Effect.fn\`, \`Effect.fnUntraced\`, or \`Effect.fnUntracedEager\`. No parameter type is available from an explicit annotation or contextual function type.`,
|
|
6044
6199
|
fixes: []
|
|
6045
6200
|
});
|
|
6046
6201
|
}
|
|
@@ -6636,7 +6791,7 @@ var effectFnOpportunity = createDiagnostic({
|
|
|
6636
6791
|
const expectedSignature = generateExpectedSignature();
|
|
6637
6792
|
report({
|
|
6638
6793
|
location: nameIdentifier ?? targetNode,
|
|
6639
|
-
messageText: `
|
|
6794
|
+
messageText: `This expression can be rewritten in the reusable function form \`${expectedSignature}\`.`,
|
|
6640
6795
|
fixes
|
|
6641
6796
|
});
|
|
6642
6797
|
}
|
|
@@ -6803,6 +6958,43 @@ var effectInVoidSuccess = createDiagnostic({
|
|
|
6803
6958
|
})
|
|
6804
6959
|
});
|
|
6805
6960
|
|
|
6961
|
+
// src/diagnostics/effectMapFlatten.ts
|
|
6962
|
+
var effectMapFlatten = createDiagnostic({
|
|
6963
|
+
name: "effectMapFlatten",
|
|
6964
|
+
code: 74,
|
|
6965
|
+
description: "Suggests using Effect.flatMap instead of Effect.map followed by Effect.flatten in piping flows",
|
|
6966
|
+
group: "style",
|
|
6967
|
+
severity: "suggestion",
|
|
6968
|
+
fixable: false,
|
|
6969
|
+
supportedEffect: ["v3", "v4"],
|
|
6970
|
+
apply: fn("effectMapFlatten.apply")(function* (sourceFile, report) {
|
|
6971
|
+
const typeParser = yield* service(TypeParser);
|
|
6972
|
+
const flows = yield* typeParser.pipingFlows(false)(sourceFile);
|
|
6973
|
+
for (const flow2 of flows) {
|
|
6974
|
+
for (let index = 0; index < flow2.transformations.length - 1; index++) {
|
|
6975
|
+
const mapTransformation = flow2.transformations[index];
|
|
6976
|
+
const flattenTransformation = flow2.transformations[index + 1];
|
|
6977
|
+
if (!mapTransformation || !flattenTransformation || !mapTransformation?.args || flattenTransformation?.args || mapTransformation.kind !== "pipe" && mapTransformation.kind !== "pipeable" || flattenTransformation.kind !== mapTransformation.kind) {
|
|
6978
|
+
continue;
|
|
6979
|
+
}
|
|
6980
|
+
const isMapCall = yield* orUndefined(
|
|
6981
|
+
typeParser.isNodeReferenceToEffectModuleApi("map")(mapTransformation.callee)
|
|
6982
|
+
);
|
|
6983
|
+
const isFlattenCall = yield* orUndefined(
|
|
6984
|
+
typeParser.isNodeReferenceToEffectModuleApi("flatten")(flattenTransformation.callee)
|
|
6985
|
+
);
|
|
6986
|
+
if (isMapCall && isFlattenCall) {
|
|
6987
|
+
report({
|
|
6988
|
+
location: flattenTransformation.callee,
|
|
6989
|
+
messageText: "`Effect.map` + `Effect.flatten` is the same as `Effect.flatMap` that expresses the same steps more directly.",
|
|
6990
|
+
fixes: []
|
|
6991
|
+
});
|
|
6992
|
+
}
|
|
6993
|
+
}
|
|
6994
|
+
}
|
|
6995
|
+
})
|
|
6996
|
+
});
|
|
6997
|
+
|
|
6806
6998
|
// src/diagnostics/effectMapVoid.ts
|
|
6807
6999
|
var effectMapVoid = createDiagnostic({
|
|
6808
7000
|
name: "effectMapVoid",
|
|
@@ -6848,7 +7040,7 @@ var effectMapVoid = createDiagnostic({
|
|
|
6848
7040
|
if (isNone2(match2)) continue;
|
|
6849
7041
|
report({
|
|
6850
7042
|
location: node.expression,
|
|
6851
|
-
messageText: "
|
|
7043
|
+
messageText: "This expression discards the success value through mapping. `Effect.asVoid` represents that form directly.",
|
|
6852
7044
|
fixes: [{
|
|
6853
7045
|
fixName: "effectMapVoid_fix",
|
|
6854
7046
|
description: "Replace with Effect.asVoid",
|
|
@@ -6903,7 +7095,7 @@ var effectSucceedWithVoid = createDiagnostic({
|
|
|
6903
7095
|
if (!tsUtils.isVoidExpression(argument)) continue;
|
|
6904
7096
|
report({
|
|
6905
7097
|
location: node,
|
|
6906
|
-
messageText: "Effect.void
|
|
7098
|
+
messageText: "`Effect.void` represents the same outcome as `Effect.succeed(undefined)` or `Effect.succeed(void 0)`.",
|
|
6907
7099
|
fixes: [{
|
|
6908
7100
|
fixName: "effectSucceedWithVoid_fix",
|
|
6909
7101
|
description: "Replace with Effect.void",
|
|
@@ -6965,7 +7157,7 @@ var extendsNativeError = createDiagnostic({
|
|
|
6965
7157
|
if (isNativeError) {
|
|
6966
7158
|
report({
|
|
6967
7159
|
location: node.name ?? typeExpression,
|
|
6968
|
-
messageText: "
|
|
7160
|
+
messageText: "This class extends the native `Error` type directly. Untagged native errors lose distinction in the Effect failure channel.",
|
|
6969
7161
|
fixes: []
|
|
6970
7162
|
});
|
|
6971
7163
|
}
|
|
@@ -7010,7 +7202,12 @@ var floatingEffect = createDiagnostic({
|
|
|
7010
7202
|
if (!isFloatingExpression(node)) continue;
|
|
7011
7203
|
const type = typeCheckerUtils.getTypeAtLocation(node.expression);
|
|
7012
7204
|
if (!type) continue;
|
|
7013
|
-
const effect = yield* option(
|
|
7205
|
+
const effect = yield* option(
|
|
7206
|
+
pipe(
|
|
7207
|
+
typeParser.effectType(type, node.expression),
|
|
7208
|
+
orElse2(() => typeParser.streamType(type, node.expression))
|
|
7209
|
+
)
|
|
7210
|
+
);
|
|
7014
7211
|
if (isSome2(effect)) {
|
|
7015
7212
|
const allowedFloatingEffects = yield* pipe(
|
|
7016
7213
|
typeParser.fiberType(type, node.expression),
|
|
@@ -7019,10 +7216,9 @@ var floatingEffect = createDiagnostic({
|
|
|
7019
7216
|
);
|
|
7020
7217
|
if (isNone2(allowedFloatingEffects)) {
|
|
7021
7218
|
const isStrictEffect = yield* option(typeParser.strictEffectType(type, node.expression));
|
|
7022
|
-
const name = isSome2(isStrictEffect) ? "Effect" : "Effect-able " + typeChecker.typeToString(type);
|
|
7023
7219
|
report({
|
|
7024
7220
|
location: node,
|
|
7025
|
-
messageText:
|
|
7221
|
+
messageText: isSome2(isStrictEffect) ? "This Effect value is neither yielded nor used in an assignment." : `This Effect-able \`${typeChecker.typeToString(type)}\` value is neither yielded nor assigned to a variable.`,
|
|
7026
7222
|
fixes: []
|
|
7027
7223
|
});
|
|
7028
7224
|
}
|
|
@@ -7066,6 +7262,7 @@ var genericEffectServices = createDiagnostic({
|
|
|
7066
7262
|
for (const [type, reportAt] of typesToCheck) {
|
|
7067
7263
|
yield* pipe(
|
|
7068
7264
|
typeParser.contextTag(type, node),
|
|
7265
|
+
orElse2(() => typeParser.serviceType(type, node)),
|
|
7069
7266
|
map4(() => {
|
|
7070
7267
|
report({
|
|
7071
7268
|
location: reportAt,
|
|
@@ -7117,7 +7314,7 @@ var makeGlobalConsoleApply = (checkInEffect) => fn(`globalConsole${checkInEffect
|
|
|
7117
7314
|
if (inEffect !== checkInEffect) continue;
|
|
7118
7315
|
report({
|
|
7119
7316
|
location: node,
|
|
7120
|
-
messageText: checkInEffect ? `
|
|
7317
|
+
messageText: checkInEffect ? `This Effect code uses \`console.${method}\`, logging in Effect code is represented through \`${alternative}\`.` : `This code uses \`console.${method}\`, the corresponding Effect logging API is \`${alternative}\`.`,
|
|
7121
7318
|
fixes: []
|
|
7122
7319
|
});
|
|
7123
7320
|
}
|
|
@@ -7166,10 +7363,10 @@ var makeGlobalDateApply = (checkInEffect) => fn(`globalDate${checkInEffect ? "In
|
|
|
7166
7363
|
let objectNode;
|
|
7167
7364
|
if (ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression) && ts.idText(node.expression.name) === "now") {
|
|
7168
7365
|
objectNode = node.expression.expression;
|
|
7169
|
-
messageText = checkInEffect ? "
|
|
7366
|
+
messageText = checkInEffect ? "This Effect code uses `Date.now()`, time access in Effect code is represented through `Clock` from Effect." : "This code uses `Date.now()`, time access is represented through `Clock` from Effect.";
|
|
7170
7367
|
} else if (ts.isNewExpression(node)) {
|
|
7171
7368
|
objectNode = node.expression;
|
|
7172
|
-
messageText = checkInEffect ? "
|
|
7369
|
+
messageText = checkInEffect ? "This Effect code constructs `new Date()`, date values in Effect code are represented through `DateTime` from Effect." : "This code constructs `new Date()`, date values are represented through `DateTime` from Effect.";
|
|
7173
7370
|
}
|
|
7174
7371
|
if (!messageText || !objectNode) continue;
|
|
7175
7372
|
const symbol3 = typeChecker.getSymbolAtLocation(objectNode);
|
|
@@ -7252,7 +7449,7 @@ var globalErrorInEffectCatch = createDiagnostic({
|
|
|
7252
7449
|
);
|
|
7253
7450
|
report({
|
|
7254
7451
|
location: node.expression,
|
|
7255
|
-
messageText: `The
|
|
7452
|
+
messageText: `The \`catch\` callback in \`${nodeText}\` returns the global \`Error\` type. Untagged errors merge together in the Effect error channel and lose type-level distinction; a tagged error preserves that distinction and can wrap the original error in a \`cause\` property.`,
|
|
7256
7453
|
fixes: []
|
|
7257
7454
|
});
|
|
7258
7455
|
}
|
|
@@ -7332,7 +7529,7 @@ var makeGlobalFetchApply = (checkInEffect) => fn(`globalFetch${checkInEffect ? "
|
|
|
7332
7529
|
if (!fetchSymbol) return;
|
|
7333
7530
|
const effectVersion = typeParser.supportedEffect();
|
|
7334
7531
|
const packageName = effectVersion === "v3" ? "@effect/platform" : "effect/unstable/http";
|
|
7335
|
-
const messageText = checkInEffect ? `
|
|
7532
|
+
const messageText = checkInEffect ? `This Effect code calls the global \`fetch\` function, HTTP requests in Effect code are represented through \`HttpClient\` from \`${packageName}\`.` : `This code uses the global \`fetch\` function, HTTP requests are represented through \`HttpClient\` from \`${packageName}\`.`;
|
|
7336
7533
|
const nodeToVisit = [];
|
|
7337
7534
|
const appendNodeToVisit = (node) => {
|
|
7338
7535
|
nodeToVisit.push(node);
|
|
@@ -7405,7 +7602,7 @@ var makeGlobalRandomApply = (checkInEffect) => fn(`globalRandom${checkInEffect ?
|
|
|
7405
7602
|
if (inEffect !== checkInEffect) continue;
|
|
7406
7603
|
report({
|
|
7407
7604
|
location: node,
|
|
7408
|
-
messageText: checkInEffect ? "
|
|
7605
|
+
messageText: checkInEffect ? "This Effect code uses `Math.random()`, randomness is represented through the Effect `Random` service." : "This code uses `Math.random()`, randomness is represented through the Effect `Random` service.",
|
|
7409
7606
|
fixes: []
|
|
7410
7607
|
});
|
|
7411
7608
|
}
|
|
@@ -7436,12 +7633,12 @@ var globalRandom = createDiagnostic({
|
|
|
7436
7633
|
// src/diagnostics/globalTimersInEffect.ts
|
|
7437
7634
|
var timerAlternatives = {
|
|
7438
7635
|
"setTimeout": {
|
|
7439
|
-
inEffect: "
|
|
7440
|
-
outsideEffect: "
|
|
7636
|
+
inEffect: "This Effect code uses `setTimeout`, the corresponding timer API in this context is `Effect.sleep or Schedule` from Effect.",
|
|
7637
|
+
outsideEffect: "This code uses `setTimeout`, the corresponding Effect timer API is `Effect.sleep or Schedule` from Effect."
|
|
7441
7638
|
},
|
|
7442
7639
|
"setInterval": {
|
|
7443
|
-
inEffect: "
|
|
7444
|
-
outsideEffect: "
|
|
7640
|
+
inEffect: "This Effect code uses `setInterval`, the corresponding timer API in this context is `Schedule or Effect.repeat` from Effect.",
|
|
7641
|
+
outsideEffect: "This code uses `setInterval`, the corresponding Effect timer API is `Schedule or Effect.repeat` from Effect."
|
|
7445
7642
|
}
|
|
7446
7643
|
};
|
|
7447
7644
|
var makeGlobalTimersApply = (checkInEffect) => fn(`globalTimers${checkInEffect ? "InEffect" : ""}.apply`)(function* (sourceFile, report) {
|
|
@@ -7683,7 +7880,7 @@ var instanceOfSchema = createDiagnostic({
|
|
|
7683
7880
|
if (isSchemaType._tag === "Some") {
|
|
7684
7881
|
report({
|
|
7685
7882
|
location: node,
|
|
7686
|
-
messageText: "
|
|
7883
|
+
messageText: "This code uses `instanceof` with an Effect Schema type. `Schema.is` is the schema-aware runtime check for this case.",
|
|
7687
7884
|
fixes: [{
|
|
7688
7885
|
fixName: "instanceOfSchema_fix",
|
|
7689
7886
|
description: "Replace with Schema.is",
|
|
@@ -7829,6 +8026,55 @@ var layerMergeAllWithDependencies = createDiagnostic({
|
|
|
7829
8026
|
})
|
|
7830
8027
|
});
|
|
7831
8028
|
|
|
8029
|
+
// src/diagnostics/lazyPromiseInEffectSync.ts
|
|
8030
|
+
var lazyPromiseInEffectSync = createDiagnostic({
|
|
8031
|
+
name: "lazyPromiseInEffectSync",
|
|
8032
|
+
code: 70,
|
|
8033
|
+
description: "Warns when Effect.sync lazily returns a Promise instead of using an async Effect constructor",
|
|
8034
|
+
group: "antipattern",
|
|
8035
|
+
severity: "warning",
|
|
8036
|
+
fixable: false,
|
|
8037
|
+
supportedEffect: ["v3", "v4"],
|
|
8038
|
+
apply: fn("lazyPromiseInEffectSync.apply")(function* (sourceFile, report) {
|
|
8039
|
+
const ts = yield* service(TypeScriptApi);
|
|
8040
|
+
const typeChecker = yield* service(TypeCheckerApi);
|
|
8041
|
+
const typeCheckerUtils = yield* service(TypeCheckerUtils);
|
|
8042
|
+
const typeParser = yield* service(TypeParser);
|
|
8043
|
+
const nodeToVisit = [];
|
|
8044
|
+
const appendNodeToVisit = (node) => {
|
|
8045
|
+
nodeToVisit.push(node);
|
|
8046
|
+
return void 0;
|
|
8047
|
+
};
|
|
8048
|
+
ts.forEachChild(sourceFile, appendNodeToVisit);
|
|
8049
|
+
while (nodeToVisit.length > 0) {
|
|
8050
|
+
const node = nodeToVisit.shift();
|
|
8051
|
+
ts.forEachChild(node, appendNodeToVisit);
|
|
8052
|
+
if (!ts.isCallExpression(node)) continue;
|
|
8053
|
+
const isSyncCall = yield* orUndefined(
|
|
8054
|
+
typeParser.isNodeReferenceToEffectModuleApi("sync")(node.expression)
|
|
8055
|
+
);
|
|
8056
|
+
if (!isSyncCall) continue;
|
|
8057
|
+
const lazyArg = node.arguments[0];
|
|
8058
|
+
if (!lazyArg) continue;
|
|
8059
|
+
const lazyArgType = typeCheckerUtils.getTypeAtLocation(lazyArg);
|
|
8060
|
+
if (!lazyArgType) continue;
|
|
8061
|
+
const entries = typeCheckerUtils.unrollUnionMembers(lazyArgType).flatMap(
|
|
8062
|
+
(member) => typeChecker.getSignaturesOfType(member, ts.SignatureKind.Call).map(
|
|
8063
|
+
(signature) => typeParser.promiseType(typeChecker.getReturnTypeOfSignature(signature), lazyArg)
|
|
8064
|
+
)
|
|
8065
|
+
);
|
|
8066
|
+
if (entries.length === 0) continue;
|
|
8067
|
+
const promiseReturn = yield* orUndefined(firstSuccessOf(entries));
|
|
8068
|
+
if (!promiseReturn) continue;
|
|
8069
|
+
report({
|
|
8070
|
+
location: lazyArg,
|
|
8071
|
+
messageText: "This `Effect.sync` thunk returns a Promise. Use `Effect.promise` or `Effect.tryPromise` to represent async work.",
|
|
8072
|
+
fixes: []
|
|
8073
|
+
});
|
|
8074
|
+
}
|
|
8075
|
+
})
|
|
8076
|
+
});
|
|
8077
|
+
|
|
7832
8078
|
// src/diagnostics/leakingRequirements.ts
|
|
7833
8079
|
var leakingRequirements = createDiagnostic({
|
|
7834
8080
|
name: "leakingRequirements",
|
|
@@ -7953,7 +8199,7 @@ var leakingRequirements = createDiagnostic({
|
|
|
7953
8199
|
location: node,
|
|
7954
8200
|
messageText: `Methods of this Service require \`${requirementsStr}\` from every caller.
|
|
7955
8201
|
|
|
7956
|
-
|
|
8202
|
+
The requirement becomes part of the public service surface instead of remaining internal to Layer implementation.
|
|
7957
8203
|
|
|
7958
8204
|
Resolve these dependencies at Layer creation and provide them to each method, so the service's type reflects its purpose, not its implementation.
|
|
7959
8205
|
|
|
@@ -8150,7 +8396,7 @@ var missedPipeableOpportunity = createDiagnostic({
|
|
|
8150
8396
|
).trim() : "";
|
|
8151
8397
|
report({
|
|
8152
8398
|
location: flow2.node,
|
|
8153
|
-
messageText: `
|
|
8399
|
+
messageText: `This nested call structure has a pipeable form. \`${subjectText}.pipe(...)\` represents the same call sequence in pipe style and may be easier to read.`,
|
|
8154
8400
|
fixes: [{
|
|
8155
8401
|
fixName: "missedPipeableOpportunity_fix",
|
|
8156
8402
|
description: "Convert to pipe style",
|
|
@@ -8231,7 +8477,7 @@ var missingEffectContext = createDiagnostic({
|
|
|
8231
8477
|
(missingTypes) => missingTypes.length > 0 ? report(
|
|
8232
8478
|
{
|
|
8233
8479
|
location: node,
|
|
8234
|
-
messageText: `
|
|
8480
|
+
messageText: `This Effect requires a service that is missing from the expected Effect context: \`${sortTypes(missingTypes).map((_) => typeChecker.typeToString(_)).join(" | ")}\`.`,
|
|
8235
8481
|
fixes: []
|
|
8236
8482
|
}
|
|
8237
8483
|
) : void 0
|
|
@@ -8582,7 +8828,7 @@ var missingReturnYieldStar = createDiagnostic({
|
|
|
8582
8828
|
}];
|
|
8583
8829
|
report({
|
|
8584
8830
|
location: unwrapped,
|
|
8585
|
-
messageText:
|
|
8831
|
+
messageText: "This Effect never succeeds; using `return yield*` preserves a definitive generator exit point for type narrowing and tooling support.",
|
|
8586
8832
|
fixes: fix
|
|
8587
8833
|
});
|
|
8588
8834
|
}
|
|
@@ -8638,7 +8884,7 @@ var missingStarInYieldEffectGen = createDiagnostic({
|
|
|
8638
8884
|
brokenGenerators.forEach(
|
|
8639
8885
|
(pos) => report({
|
|
8640
8886
|
location: { pos, end: pos + "function".length },
|
|
8641
|
-
messageText: `
|
|
8887
|
+
messageText: "This uses `yield` for an `Effect` value. `yield*` is the Effect-aware form in this context.",
|
|
8642
8888
|
fixes: []
|
|
8643
8889
|
})
|
|
8644
8890
|
);
|
|
@@ -8660,7 +8906,7 @@ var missingStarInYieldEffectGen = createDiagnostic({
|
|
|
8660
8906
|
}] : [];
|
|
8661
8907
|
report({
|
|
8662
8908
|
location: node,
|
|
8663
|
-
messageText: `
|
|
8909
|
+
messageText: "This uses `yield` for an `Effect` value. `yield*` is the Effect-aware form in this context.",
|
|
8664
8910
|
fixes: fix
|
|
8665
8911
|
});
|
|
8666
8912
|
});
|
|
@@ -8728,7 +8974,7 @@ var multipleEffectProvide = createDiagnostic({
|
|
|
8728
8974
|
if (chunk.length < 2) continue;
|
|
8729
8975
|
report({
|
|
8730
8976
|
location: chunk[0].node,
|
|
8731
|
-
messageText: "
|
|
8977
|
+
messageText: "This expression chains multiple `Effect.provide` calls. Providing Layers in multiple calls in a chain can break service lifecycle behavior compared with a single combined provide with merged layers.",
|
|
8732
8978
|
fixes: [{
|
|
8733
8979
|
fixName: "multipleEffectProvide_fix",
|
|
8734
8980
|
description: "Combine into a single provide",
|
|
@@ -8762,6 +9008,74 @@ var multipleEffectProvide = createDiagnostic({
|
|
|
8762
9008
|
})
|
|
8763
9009
|
});
|
|
8764
9010
|
|
|
9011
|
+
// src/diagnostics/nestedEffectGenYield.ts
|
|
9012
|
+
var nestedEffectGenYield = createDiagnostic({
|
|
9013
|
+
name: "nestedEffectGenYield",
|
|
9014
|
+
code: 71,
|
|
9015
|
+
description: "Warns when yielding a nested bare Effect.gen inside an existing Effect generator context",
|
|
9016
|
+
group: "style",
|
|
9017
|
+
severity: "off",
|
|
9018
|
+
fixable: false,
|
|
9019
|
+
supportedEffect: ["v3", "v4"],
|
|
9020
|
+
apply: fn("nestedEffectGenYield.apply")(function* (sourceFile, report) {
|
|
9021
|
+
const ts = yield* service(TypeScriptApi);
|
|
9022
|
+
const typeParser = yield* service(TypeParser);
|
|
9023
|
+
const nodeToVisit = [];
|
|
9024
|
+
const appendNodeToVisit = (node) => {
|
|
9025
|
+
nodeToVisit.push(node);
|
|
9026
|
+
return void 0;
|
|
9027
|
+
};
|
|
9028
|
+
ts.forEachChild(sourceFile, appendNodeToVisit);
|
|
9029
|
+
while (nodeToVisit.length > 0) {
|
|
9030
|
+
const node = nodeToVisit.shift();
|
|
9031
|
+
ts.forEachChild(node, appendNodeToVisit);
|
|
9032
|
+
if (!ts.isYieldExpression(node) || !node.asteriskToken || !node.expression) continue;
|
|
9033
|
+
const { inEffect } = yield* typeParser.findEnclosingScopes(node);
|
|
9034
|
+
if (!inEffect) continue;
|
|
9035
|
+
const bareNestedEffectGen = yield* orUndefined(typeParser.effectGen(node.expression));
|
|
9036
|
+
if (!bareNestedEffectGen) continue;
|
|
9037
|
+
report({
|
|
9038
|
+
location: node.expression,
|
|
9039
|
+
messageText: "This `yield*` is applied to a nested `Effect.gen(...)` that can be inlined in the parent Effect generator context.",
|
|
9040
|
+
fixes: []
|
|
9041
|
+
});
|
|
9042
|
+
}
|
|
9043
|
+
})
|
|
9044
|
+
});
|
|
9045
|
+
|
|
9046
|
+
// src/diagnostics/newPromise.ts
|
|
9047
|
+
var newPromise = createDiagnostic({
|
|
9048
|
+
name: "newPromise",
|
|
9049
|
+
code: 68,
|
|
9050
|
+
description: "Warns when constructing promises with new Promise instead of using Effect APIs",
|
|
9051
|
+
group: "effectNative",
|
|
9052
|
+
severity: "off",
|
|
9053
|
+
fixable: false,
|
|
9054
|
+
supportedEffect: ["v3", "v4"],
|
|
9055
|
+
apply: fn("newPromise.apply")(function* (sourceFile, report) {
|
|
9056
|
+
const ts = yield* service(TypeScriptApi);
|
|
9057
|
+
const typeChecker = yield* service(TypeCheckerApi);
|
|
9058
|
+
const typeCheckerUtils = yield* service(TypeCheckerUtils);
|
|
9059
|
+
const promiseSymbol = typeChecker.resolveName("Promise", void 0, ts.SymbolFlags.Value, false);
|
|
9060
|
+
if (!promiseSymbol) return;
|
|
9061
|
+
const visit = (node) => {
|
|
9062
|
+
if (ts.isNewExpression(node)) {
|
|
9063
|
+
const symbol3 = typeChecker.getSymbolAtLocation(node.expression);
|
|
9064
|
+
if (symbol3 && typeCheckerUtils.resolveToGlobalSymbol(symbol3) === promiseSymbol) {
|
|
9065
|
+
report({
|
|
9066
|
+
location: node,
|
|
9067
|
+
messageText: "This code constructs `new Promise(...)`, prefer Effect APIs such as `Effect.async`, `Effect.promise`, or `Effect.tryPromise` instead of manual Promise construction.",
|
|
9068
|
+
fixes: []
|
|
9069
|
+
});
|
|
9070
|
+
}
|
|
9071
|
+
}
|
|
9072
|
+
ts.forEachChild(node, visit);
|
|
9073
|
+
return void 0;
|
|
9074
|
+
};
|
|
9075
|
+
ts.forEachChild(sourceFile, visit);
|
|
9076
|
+
})
|
|
9077
|
+
});
|
|
9078
|
+
|
|
8765
9079
|
// src/diagnostics/nodeBuiltinImport.ts
|
|
8766
9080
|
var moduleAlternativesV3 = /* @__PURE__ */ new Map([
|
|
8767
9081
|
["fs", { alternative: "FileSystem", module: "fs", package: "@effect/platform" }],
|
|
@@ -8818,7 +9132,7 @@ var nodeBuiltinImport = createDiagnostic({
|
|
|
8818
9132
|
if (match2) {
|
|
8819
9133
|
report({
|
|
8820
9134
|
location: statement.moduleSpecifier,
|
|
8821
|
-
messageText: `
|
|
9135
|
+
messageText: `This module reference uses the \`${match2.module}\` module, the corresponding Effect API is \`${match2.alternative}\` from \`${match2.package}\`.`,
|
|
8822
9136
|
fixes: []
|
|
8823
9137
|
});
|
|
8824
9138
|
}
|
|
@@ -8831,7 +9145,7 @@ var nodeBuiltinImport = createDiagnostic({
|
|
|
8831
9145
|
if (match2) {
|
|
8832
9146
|
report({
|
|
8833
9147
|
location: arg,
|
|
8834
|
-
messageText: `
|
|
9148
|
+
messageText: `This module reference uses the \`${match2.module}\` module, the corresponding Effect API is \`${match2.alternative}\` from \`${match2.package}\`.`,
|
|
8835
9149
|
fixes: []
|
|
8836
9150
|
});
|
|
8837
9151
|
}
|
|
@@ -8885,7 +9199,7 @@ var nonObjectEffectServiceType = createDiagnostic({
|
|
|
8885
9199
|
const propertyValue = property.initializer;
|
|
8886
9200
|
const errorToReport = {
|
|
8887
9201
|
location: property.name,
|
|
8888
|
-
messageText: "Effect.Service
|
|
9202
|
+
messageText: "`Effect.Service` is declared with a primitive service type. `Effect.Service` models object-shaped services; primitive values use `Context.Tag` or `Effect.Tag` directly.",
|
|
8889
9203
|
fixes: []
|
|
8890
9204
|
};
|
|
8891
9205
|
if (propertyName === "succeed") {
|
|
@@ -9105,6 +9419,14 @@ var effectModuleMigrationDb = {
|
|
|
9105
9419
|
"yieldNow": asUnchanged,
|
|
9106
9420
|
"zip": asUnchanged,
|
|
9107
9421
|
"zipWith": asUnchanged,
|
|
9422
|
+
"annotateLogsScoped": asUnchanged,
|
|
9423
|
+
"awaitAllChildren": asUnchanged,
|
|
9424
|
+
"bind": asUnchanged,
|
|
9425
|
+
"bindTo": asUnchanged,
|
|
9426
|
+
"Do": asUnchanged,
|
|
9427
|
+
"let": asUnchanged,
|
|
9428
|
+
"partition": asUnchanged,
|
|
9429
|
+
"validate": asUnchanged,
|
|
9108
9430
|
// Renamed APIs (v3 name → v4 name)
|
|
9109
9431
|
"catchAll": asRenamedSameBehaviour("catch"),
|
|
9110
9432
|
"catchAllCause": asRenamedSameBehaviour("catchCause"),
|
|
@@ -9124,14 +9446,6 @@ var effectModuleMigrationDb = {
|
|
|
9124
9446
|
"serviceOptional": asRenamedSameBehaviour("serviceOption"),
|
|
9125
9447
|
"tapErrorCause": asRenamedSameBehaviour("tapCause"),
|
|
9126
9448
|
// Removed APIs
|
|
9127
|
-
"annotateLogsScoped": asUnchanged,
|
|
9128
|
-
"awaitAllChildren": asUnchanged,
|
|
9129
|
-
"bind": asUnchanged,
|
|
9130
|
-
"bindTo": asUnchanged,
|
|
9131
|
-
"Do": asUnchanged,
|
|
9132
|
-
"let": asUnchanged,
|
|
9133
|
-
"partition": asUnchanged,
|
|
9134
|
-
"validate": asUnchanged,
|
|
9135
9449
|
"catchSomeDefect": asRemoved(
|
|
9136
9450
|
"Use Effect.catchDefect or Effect.matchCause to handle specific defects."
|
|
9137
9451
|
),
|
|
@@ -9647,7 +9961,7 @@ var outdatedApi = createDiagnostic({
|
|
|
9647
9961
|
hasReported = true;
|
|
9648
9962
|
report({
|
|
9649
9963
|
location: propertyAccess.name,
|
|
9650
|
-
messageText:
|
|
9964
|
+
messageText: `This project targets Effect v4, but this code uses the Effect v3 API \`${propertyName}\`. The referenced API belongs to the v3 surface rather than the configured v4 surface.`,
|
|
9651
9965
|
fixes: []
|
|
9652
9966
|
});
|
|
9653
9967
|
}
|
|
@@ -9690,7 +10004,7 @@ var outdatedApi = createDiagnostic({
|
|
|
9690
10004
|
if (hasReported) {
|
|
9691
10005
|
report({
|
|
9692
10006
|
location: { pos: 0, end: 0 },
|
|
9693
|
-
messageText: "This project targets Effect v4, but
|
|
10007
|
+
messageText: "This project targets Effect v4, but this code uses Effect v3 APIs. The referenced API belongs to the v3 surface rather than the configured v4 surface.",
|
|
9694
10008
|
fixes: []
|
|
9695
10009
|
});
|
|
9696
10010
|
}
|
|
@@ -10935,7 +11249,7 @@ var overriddenSchemaConstructor = createDiagnostic({
|
|
|
10935
11249
|
};
|
|
10936
11250
|
report({
|
|
10937
11251
|
location: member,
|
|
10938
|
-
messageText: "
|
|
11252
|
+
messageText: "This Schema subclass defines its own constructor. For Schema classes, constructor overrides break decoding behavior for the class shape. Custom construction can be expressed through a static `new` method instead.",
|
|
10939
11253
|
fixes: (member.body ? [fixAsStaticNew] : []).concat([{
|
|
10940
11254
|
fixName: "overriddenSchemaConstructor_fix",
|
|
10941
11255
|
description: "Remove the constructor override",
|
|
@@ -11057,7 +11371,7 @@ var preferSchemaOverJson = createDiagnostic({
|
|
|
11057
11371
|
if (isSome2(match2)) {
|
|
11058
11372
|
report({
|
|
11059
11373
|
location: match2.value,
|
|
11060
|
-
messageText: "
|
|
11374
|
+
messageText: "This code uses `JSON.parse` or `JSON.stringify`. Effect Schema provides Effect-aware APIs for JSON parsing and stringifying.",
|
|
11061
11375
|
fixes: []
|
|
11062
11376
|
});
|
|
11063
11377
|
}
|
|
@@ -11065,6 +11379,63 @@ var preferSchemaOverJson = createDiagnostic({
|
|
|
11065
11379
|
})
|
|
11066
11380
|
});
|
|
11067
11381
|
|
|
11382
|
+
// src/diagnostics/processEnvInEffect.ts
|
|
11383
|
+
var isEnvPropertyAccess = (tsApi, node) => tsApi.isPropertyAccessExpression(node) && tsApi.idText(node.name) === "env";
|
|
11384
|
+
var isProcessEnvMemberAccess = (tsApi, node) => (tsApi.isPropertyAccessExpression(node) || tsApi.isElementAccessExpression(node)) && isEnvPropertyAccess(tsApi, node.expression);
|
|
11385
|
+
var makeProcessEnvApply = (checkInEffect) => fn(`processEnv${checkInEffect ? "InEffect" : ""}.apply`)(function* (sourceFile, report) {
|
|
11386
|
+
const ts = yield* service(TypeScriptApi);
|
|
11387
|
+
const typeChecker = yield* service(TypeCheckerApi);
|
|
11388
|
+
const typeCheckerUtils = yield* service(TypeCheckerUtils);
|
|
11389
|
+
const typeParser = yield* service(TypeParser);
|
|
11390
|
+
const processSymbol = typeChecker.resolveName("process", void 0, ts.SymbolFlags.Value, false);
|
|
11391
|
+
if (!processSymbol) return;
|
|
11392
|
+
const nodeToVisit = [];
|
|
11393
|
+
const appendNodeToVisit = (node) => {
|
|
11394
|
+
nodeToVisit.push(node);
|
|
11395
|
+
return void 0;
|
|
11396
|
+
};
|
|
11397
|
+
ts.forEachChild(sourceFile, appendNodeToVisit);
|
|
11398
|
+
while (nodeToVisit.length > 0) {
|
|
11399
|
+
const node = nodeToVisit.shift();
|
|
11400
|
+
ts.forEachChild(node, appendNodeToVisit);
|
|
11401
|
+
if (!isProcessEnvMemberAccess(ts, node)) continue;
|
|
11402
|
+
const processNode = node.expression.expression;
|
|
11403
|
+
if (!ts.isIdentifier(processNode) || ts.idText(processNode) !== "process") continue;
|
|
11404
|
+
const symbol3 = typeChecker.getSymbolAtLocation(processNode);
|
|
11405
|
+
if (!symbol3) continue;
|
|
11406
|
+
if (typeCheckerUtils.resolveToGlobalSymbol(symbol3) !== processSymbol) continue;
|
|
11407
|
+
const { inEffect } = yield* typeParser.findEnclosingScopes(node);
|
|
11408
|
+
if (inEffect !== checkInEffect) continue;
|
|
11409
|
+
report({
|
|
11410
|
+
location: node,
|
|
11411
|
+
messageText: checkInEffect ? "This Effect code reads from `process.env`, environment configuration in Effect code is represented through `Config` from Effect." : "This code reads from `process.env`, environment configuration is represented through `Config` from Effect.",
|
|
11412
|
+
fixes: []
|
|
11413
|
+
});
|
|
11414
|
+
}
|
|
11415
|
+
});
|
|
11416
|
+
var processEnvInEffect = createDiagnostic({
|
|
11417
|
+
name: "processEnvInEffect",
|
|
11418
|
+
code: 65,
|
|
11419
|
+
description: "Warns when reading process.env inside Effect generators instead of using Effect Config",
|
|
11420
|
+
group: "effectNative",
|
|
11421
|
+
severity: "off",
|
|
11422
|
+
fixable: false,
|
|
11423
|
+
supportedEffect: ["v3", "v4"],
|
|
11424
|
+
apply: makeProcessEnvApply(true)
|
|
11425
|
+
});
|
|
11426
|
+
|
|
11427
|
+
// src/diagnostics/processEnv.ts
|
|
11428
|
+
var processEnv = createDiagnostic({
|
|
11429
|
+
name: "processEnv",
|
|
11430
|
+
code: 64,
|
|
11431
|
+
description: "Warns when reading process.env outside Effect generators instead of using Effect Config",
|
|
11432
|
+
group: "effectNative",
|
|
11433
|
+
severity: "off",
|
|
11434
|
+
fixable: false,
|
|
11435
|
+
supportedEffect: ["v3", "v4"],
|
|
11436
|
+
apply: makeProcessEnvApply(false)
|
|
11437
|
+
});
|
|
11438
|
+
|
|
11068
11439
|
// src/diagnostics/redundantSchemaTagIdentifier.ts
|
|
11069
11440
|
var redundantSchemaTagIdentifier = createDiagnostic({
|
|
11070
11441
|
name: "redundantSchemaTagIdentifier",
|
|
@@ -11175,9 +11546,7 @@ var returnEffectInGen = createDiagnostic({
|
|
|
11175
11546
|
}] : [];
|
|
11176
11547
|
report({
|
|
11177
11548
|
location: node,
|
|
11178
|
-
messageText:
|
|
11179
|
-
Maybe you wanted to return yield* instead?
|
|
11180
|
-
Nested Effect-able types may be intended if you plan to later manually flatten or unwrap this Effect, if so you can safely disable this diagnostic for this line through quickfixes.`,
|
|
11549
|
+
messageText: "This generator returns an Effect-able value directly, which produces a nested `Effect<Effect<...>>`. If the intended result is the inner Effect value, `return yield*` represents that form.",
|
|
11181
11550
|
fixes: fix
|
|
11182
11551
|
});
|
|
11183
11552
|
}),
|
|
@@ -11332,9 +11701,7 @@ var runEffectInsideEffect = createDiagnostic({
|
|
|
11332
11701
|
);
|
|
11333
11702
|
});
|
|
11334
11703
|
const v4MethodName = `${isEffectRunCall.value.methodName}With`;
|
|
11335
|
-
const messageText = supportedEffect === "v4" ?
|
|
11336
|
-
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.
|
|
11337
|
-
Consider extracting the Runtime by using for example Effect.runtime and then use Runtime.${isEffectRunCall.value.methodName} with the extracted runtime instead.`;
|
|
11704
|
+
const messageText = supportedEffect === "v4" ? `\`${nodeText}\` is called inside an Effect with a separate services invocation. In this context, child Effects run with the surrounding services, which can be accessed through \`Effect.services\` and \`Effect.${v4MethodName}\`.` : `\`${nodeText}\` is called inside an Effect with a separate runtime invocation. In this context, run child Effects with the surrounding runtime, which can be accessed through \`Effect.runtime\` and \`Runtime.${isEffectRunCall.value.methodName}\`.`;
|
|
11338
11705
|
report({
|
|
11339
11706
|
location: node.expression,
|
|
11340
11707
|
messageText,
|
|
@@ -11347,7 +11714,7 @@ Consider extracting the Runtime by using for example Effect.runtime and then use
|
|
|
11347
11714
|
} else {
|
|
11348
11715
|
report({
|
|
11349
11716
|
location: node.expression,
|
|
11350
|
-
messageText:
|
|
11717
|
+
messageText: `\`${nodeText}\` is called inside an existing Effect context. Here, the inner Effect can be used directly.`,
|
|
11351
11718
|
fixes: []
|
|
11352
11719
|
});
|
|
11353
11720
|
}
|
|
@@ -11402,7 +11769,7 @@ var schemaStructWithTag = createDiagnostic({
|
|
|
11402
11769
|
const otherProperties = arg.properties.filter((prop) => prop !== tagProperty);
|
|
11403
11770
|
report({
|
|
11404
11771
|
location: node,
|
|
11405
|
-
messageText: "Schema.Struct
|
|
11772
|
+
messageText: "This `Schema.Struct` includes a `_tag` field. `Schema.TaggedStruct` is the tagged-struct form for this pattern and makes the tag optional in the constructor.",
|
|
11406
11773
|
fixes: [{
|
|
11407
11774
|
fixName: "schemaStructWithTag_fix",
|
|
11408
11775
|
description: "Replace with Schema.TaggedStruct",
|
|
@@ -11495,7 +11862,7 @@ var schemaSyncInEffect = createDiagnostic({
|
|
|
11495
11862
|
const effectMethodName = syncToEffectMethod[isSchemaSyncCall.value.methodName];
|
|
11496
11863
|
report({
|
|
11497
11864
|
location: node.expression,
|
|
11498
|
-
messageText:
|
|
11865
|
+
messageText: `\`${nodeText}\` is used inside an Effect generator. \`Schema.${effectMethodName}\` preserves the typed Effect error channel for this operation without throwing.`,
|
|
11499
11866
|
fixes: []
|
|
11500
11867
|
});
|
|
11501
11868
|
}
|
|
@@ -11555,7 +11922,7 @@ var schemaUnionOfLiterals = createDiagnostic({
|
|
|
11555
11922
|
const schemaLiteralExpression = firstLiteralCall.expression;
|
|
11556
11923
|
report({
|
|
11557
11924
|
location: node,
|
|
11558
|
-
messageText: "
|
|
11925
|
+
messageText: "This `Schema.Union` contains multiple `Schema.Literal` members and can be simplified to a single `Schema.Literal` call.",
|
|
11559
11926
|
fixes: [{
|
|
11560
11927
|
fixName: "schemaUnionOfLiterals_fix",
|
|
11561
11928
|
description: "Replace with a single Schema.Literal call",
|
|
@@ -11618,8 +11985,7 @@ var scopeInLayerEffect = createDiagnostic({
|
|
|
11618
11985
|
map4(
|
|
11619
11986
|
() => report({
|
|
11620
11987
|
location: node,
|
|
11621
|
-
messageText: `
|
|
11622
|
-
Consider using "scoped" instead to get rid of the scope in the requirements.`,
|
|
11988
|
+
messageText: "This layer construction leaves `Scope` in the requirement set. The scoped API removes `Scope` from the resulting requirements.",
|
|
11623
11989
|
fixes: methodIdentifier ? [{
|
|
11624
11990
|
fixName: "scopeInLayerEffect_scoped",
|
|
11625
11991
|
description: "Use scoped for Layer creation",
|
|
@@ -11719,7 +12085,7 @@ var serviceNotAsClass = createDiagnostic({
|
|
|
11719
12085
|
const shapeText = typeArgs.length > 0 ? typeArgs.map((t) => sourceFile.text.substring(ts.getTokenPosOfNode(t, sourceFile), t.end)).join(", ") : "Shape";
|
|
11720
12086
|
report({
|
|
11721
12087
|
location: callExpr,
|
|
11722
|
-
messageText:
|
|
12088
|
+
messageText: `\`ServiceMap.Service\` is assigned to a variable here, but this API is intended for a class declaration shape such as \`class ${variableName} extends ServiceMap.Service<${variableName}, ${shapeText}>()("${argsText.replace(/['"]/g, "")}") {}\`.`,
|
|
11723
12089
|
fixes: [{
|
|
11724
12090
|
fixName: "serviceNotAsClass",
|
|
11725
12091
|
description: `Convert to class declaration`,
|
|
@@ -11932,7 +12298,7 @@ var tryCatchInEffectGen = createDiagnostic({
|
|
|
11932
12298
|
map4(() => {
|
|
11933
12299
|
report({
|
|
11934
12300
|
location: node,
|
|
11935
|
-
messageText: `
|
|
12301
|
+
messageText: `This Effect generator contains \`try/catch\`; in this context, error handling is expressed with Effect APIs such as ${alternatives.join(", ")}).`,
|
|
11936
12302
|
fixes: []
|
|
11937
12303
|
});
|
|
11938
12304
|
}),
|
|
@@ -11993,8 +12359,7 @@ var unknownInEffectCatch = createDiagnostic({
|
|
|
11993
12359
|
);
|
|
11994
12360
|
report({
|
|
11995
12361
|
location: node.expression,
|
|
11996
|
-
messageText: `The
|
|
11997
|
-
Consider wrapping unknown errors into Effect's Data.TaggedError for example, or narrow down the type to the specific error raised.`,
|
|
12362
|
+
messageText: `The \`catch\` callback in \`${nodeText}\` returns \`unknown\`, so the Effect error type stays untyped. A specific typed error preserves error-channel information, for example by narrowing the value or wrapping it in \`Data.TaggedError\`.`,
|
|
11998
12363
|
fixes: []
|
|
11999
12364
|
});
|
|
12000
12365
|
}
|
|
@@ -12008,6 +12373,56 @@ Consider wrapping unknown errors into Effect's Data.TaggedError for example, or
|
|
|
12008
12373
|
})
|
|
12009
12374
|
});
|
|
12010
12375
|
|
|
12376
|
+
// src/diagnostics/unnecessaryArrowBlock.ts
|
|
12377
|
+
var unnecessaryArrowBlock = createDiagnostic({
|
|
12378
|
+
name: "unnecessaryArrowBlock",
|
|
12379
|
+
code: 72,
|
|
12380
|
+
description: "Suggests using a concise arrow body when the block only returns an expression",
|
|
12381
|
+
group: "style",
|
|
12382
|
+
severity: "off",
|
|
12383
|
+
fixable: true,
|
|
12384
|
+
supportedEffect: ["v3", "v4"],
|
|
12385
|
+
apply: fn("unnecessaryArrowBlock.apply")(function* (sourceFile, report) {
|
|
12386
|
+
const ts = yield* service(TypeScriptApi);
|
|
12387
|
+
const nodeToVisit = [];
|
|
12388
|
+
const appendNodeToVisit = (node) => {
|
|
12389
|
+
nodeToVisit.push(node);
|
|
12390
|
+
return void 0;
|
|
12391
|
+
};
|
|
12392
|
+
ts.forEachChild(sourceFile, appendNodeToVisit);
|
|
12393
|
+
while (nodeToVisit.length > 0) {
|
|
12394
|
+
const node = nodeToVisit.shift();
|
|
12395
|
+
ts.forEachChild(node, appendNodeToVisit);
|
|
12396
|
+
if (!ts.isArrowFunction(node) || !ts.isBlock(node.body)) continue;
|
|
12397
|
+
if (node.body.statements.length !== 1) continue;
|
|
12398
|
+
const [statement] = node.body.statements;
|
|
12399
|
+
if (!ts.isReturnStatement(statement) || !statement.expression) continue;
|
|
12400
|
+
const returnedExpression = statement.expression;
|
|
12401
|
+
report({
|
|
12402
|
+
location: node.body,
|
|
12403
|
+
messageText: "This arrow function block only returns an expression and can use a concise body.",
|
|
12404
|
+
fixes: [{
|
|
12405
|
+
fixName: "unnecessaryArrowBlock_fix",
|
|
12406
|
+
description: "Use a concise arrow body",
|
|
12407
|
+
apply: gen(function* () {
|
|
12408
|
+
const changeTracker = yield* service(ChangeTracker);
|
|
12409
|
+
const replacementNode = ts.factory.updateArrowFunction(
|
|
12410
|
+
node,
|
|
12411
|
+
node.modifiers,
|
|
12412
|
+
node.typeParameters,
|
|
12413
|
+
node.parameters,
|
|
12414
|
+
node.type,
|
|
12415
|
+
node.equalsGreaterThanToken,
|
|
12416
|
+
ts.factory.createParenthesizedExpression(returnedExpression)
|
|
12417
|
+
);
|
|
12418
|
+
changeTracker.replaceNode(sourceFile, node, replacementNode);
|
|
12419
|
+
})
|
|
12420
|
+
}]
|
|
12421
|
+
});
|
|
12422
|
+
}
|
|
12423
|
+
})
|
|
12424
|
+
});
|
|
12425
|
+
|
|
12011
12426
|
// src/diagnostics/unnecessaryEffectGen.ts
|
|
12012
12427
|
var unnecessaryEffectGen = createDiagnostic({
|
|
12013
12428
|
name: "unnecessaryEffectGen",
|
|
@@ -12091,7 +12506,7 @@ var unnecessaryFailYieldableError = createDiagnostic({
|
|
|
12091
12506
|
map4(
|
|
12092
12507
|
() => report({
|
|
12093
12508
|
location: node,
|
|
12094
|
-
messageText:
|
|
12509
|
+
messageText: "This `yield* Effect.fail(...)` passes a yieldable error value. `yield*` represents that value directly without wrapping it in `Effect.fail`.",
|
|
12095
12510
|
fixes: [{
|
|
12096
12511
|
fixName: "unnecessaryFailYieldableError_fix",
|
|
12097
12512
|
description: "Replace yield* Effect.fail with yield*",
|
|
@@ -12196,7 +12611,7 @@ var unnecessaryPipeChain = createDiagnostic({
|
|
|
12196
12611
|
map4(({ innerCall, pipeCall }) => {
|
|
12197
12612
|
report({
|
|
12198
12613
|
location: node,
|
|
12199
|
-
messageText: `
|
|
12614
|
+
messageText: "This expression contains chained `pipe` calls that can be simplified to a single `pipe` call.",
|
|
12200
12615
|
fixes: [{
|
|
12201
12616
|
fixName: "unnecessaryPipeChain_fix",
|
|
12202
12617
|
description: "Rewrite as single pipe call",
|
|
@@ -12308,12 +12723,17 @@ var unsupportedServiceAccessors = createDiagnostic({
|
|
|
12308
12723
|
var diagnostics = [
|
|
12309
12724
|
outdatedApi,
|
|
12310
12725
|
anyUnknownInErrorContext,
|
|
12726
|
+
asyncFunction,
|
|
12311
12727
|
instanceOfSchema,
|
|
12312
12728
|
catchAllToMapError,
|
|
12313
12729
|
catchUnfailableEffect,
|
|
12314
12730
|
classSelfMismatch,
|
|
12731
|
+
cryptoRandomUUID,
|
|
12732
|
+
cryptoRandomUUIDInEffect,
|
|
12315
12733
|
duplicatePackage,
|
|
12734
|
+
effectDoNotation,
|
|
12316
12735
|
effectFnImplicitAny,
|
|
12736
|
+
effectMapFlatten,
|
|
12317
12737
|
effectGenUsesAdapter,
|
|
12318
12738
|
missingEffectContext,
|
|
12319
12739
|
missingEffectError,
|
|
@@ -12322,6 +12742,8 @@ var diagnostics = [
|
|
|
12322
12742
|
floatingEffect,
|
|
12323
12743
|
effectInFailure,
|
|
12324
12744
|
missingStarInYieldEffectGen,
|
|
12745
|
+
newPromise,
|
|
12746
|
+
lazyPromiseInEffectSync,
|
|
12325
12747
|
unnecessaryEffectGen,
|
|
12326
12748
|
unnecessaryFailYieldableError,
|
|
12327
12749
|
missingReturnYieldStar,
|
|
@@ -12330,6 +12752,8 @@ var diagnostics = [
|
|
|
12330
12752
|
genericEffectServices,
|
|
12331
12753
|
globalFetch,
|
|
12332
12754
|
globalFetchInEffect,
|
|
12755
|
+
processEnv,
|
|
12756
|
+
processEnvInEffect,
|
|
12333
12757
|
returnEffectInGen,
|
|
12334
12758
|
tryCatchInEffectGen,
|
|
12335
12759
|
importFromBarrel,
|
|
@@ -12347,6 +12771,8 @@ var diagnostics = [
|
|
|
12347
12771
|
strictEffectProvide,
|
|
12348
12772
|
unknownInEffectCatch,
|
|
12349
12773
|
runEffectInsideEffect,
|
|
12774
|
+
nestedEffectGenYield,
|
|
12775
|
+
unnecessaryArrowBlock,
|
|
12350
12776
|
schemaUnionOfLiterals,
|
|
12351
12777
|
schemaStructWithTag,
|
|
12352
12778
|
globalErrorInEffectCatch,
|