@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
package/transform.js
CHANGED
|
@@ -24,7 +24,7 @@ __export(transform_exports, {
|
|
|
24
24
|
});
|
|
25
25
|
module.exports = __toCommonJS(transform_exports);
|
|
26
26
|
|
|
27
|
-
// ../../node_modules/.pnpm/effect@4.0.0-beta.
|
|
27
|
+
// ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/Pipeable.js
|
|
28
28
|
var pipeArguments = (self, args3) => {
|
|
29
29
|
switch (args3.length) {
|
|
30
30
|
case 0:
|
|
@@ -57,7 +57,7 @@ var pipeArguments = (self, args3) => {
|
|
|
57
57
|
}
|
|
58
58
|
};
|
|
59
59
|
|
|
60
|
-
// ../../node_modules/.pnpm/effect@4.0.0-beta.
|
|
60
|
+
// ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/Function.js
|
|
61
61
|
var dual = function(arity, body) {
|
|
62
62
|
if (typeof arity === "function") {
|
|
63
63
|
return function() {
|
|
@@ -105,7 +105,7 @@ function pipe(a, ...args3) {
|
|
|
105
105
|
return pipeArguments(a, args3);
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
-
// ../../node_modules/.pnpm/effect@4.0.0-beta.
|
|
108
|
+
// ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/internal/equal.js
|
|
109
109
|
var getAllObjectKeys = (obj) => {
|
|
110
110
|
const keys2 = new Set(Reflect.ownKeys(obj));
|
|
111
111
|
if (obj.constructor === Object) return keys2;
|
|
@@ -128,7 +128,7 @@ var getAllObjectKeys = (obj) => {
|
|
|
128
128
|
};
|
|
129
129
|
var byReferenceInstances = /* @__PURE__ */ new WeakSet();
|
|
130
130
|
|
|
131
|
-
// ../../node_modules/.pnpm/effect@4.0.0-beta.
|
|
131
|
+
// ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/Predicate.js
|
|
132
132
|
function isString(input) {
|
|
133
133
|
return typeof input === "string";
|
|
134
134
|
}
|
|
@@ -149,7 +149,7 @@ function isObjectKeyword(input) {
|
|
|
149
149
|
}
|
|
150
150
|
var hasProperty = /* @__PURE__ */ dual(2, (self, property) => isObjectKeyword(self) && property in self);
|
|
151
151
|
|
|
152
|
-
// ../../node_modules/.pnpm/effect@4.0.0-beta.
|
|
152
|
+
// ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/Hash.js
|
|
153
153
|
var symbol = "~effect/interfaces/Hash";
|
|
154
154
|
var hash = (self) => {
|
|
155
155
|
switch (typeof self) {
|
|
@@ -268,7 +268,7 @@ function withVisitedTracking(obj, fn2) {
|
|
|
268
268
|
return result;
|
|
269
269
|
}
|
|
270
270
|
|
|
271
|
-
// ../../node_modules/.pnpm/effect@4.0.0-beta.
|
|
271
|
+
// ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/Equal.js
|
|
272
272
|
var symbol2 = "~effect/interfaces/Equal";
|
|
273
273
|
function equals() {
|
|
274
274
|
if (arguments.length === 1) {
|
|
@@ -430,10 +430,10 @@ var compareSets = /* @__PURE__ */ makeCompareSet(compareBoth);
|
|
|
430
430
|
var isEqual = (u) => hasProperty(u, symbol2);
|
|
431
431
|
var asEquivalence = () => equals;
|
|
432
432
|
|
|
433
|
-
// ../../node_modules/.pnpm/effect@4.0.0-beta.
|
|
433
|
+
// ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/internal/array.js
|
|
434
434
|
var isArrayNonEmpty = (self) => self.length > 0;
|
|
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/Redactable.js
|
|
437
437
|
var symbolRedactable = /* @__PURE__ */ Symbol.for("~effect/Inspectable/redactable");
|
|
438
438
|
var isRedactable = (u) => hasProperty(u, symbolRedactable);
|
|
439
439
|
function redact(u) {
|
|
@@ -452,7 +452,7 @@ var emptyServiceMap = {
|
|
|
452
452
|
}
|
|
453
453
|
};
|
|
454
454
|
|
|
455
|
-
// ../../node_modules/.pnpm/effect@4.0.0-beta.
|
|
455
|
+
// ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/Formatter.js
|
|
456
456
|
function format(input, options) {
|
|
457
457
|
const space = options?.space ?? 0;
|
|
458
458
|
const seen = /* @__PURE__ */ new WeakSet();
|
|
@@ -531,7 +531,7 @@ function safeToString(input) {
|
|
|
531
531
|
}
|
|
532
532
|
}
|
|
533
533
|
|
|
534
|
-
// ../../node_modules/.pnpm/effect@4.0.0-beta.
|
|
534
|
+
// ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/Inspectable.js
|
|
535
535
|
var NodeInspectSymbol = /* @__PURE__ */ Symbol.for("nodejs.util.inspect.custom");
|
|
536
536
|
var toJson = (input) => {
|
|
537
537
|
try {
|
|
@@ -575,7 +575,7 @@ var Class = class {
|
|
|
575
575
|
}
|
|
576
576
|
};
|
|
577
577
|
|
|
578
|
-
// ../../node_modules/.pnpm/effect@4.0.0-beta.
|
|
578
|
+
// ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/Utils.js
|
|
579
579
|
var SingleShotGen = class _SingleShotGen {
|
|
580
580
|
called = false;
|
|
581
581
|
self;
|
|
@@ -618,7 +618,7 @@ var forced = {
|
|
|
618
618
|
var isNotOptimizedAway = /* @__PURE__ */ standard[InternalTypeId](() => new Error().stack)?.includes(InternalTypeId) === true;
|
|
619
619
|
var internalCall = isNotOptimizedAway ? standard[InternalTypeId] : forced[InternalTypeId];
|
|
620
620
|
|
|
621
|
-
// ../../node_modules/.pnpm/effect@4.0.0-beta.
|
|
621
|
+
// ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/internal/core.js
|
|
622
622
|
var EffectTypeId = `~effect/Effect`;
|
|
623
623
|
var ExitTypeId = `~effect/Exit`;
|
|
624
624
|
var effectVariance = {
|
|
@@ -965,7 +965,7 @@ var DoneVoid = {
|
|
|
965
965
|
value: void 0
|
|
966
966
|
};
|
|
967
967
|
|
|
968
|
-
// ../../node_modules/.pnpm/effect@4.0.0-beta.
|
|
968
|
+
// ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/internal/option.js
|
|
969
969
|
var TypeId = "~effect/data/Option";
|
|
970
970
|
var CommonProto = {
|
|
971
971
|
[TypeId]: {
|
|
@@ -1036,7 +1036,7 @@ var some = (value) => {
|
|
|
1036
1036
|
return a;
|
|
1037
1037
|
};
|
|
1038
1038
|
|
|
1039
|
-
// ../../node_modules/.pnpm/effect@4.0.0-beta.
|
|
1039
|
+
// ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/internal/result.js
|
|
1040
1040
|
var TypeId2 = "~effect/data/Result";
|
|
1041
1041
|
var CommonProto2 = {
|
|
1042
1042
|
[TypeId2]: {
|
|
@@ -1107,13 +1107,13 @@ var succeed = (success) => {
|
|
|
1107
1107
|
return a;
|
|
1108
1108
|
};
|
|
1109
1109
|
|
|
1110
|
-
// ../../node_modules/.pnpm/effect@4.0.0-beta.
|
|
1110
|
+
// ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/Order.js
|
|
1111
1111
|
function make(compare) {
|
|
1112
1112
|
return (self, that) => self === that ? 0 : compare(self, that);
|
|
1113
1113
|
}
|
|
1114
1114
|
var String2 = /* @__PURE__ */ make((self, that) => self < that ? -1 : 1);
|
|
1115
1115
|
|
|
1116
|
-
// ../../node_modules/.pnpm/effect@4.0.0-beta.
|
|
1116
|
+
// ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/Option.js
|
|
1117
1117
|
var none2 = () => none;
|
|
1118
1118
|
var some2 = some;
|
|
1119
1119
|
var isNone2 = isNone;
|
|
@@ -1123,7 +1123,7 @@ var orElse = /* @__PURE__ */ dual(2, (self, that) => isNone2(self) ? that() : se
|
|
|
1123
1123
|
var fromNullishOr = (a) => a == null ? none2() : some2(a);
|
|
1124
1124
|
var getOrUndefined = /* @__PURE__ */ getOrElse(constUndefined);
|
|
1125
1125
|
|
|
1126
|
-
// ../../node_modules/.pnpm/effect@4.0.0-beta.
|
|
1126
|
+
// ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/Result.js
|
|
1127
1127
|
var succeed2 = succeed;
|
|
1128
1128
|
var fail2 = fail;
|
|
1129
1129
|
var isFailure2 = isFailure;
|
|
@@ -1131,7 +1131,7 @@ var isSuccess2 = isSuccess;
|
|
|
1131
1131
|
var map = /* @__PURE__ */ dual(2, (self, f) => isSuccess2(self) ? succeed2(f(self.success)) : fail2(self.failure));
|
|
1132
1132
|
var getOrElse2 = /* @__PURE__ */ dual(2, (self, onFailure) => isFailure2(self) ? onFailure(self.failure) : self.success);
|
|
1133
1133
|
|
|
1134
|
-
// ../../node_modules/.pnpm/effect@4.0.0-beta.
|
|
1134
|
+
// ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/Record.js
|
|
1135
1135
|
var map2 = /* @__PURE__ */ dual(2, (self, f) => {
|
|
1136
1136
|
const out = {
|
|
1137
1137
|
...self
|
|
@@ -1143,7 +1143,7 @@ var map2 = /* @__PURE__ */ dual(2, (self, f) => {
|
|
|
1143
1143
|
});
|
|
1144
1144
|
var keys = (self) => Object.keys(self);
|
|
1145
1145
|
|
|
1146
|
-
// ../../node_modules/.pnpm/effect@4.0.0-beta.
|
|
1146
|
+
// ../../node_modules/.pnpm/effect@4.0.0-beta.43/node_modules/effect/dist/Array.js
|
|
1147
1147
|
var Array2 = globalThis.Array;
|
|
1148
1148
|
var fromIterable = (collection) => Array2.isArray(collection) ? collection : Array2.from(collection);
|
|
1149
1149
|
var append = /* @__PURE__ */ dual(2, (self, last) => [...self, last]);
|
|
@@ -3477,6 +3477,14 @@ function make2(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
|
|
|
3477
3477
|
),
|
|
3478
3478
|
([A, E, R]) => ({ A, E, R })
|
|
3479
3479
|
);
|
|
3480
|
+
const streamVarianceStruct = (type, atLocation) => map4(
|
|
3481
|
+
all(
|
|
3482
|
+
varianceStructCovariantType(type, atLocation, "_A"),
|
|
3483
|
+
varianceStructCovariantType(type, atLocation, "_E"),
|
|
3484
|
+
varianceStructCovariantType(type, atLocation, "_R")
|
|
3485
|
+
),
|
|
3486
|
+
([A, E, R]) => ({ A, E, R })
|
|
3487
|
+
);
|
|
3480
3488
|
const layerVarianceStruct = (type, atLocation) => map4(
|
|
3481
3489
|
all(
|
|
3482
3490
|
varianceStructContravariantType(type, atLocation, "_ROut"),
|
|
@@ -3523,6 +3531,21 @@ function make2(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
|
|
|
3523
3531
|
"TypeParser.strictEffectType",
|
|
3524
3532
|
(type) => type
|
|
3525
3533
|
);
|
|
3534
|
+
const streamType = cachedBy(
|
|
3535
|
+
fn("TypeParser.streamType")(function* (type, atLocation) {
|
|
3536
|
+
if (supportedEffect() === "v3") {
|
|
3537
|
+
return yield* effectType(type, atLocation);
|
|
3538
|
+
}
|
|
3539
|
+
const typeIdSymbol = typeChecker.getPropertyOfType(type, "~effect/Stream");
|
|
3540
|
+
if (!typeIdSymbol) {
|
|
3541
|
+
return yield* typeParserIssue("Type is not a stream", type, atLocation);
|
|
3542
|
+
}
|
|
3543
|
+
const typeIdType = typeChecker.getTypeOfSymbolAtLocation(typeIdSymbol, atLocation);
|
|
3544
|
+
return yield* streamVarianceStruct(typeIdType, atLocation);
|
|
3545
|
+
}),
|
|
3546
|
+
"TypeParser.streamType",
|
|
3547
|
+
(type) => type
|
|
3548
|
+
);
|
|
3526
3549
|
const isEffectTypeSourceFile = cachedBy(
|
|
3527
3550
|
fn("TypeParser.isEffectTypeSourceFile")(function* (sourceFile) {
|
|
3528
3551
|
const moduleSymbol = typeChecker.getSymbolAtLocation(sourceFile);
|
|
@@ -4094,33 +4117,33 @@ function make2(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
|
|
|
4094
4117
|
),
|
|
4095
4118
|
([Identifier, Service]) => ({ Identifier, Service })
|
|
4096
4119
|
);
|
|
4097
|
-
const serviceVarianceStruct = (type, atLocation) => map4(
|
|
4098
|
-
all(
|
|
4099
|
-
varianceStructInvariantType(type, atLocation, "_Identifier"),
|
|
4100
|
-
varianceStructInvariantType(type, atLocation, "_Service")
|
|
4101
|
-
),
|
|
4102
|
-
([Identifier, Service]) => ({ Identifier, Service })
|
|
4103
|
-
);
|
|
4104
4120
|
const serviceType = cachedBy(
|
|
4105
4121
|
fn("TypeParser.serviceType")(function* (type, atLocation) {
|
|
4122
|
+
if (supportedEffect() !== "v4") return yield* typeParserIssue("v4 only");
|
|
4106
4123
|
yield* pipeableType(type, atLocation);
|
|
4107
|
-
const
|
|
4108
|
-
|
|
4109
|
-
|
|
4110
|
-
if (propertiesSymbols.length === 0) {
|
|
4111
|
-
return yield* typeParserIssue("Type has no tag variance struct", type, atLocation);
|
|
4124
|
+
const typeIdSymbol = typeChecker.getPropertyOfType(type, "~effect/ServiceMap/Service");
|
|
4125
|
+
if (!typeIdSymbol) {
|
|
4126
|
+
return yield* typeParserIssue("Type has no service key type id", type, atLocation);
|
|
4112
4127
|
}
|
|
4113
|
-
|
|
4114
|
-
|
|
4115
|
-
|
|
4116
|
-
|
|
4117
|
-
|
|
4128
|
+
const identifierSymbol = typeChecker.getPropertyOfType(type, "Identifier");
|
|
4129
|
+
if (!identifierSymbol) {
|
|
4130
|
+
return yield* typeParserIssue("Type has no 'Identifier' property", type, atLocation);
|
|
4131
|
+
}
|
|
4132
|
+
const serviceSymbol = typeChecker.getPropertyOfType(type, "Service");
|
|
4133
|
+
if (!serviceSymbol) {
|
|
4134
|
+
return yield* typeParserIssue("Type has no 'Service' property", type, atLocation);
|
|
4135
|
+
}
|
|
4136
|
+
return {
|
|
4137
|
+
Identifier: typeChecker.getTypeOfSymbolAtLocation(identifierSymbol, atLocation),
|
|
4138
|
+
Service: typeChecker.getTypeOfSymbolAtLocation(serviceSymbol, atLocation)
|
|
4139
|
+
};
|
|
4118
4140
|
}),
|
|
4119
4141
|
"TypeParser.serviceType",
|
|
4120
4142
|
(type) => type
|
|
4121
4143
|
);
|
|
4122
4144
|
const contextTag = cachedBy(
|
|
4123
4145
|
fn("TypeParser.contextTag")(function* (type, atLocation) {
|
|
4146
|
+
if (supportedEffect() !== "v3") return yield* typeParserIssue("v3 only");
|
|
4124
4147
|
yield* pipeableType(type, atLocation);
|
|
4125
4148
|
const propertiesSymbols = typeChecker.getPropertiesOfType(type).filter(
|
|
4126
4149
|
(_) => _.flags & ts.SymbolFlags.Property && !(_.flags & ts.SymbolFlags.Optional) && _.valueDeclaration
|
|
@@ -4261,6 +4284,19 @@ function make2(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
|
|
|
4261
4284
|
"TypeParser.promiseLike",
|
|
4262
4285
|
(type) => type
|
|
4263
4286
|
);
|
|
4287
|
+
const promiseType = cachedBy(
|
|
4288
|
+
function(type, atLocation) {
|
|
4289
|
+
const promiseSymbol = typeChecker.resolveName("Promise", void 0, ts.SymbolFlags.Type, false);
|
|
4290
|
+
if (!promiseSymbol) return typeParserIssue("global Promise type not found", type, atLocation);
|
|
4291
|
+
const globalPromiseType = typeChecker.getDeclaredTypeOfSymbol(promiseSymbol);
|
|
4292
|
+
if (type === globalPromiseType || "target" in type && type.target === globalPromiseType || typeChecker.isTypeAssignableTo(type, globalPromiseType)) {
|
|
4293
|
+
return succeed3(type);
|
|
4294
|
+
}
|
|
4295
|
+
return typeParserIssue("type is not a Promise", type, atLocation);
|
|
4296
|
+
},
|
|
4297
|
+
"TypeParser.promiseType",
|
|
4298
|
+
(type) => type
|
|
4299
|
+
);
|
|
4264
4300
|
const extendsSchemaClass = cachedBy(
|
|
4265
4301
|
fn("TypeParser.extendsSchemaClass")(function* (atLocation) {
|
|
4266
4302
|
if (!atLocation.name) {
|
|
@@ -5258,6 +5294,7 @@ function make2(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
|
|
|
5258
5294
|
isServiceMapTypeSourceFile,
|
|
5259
5295
|
isNodeReferenceToServiceMapModuleApi,
|
|
5260
5296
|
effectType,
|
|
5297
|
+
streamType,
|
|
5261
5298
|
strictEffectType,
|
|
5262
5299
|
layerType,
|
|
5263
5300
|
fiberType,
|
|
@@ -5279,6 +5316,7 @@ function make2(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
|
|
|
5279
5316
|
singleArgCall,
|
|
5280
5317
|
scopeType,
|
|
5281
5318
|
promiseLike,
|
|
5319
|
+
promiseType,
|
|
5282
5320
|
extendsEffectTag,
|
|
5283
5321
|
extendsEffectService,
|
|
5284
5322
|
extendsServiceMapService,
|
|
@@ -5407,6 +5445,37 @@ var anyUnknownInErrorContext = createDiagnostic({
|
|
|
5407
5445
|
})
|
|
5408
5446
|
});
|
|
5409
5447
|
|
|
5448
|
+
// src/diagnostics/asyncFunction.ts
|
|
5449
|
+
var asyncFunction = createDiagnostic({
|
|
5450
|
+
name: "asyncFunction",
|
|
5451
|
+
code: 69,
|
|
5452
|
+
description: "Warns when declaring async functions and suggests using Effect values and Effect.gen for async control flow",
|
|
5453
|
+
group: "effectNative",
|
|
5454
|
+
severity: "off",
|
|
5455
|
+
fixable: false,
|
|
5456
|
+
supportedEffect: ["v3", "v4"],
|
|
5457
|
+
apply: fn("asyncFunction.apply")(function* (sourceFile, report) {
|
|
5458
|
+
const ts = yield* service(TypeScriptApi);
|
|
5459
|
+
const hasAsyncModifier = (node) => ts.getModifiers(node)?.some((modifier) => modifier.kind === ts.SyntaxKind.AsyncKeyword) === true;
|
|
5460
|
+
const visit = (node) => {
|
|
5461
|
+
if (!ts.isFunctionDeclaration(node) && !ts.isFunctionExpression(node) && !ts.isArrowFunction(node) && !ts.isMethodDeclaration(node)) {
|
|
5462
|
+
ts.forEachChild(node, visit);
|
|
5463
|
+
return void 0;
|
|
5464
|
+
}
|
|
5465
|
+
if (hasAsyncModifier(node)) {
|
|
5466
|
+
report({
|
|
5467
|
+
location: node,
|
|
5468
|
+
messageText: "This code declares an async function, consider representing this async control flow with Effect values and `Effect.gen`.",
|
|
5469
|
+
fixes: []
|
|
5470
|
+
});
|
|
5471
|
+
}
|
|
5472
|
+
ts.forEachChild(node, visit);
|
|
5473
|
+
return void 0;
|
|
5474
|
+
};
|
|
5475
|
+
ts.forEachChild(sourceFile, visit);
|
|
5476
|
+
})
|
|
5477
|
+
});
|
|
5478
|
+
|
|
5410
5479
|
// src/diagnostics/catchAllToMapError.ts
|
|
5411
5480
|
var catchAllToMapError = createDiagnostic({
|
|
5412
5481
|
name: "catchAllToMapError",
|
|
@@ -5485,7 +5554,7 @@ var catchAllToMapError = createDiagnostic({
|
|
|
5485
5554
|
const { failArg, failCall } = failCallInfo;
|
|
5486
5555
|
report({
|
|
5487
5556
|
location: transformation.callee,
|
|
5488
|
-
messageText:
|
|
5557
|
+
messageText: `\`Effect.mapError\` expresses the same error-type transformation more directly than \`Effect.${catchAllName}\` followed by \`Effect.fail\`.`,
|
|
5489
5558
|
fixes: [{
|
|
5490
5559
|
fixName: "catchAllToMapError_fix",
|
|
5491
5560
|
description: "Replace with Effect.mapError",
|
|
@@ -5549,7 +5618,7 @@ var catchUnfailableEffect = createDiagnostic({
|
|
|
5549
5618
|
if (E.flags & ts.TypeFlags.Never) {
|
|
5550
5619
|
report({
|
|
5551
5620
|
location: transformation.callee,
|
|
5552
|
-
messageText:
|
|
5621
|
+
messageText: "The previous Effect does not fail, so this error-handling branch will never run.",
|
|
5553
5622
|
fixes: []
|
|
5554
5623
|
});
|
|
5555
5624
|
}
|
|
@@ -5612,7 +5681,7 @@ var classSelfMismatch = createDiagnostic({
|
|
|
5612
5681
|
if (actualName !== expectedName) {
|
|
5613
5682
|
report({
|
|
5614
5683
|
location: selfTypeNode,
|
|
5615
|
-
messageText: `Self type parameter should be
|
|
5684
|
+
messageText: `The \`Self\` type parameter for this class should be \`${expectedName}\`.`,
|
|
5616
5685
|
fixes: [{
|
|
5617
5686
|
fixName: "classSelfMismatch_fix",
|
|
5618
5687
|
description: `Replace '${actualName}' with '${expectedName}'`,
|
|
@@ -5635,6 +5704,59 @@ var classSelfMismatch = createDiagnostic({
|
|
|
5635
5704
|
})
|
|
5636
5705
|
});
|
|
5637
5706
|
|
|
5707
|
+
// src/diagnostics/cryptoRandomUUIDInEffect.ts
|
|
5708
|
+
var makeCryptoRandomUUIDApply = (checkInEffect) => fn(`cryptoRandomUUID${checkInEffect ? "InEffect" : ""}.apply`)(function* (sourceFile, report) {
|
|
5709
|
+
const ts = yield* service(TypeScriptApi);
|
|
5710
|
+
const typeChecker = yield* service(TypeCheckerApi);
|
|
5711
|
+
const typeCheckerUtils = yield* service(TypeCheckerUtils);
|
|
5712
|
+
const typeParser = yield* service(TypeParser);
|
|
5713
|
+
const cryptoSymbol = typeChecker.resolveName("crypto", void 0, ts.SymbolFlags.Value, false);
|
|
5714
|
+
if (!cryptoSymbol) return;
|
|
5715
|
+
const nodeToVisit = [];
|
|
5716
|
+
const appendNodeToVisit = (node) => {
|
|
5717
|
+
nodeToVisit.push(node);
|
|
5718
|
+
return void 0;
|
|
5719
|
+
};
|
|
5720
|
+
ts.forEachChild(sourceFile, appendNodeToVisit);
|
|
5721
|
+
while (nodeToVisit.length > 0) {
|
|
5722
|
+
const node = nodeToVisit.shift();
|
|
5723
|
+
ts.forEachChild(node, appendNodeToVisit);
|
|
5724
|
+
if (!ts.isCallExpression(node) || !ts.isPropertyAccessExpression(node.expression) || ts.idText(node.expression.name) !== "randomUUID") continue;
|
|
5725
|
+
const symbol3 = typeChecker.getSymbolAtLocation(node.expression.expression);
|
|
5726
|
+
if (!symbol3) continue;
|
|
5727
|
+
if (typeCheckerUtils.resolveToGlobalSymbol(symbol3) !== cryptoSymbol) continue;
|
|
5728
|
+
const { inEffect } = yield* typeParser.findEnclosingScopes(node);
|
|
5729
|
+
if (inEffect !== checkInEffect) continue;
|
|
5730
|
+
report({
|
|
5731
|
+
location: node,
|
|
5732
|
+
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.",
|
|
5733
|
+
fixes: []
|
|
5734
|
+
});
|
|
5735
|
+
}
|
|
5736
|
+
});
|
|
5737
|
+
var cryptoRandomUUIDInEffect = createDiagnostic({
|
|
5738
|
+
name: "cryptoRandomUUIDInEffect",
|
|
5739
|
+
code: 67,
|
|
5740
|
+
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",
|
|
5741
|
+
group: "effectNative",
|
|
5742
|
+
severity: "off",
|
|
5743
|
+
fixable: false,
|
|
5744
|
+
supportedEffect: ["v4"],
|
|
5745
|
+
apply: makeCryptoRandomUUIDApply(true)
|
|
5746
|
+
});
|
|
5747
|
+
|
|
5748
|
+
// src/diagnostics/cryptoRandomUUID.ts
|
|
5749
|
+
var cryptoRandomUUID = createDiagnostic({
|
|
5750
|
+
name: "cryptoRandomUUID",
|
|
5751
|
+
code: 66,
|
|
5752
|
+
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",
|
|
5753
|
+
group: "effectNative",
|
|
5754
|
+
severity: "off",
|
|
5755
|
+
fixable: false,
|
|
5756
|
+
supportedEffect: ["v4"],
|
|
5757
|
+
apply: makeCryptoRandomUUIDApply(false)
|
|
5758
|
+
});
|
|
5759
|
+
|
|
5638
5760
|
// src/core/KeyBuilder.ts
|
|
5639
5761
|
var makeKeyBuilder = fn("KeyBuilder")(
|
|
5640
5762
|
function* (sourceFile) {
|
|
@@ -5802,7 +5924,7 @@ var deterministicKeys = createDiagnostic({
|
|
|
5802
5924
|
if (actualIdentifier !== expectedKey) {
|
|
5803
5925
|
report({
|
|
5804
5926
|
location: keyStringLiteral,
|
|
5805
|
-
messageText: `
|
|
5927
|
+
messageText: `This key does not match the deterministic key for this declaration. The expected key is \`${expectedKey}\`.`,
|
|
5806
5928
|
fixes: [{
|
|
5807
5929
|
fixName: "deterministicKeys_fix",
|
|
5808
5930
|
description: `Replace '${actualIdentifier}' with '${expectedKey}'`,
|
|
@@ -5841,9 +5963,8 @@ var duplicatePackage = createDiagnostic({
|
|
|
5841
5963
|
const versions = Object.keys(resolvedPackages[packageName]);
|
|
5842
5964
|
report({
|
|
5843
5965
|
location: sourceFile.statements[0],
|
|
5844
|
-
messageText: `
|
|
5845
|
-
|
|
5846
|
-
If this is intended set the LSP config "allowedDuplicatedPackages" to ${JSON.stringify(options.allowedDuplicatedPackages.concat([packageName]))}.
|
|
5966
|
+
messageText: `Multiple versions of package \`${packageName}\` were detected: ${versions.join(", ")}. Package duplication can change runtime identity and type equality across Effect modules.
|
|
5967
|
+
If this is intentional, set the LSP config \`allowedDuplicatedPackages\` to ${JSON.stringify(options.allowedDuplicatedPackages.concat([packageName]))}.
|
|
5847
5968
|
|
|
5848
5969
|
${versions.map((version) => `- found ${version} at ${resolvedPackages[packageName][version]}`).join("\n")}`,
|
|
5849
5970
|
fixes: []
|
|
@@ -5853,6 +5974,40 @@ ${versions.map((version) => `- found ${version} at ${resolvedPackages[packageNam
|
|
|
5853
5974
|
})
|
|
5854
5975
|
});
|
|
5855
5976
|
|
|
5977
|
+
// src/diagnostics/effectDoNotation.ts
|
|
5978
|
+
var effectDoNotation = createDiagnostic({
|
|
5979
|
+
name: "effectDoNotation",
|
|
5980
|
+
code: 73,
|
|
5981
|
+
description: "Suggests using Effect.gen or Effect.fn instead of the Effect.Do notation helpers",
|
|
5982
|
+
group: "style",
|
|
5983
|
+
severity: "off",
|
|
5984
|
+
fixable: false,
|
|
5985
|
+
supportedEffect: ["v3", "v4"],
|
|
5986
|
+
apply: fn("effectDoNotation.apply")(function* (sourceFile, report) {
|
|
5987
|
+
const ts = yield* service(TypeScriptApi);
|
|
5988
|
+
const typeParser = yield* service(TypeParser);
|
|
5989
|
+
const nodeToVisit = [];
|
|
5990
|
+
const appendNodeToVisit = (node) => {
|
|
5991
|
+
nodeToVisit.push(node);
|
|
5992
|
+
return void 0;
|
|
5993
|
+
};
|
|
5994
|
+
ts.forEachChild(sourceFile, appendNodeToVisit);
|
|
5995
|
+
while (nodeToVisit.length > 0) {
|
|
5996
|
+
const node = nodeToVisit.shift();
|
|
5997
|
+
const isReference = yield* orUndefined(typeParser.isNodeReferenceToEffectModuleApi("Do")(node));
|
|
5998
|
+
if (isReference) {
|
|
5999
|
+
report({
|
|
6000
|
+
location: node,
|
|
6001
|
+
messageText: "This uses the Effect do emulation. `Effect.gen` or `Effect.fn` achieve the same result with native JS scopes.",
|
|
6002
|
+
fixes: []
|
|
6003
|
+
});
|
|
6004
|
+
continue;
|
|
6005
|
+
}
|
|
6006
|
+
ts.forEachChild(node, appendNodeToVisit);
|
|
6007
|
+
}
|
|
6008
|
+
})
|
|
6009
|
+
});
|
|
6010
|
+
|
|
5856
6011
|
// src/diagnostics/effectFnIife.ts
|
|
5857
6012
|
var effectFnIife = createDiagnostic({
|
|
5858
6013
|
name: "effectFnIife",
|
|
@@ -5951,7 +6106,7 @@ var effectFnIife = createDiagnostic({
|
|
|
5951
6106
|
const traceExpressionText = traceExpression ? sourceFile.text.slice(traceExpression.pos, traceExpression.end) : void 0;
|
|
5952
6107
|
report({
|
|
5953
6108
|
location: node,
|
|
5954
|
-
messageText:
|
|
6109
|
+
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` : ``}.`,
|
|
5955
6110
|
fixes
|
|
5956
6111
|
});
|
|
5957
6112
|
}
|
|
@@ -6036,7 +6191,7 @@ var effectFnImplicitAny = createDiagnostic({
|
|
|
6036
6191
|
const parameterName = getParameterName(ts, parameter.name);
|
|
6037
6192
|
report({
|
|
6038
6193
|
location: parameter.name,
|
|
6039
|
-
messageText: `Parameter
|
|
6194
|
+
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.`,
|
|
6040
6195
|
fixes: []
|
|
6041
6196
|
});
|
|
6042
6197
|
}
|
|
@@ -6632,7 +6787,7 @@ var effectFnOpportunity = createDiagnostic({
|
|
|
6632
6787
|
const expectedSignature = generateExpectedSignature();
|
|
6633
6788
|
report({
|
|
6634
6789
|
location: nameIdentifier ?? targetNode,
|
|
6635
|
-
messageText: `
|
|
6790
|
+
messageText: `This expression can be rewritten in the reusable function form \`${expectedSignature}\`.`,
|
|
6636
6791
|
fixes
|
|
6637
6792
|
});
|
|
6638
6793
|
}
|
|
@@ -6799,6 +6954,43 @@ var effectInVoidSuccess = createDiagnostic({
|
|
|
6799
6954
|
})
|
|
6800
6955
|
});
|
|
6801
6956
|
|
|
6957
|
+
// src/diagnostics/effectMapFlatten.ts
|
|
6958
|
+
var effectMapFlatten = createDiagnostic({
|
|
6959
|
+
name: "effectMapFlatten",
|
|
6960
|
+
code: 74,
|
|
6961
|
+
description: "Suggests using Effect.flatMap instead of Effect.map followed by Effect.flatten in piping flows",
|
|
6962
|
+
group: "style",
|
|
6963
|
+
severity: "suggestion",
|
|
6964
|
+
fixable: false,
|
|
6965
|
+
supportedEffect: ["v3", "v4"],
|
|
6966
|
+
apply: fn("effectMapFlatten.apply")(function* (sourceFile, report) {
|
|
6967
|
+
const typeParser = yield* service(TypeParser);
|
|
6968
|
+
const flows = yield* typeParser.pipingFlows(false)(sourceFile);
|
|
6969
|
+
for (const flow2 of flows) {
|
|
6970
|
+
for (let index = 0; index < flow2.transformations.length - 1; index++) {
|
|
6971
|
+
const mapTransformation = flow2.transformations[index];
|
|
6972
|
+
const flattenTransformation = flow2.transformations[index + 1];
|
|
6973
|
+
if (!mapTransformation || !flattenTransformation || !mapTransformation?.args || flattenTransformation?.args || mapTransformation.kind !== "pipe" && mapTransformation.kind !== "pipeable" || flattenTransformation.kind !== mapTransformation.kind) {
|
|
6974
|
+
continue;
|
|
6975
|
+
}
|
|
6976
|
+
const isMapCall = yield* orUndefined(
|
|
6977
|
+
typeParser.isNodeReferenceToEffectModuleApi("map")(mapTransformation.callee)
|
|
6978
|
+
);
|
|
6979
|
+
const isFlattenCall = yield* orUndefined(
|
|
6980
|
+
typeParser.isNodeReferenceToEffectModuleApi("flatten")(flattenTransformation.callee)
|
|
6981
|
+
);
|
|
6982
|
+
if (isMapCall && isFlattenCall) {
|
|
6983
|
+
report({
|
|
6984
|
+
location: flattenTransformation.callee,
|
|
6985
|
+
messageText: "`Effect.map` + `Effect.flatten` is the same as `Effect.flatMap` that expresses the same steps more directly.",
|
|
6986
|
+
fixes: []
|
|
6987
|
+
});
|
|
6988
|
+
}
|
|
6989
|
+
}
|
|
6990
|
+
}
|
|
6991
|
+
})
|
|
6992
|
+
});
|
|
6993
|
+
|
|
6802
6994
|
// src/diagnostics/effectMapVoid.ts
|
|
6803
6995
|
var effectMapVoid = createDiagnostic({
|
|
6804
6996
|
name: "effectMapVoid",
|
|
@@ -6844,7 +7036,7 @@ var effectMapVoid = createDiagnostic({
|
|
|
6844
7036
|
if (isNone2(match2)) continue;
|
|
6845
7037
|
report({
|
|
6846
7038
|
location: node.expression,
|
|
6847
|
-
messageText: "
|
|
7039
|
+
messageText: "This expression discards the success value through mapping. `Effect.asVoid` represents that form directly.",
|
|
6848
7040
|
fixes: [{
|
|
6849
7041
|
fixName: "effectMapVoid_fix",
|
|
6850
7042
|
description: "Replace with Effect.asVoid",
|
|
@@ -6899,7 +7091,7 @@ var effectSucceedWithVoid = createDiagnostic({
|
|
|
6899
7091
|
if (!tsUtils.isVoidExpression(argument)) continue;
|
|
6900
7092
|
report({
|
|
6901
7093
|
location: node,
|
|
6902
|
-
messageText: "Effect.void
|
|
7094
|
+
messageText: "`Effect.void` represents the same outcome as `Effect.succeed(undefined)` or `Effect.succeed(void 0)`.",
|
|
6903
7095
|
fixes: [{
|
|
6904
7096
|
fixName: "effectSucceedWithVoid_fix",
|
|
6905
7097
|
description: "Replace with Effect.void",
|
|
@@ -6961,7 +7153,7 @@ var extendsNativeError = createDiagnostic({
|
|
|
6961
7153
|
if (isNativeError) {
|
|
6962
7154
|
report({
|
|
6963
7155
|
location: node.name ?? typeExpression,
|
|
6964
|
-
messageText: "
|
|
7156
|
+
messageText: "This class extends the native `Error` type directly. Untagged native errors lose distinction in the Effect failure channel.",
|
|
6965
7157
|
fixes: []
|
|
6966
7158
|
});
|
|
6967
7159
|
}
|
|
@@ -7006,7 +7198,12 @@ var floatingEffect = createDiagnostic({
|
|
|
7006
7198
|
if (!isFloatingExpression(node)) continue;
|
|
7007
7199
|
const type = typeCheckerUtils.getTypeAtLocation(node.expression);
|
|
7008
7200
|
if (!type) continue;
|
|
7009
|
-
const effect = yield* option(
|
|
7201
|
+
const effect = yield* option(
|
|
7202
|
+
pipe(
|
|
7203
|
+
typeParser.effectType(type, node.expression),
|
|
7204
|
+
orElse2(() => typeParser.streamType(type, node.expression))
|
|
7205
|
+
)
|
|
7206
|
+
);
|
|
7010
7207
|
if (isSome2(effect)) {
|
|
7011
7208
|
const allowedFloatingEffects = yield* pipe(
|
|
7012
7209
|
typeParser.fiberType(type, node.expression),
|
|
@@ -7015,10 +7212,9 @@ var floatingEffect = createDiagnostic({
|
|
|
7015
7212
|
);
|
|
7016
7213
|
if (isNone2(allowedFloatingEffects)) {
|
|
7017
7214
|
const isStrictEffect = yield* option(typeParser.strictEffectType(type, node.expression));
|
|
7018
|
-
const name = isSome2(isStrictEffect) ? "Effect" : "Effect-able " + typeChecker.typeToString(type);
|
|
7019
7215
|
report({
|
|
7020
7216
|
location: node,
|
|
7021
|
-
messageText:
|
|
7217
|
+
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.`,
|
|
7022
7218
|
fixes: []
|
|
7023
7219
|
});
|
|
7024
7220
|
}
|
|
@@ -7062,6 +7258,7 @@ var genericEffectServices = createDiagnostic({
|
|
|
7062
7258
|
for (const [type, reportAt] of typesToCheck) {
|
|
7063
7259
|
yield* pipe(
|
|
7064
7260
|
typeParser.contextTag(type, node),
|
|
7261
|
+
orElse2(() => typeParser.serviceType(type, node)),
|
|
7065
7262
|
map4(() => {
|
|
7066
7263
|
report({
|
|
7067
7264
|
location: reportAt,
|
|
@@ -7113,7 +7310,7 @@ var makeGlobalConsoleApply = (checkInEffect) => fn(`globalConsole${checkInEffect
|
|
|
7113
7310
|
if (inEffect !== checkInEffect) continue;
|
|
7114
7311
|
report({
|
|
7115
7312
|
location: node,
|
|
7116
|
-
messageText: checkInEffect ? `
|
|
7313
|
+
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}\`.`,
|
|
7117
7314
|
fixes: []
|
|
7118
7315
|
});
|
|
7119
7316
|
}
|
|
@@ -7162,10 +7359,10 @@ var makeGlobalDateApply = (checkInEffect) => fn(`globalDate${checkInEffect ? "In
|
|
|
7162
7359
|
let objectNode;
|
|
7163
7360
|
if (ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression) && ts.idText(node.expression.name) === "now") {
|
|
7164
7361
|
objectNode = node.expression.expression;
|
|
7165
|
-
messageText = checkInEffect ? "
|
|
7362
|
+
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.";
|
|
7166
7363
|
} else if (ts.isNewExpression(node)) {
|
|
7167
7364
|
objectNode = node.expression;
|
|
7168
|
-
messageText = checkInEffect ? "
|
|
7365
|
+
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.";
|
|
7169
7366
|
}
|
|
7170
7367
|
if (!messageText || !objectNode) continue;
|
|
7171
7368
|
const symbol3 = typeChecker.getSymbolAtLocation(objectNode);
|
|
@@ -7248,7 +7445,7 @@ var globalErrorInEffectCatch = createDiagnostic({
|
|
|
7248
7445
|
);
|
|
7249
7446
|
report({
|
|
7250
7447
|
location: node.expression,
|
|
7251
|
-
messageText: `The
|
|
7448
|
+
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.`,
|
|
7252
7449
|
fixes: []
|
|
7253
7450
|
});
|
|
7254
7451
|
}
|
|
@@ -7328,7 +7525,7 @@ var makeGlobalFetchApply = (checkInEffect) => fn(`globalFetch${checkInEffect ? "
|
|
|
7328
7525
|
if (!fetchSymbol) return;
|
|
7329
7526
|
const effectVersion = typeParser.supportedEffect();
|
|
7330
7527
|
const packageName = effectVersion === "v3" ? "@effect/platform" : "effect/unstable/http";
|
|
7331
|
-
const messageText = checkInEffect ? `
|
|
7528
|
+
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}\`.`;
|
|
7332
7529
|
const nodeToVisit = [];
|
|
7333
7530
|
const appendNodeToVisit = (node) => {
|
|
7334
7531
|
nodeToVisit.push(node);
|
|
@@ -7401,7 +7598,7 @@ var makeGlobalRandomApply = (checkInEffect) => fn(`globalRandom${checkInEffect ?
|
|
|
7401
7598
|
if (inEffect !== checkInEffect) continue;
|
|
7402
7599
|
report({
|
|
7403
7600
|
location: node,
|
|
7404
|
-
messageText: checkInEffect ? "
|
|
7601
|
+
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.",
|
|
7405
7602
|
fixes: []
|
|
7406
7603
|
});
|
|
7407
7604
|
}
|
|
@@ -7432,12 +7629,12 @@ var globalRandom = createDiagnostic({
|
|
|
7432
7629
|
// src/diagnostics/globalTimersInEffect.ts
|
|
7433
7630
|
var timerAlternatives = {
|
|
7434
7631
|
"setTimeout": {
|
|
7435
|
-
inEffect: "
|
|
7436
|
-
outsideEffect: "
|
|
7632
|
+
inEffect: "This Effect code uses `setTimeout`, the corresponding timer API in this context is `Effect.sleep or Schedule` from Effect.",
|
|
7633
|
+
outsideEffect: "This code uses `setTimeout`, the corresponding Effect timer API is `Effect.sleep or Schedule` from Effect."
|
|
7437
7634
|
},
|
|
7438
7635
|
"setInterval": {
|
|
7439
|
-
inEffect: "
|
|
7440
|
-
outsideEffect: "
|
|
7636
|
+
inEffect: "This Effect code uses `setInterval`, the corresponding timer API in this context is `Schedule or Effect.repeat` from Effect.",
|
|
7637
|
+
outsideEffect: "This code uses `setInterval`, the corresponding Effect timer API is `Schedule or Effect.repeat` from Effect."
|
|
7441
7638
|
}
|
|
7442
7639
|
};
|
|
7443
7640
|
var makeGlobalTimersApply = (checkInEffect) => fn(`globalTimers${checkInEffect ? "InEffect" : ""}.apply`)(function* (sourceFile, report) {
|
|
@@ -7679,7 +7876,7 @@ var instanceOfSchema = createDiagnostic({
|
|
|
7679
7876
|
if (isSchemaType._tag === "Some") {
|
|
7680
7877
|
report({
|
|
7681
7878
|
location: node,
|
|
7682
|
-
messageText: "
|
|
7879
|
+
messageText: "This code uses `instanceof` with an Effect Schema type. `Schema.is` is the schema-aware runtime check for this case.",
|
|
7683
7880
|
fixes: [{
|
|
7684
7881
|
fixName: "instanceOfSchema_fix",
|
|
7685
7882
|
description: "Replace with Schema.is",
|
|
@@ -7825,6 +8022,55 @@ var layerMergeAllWithDependencies = createDiagnostic({
|
|
|
7825
8022
|
})
|
|
7826
8023
|
});
|
|
7827
8024
|
|
|
8025
|
+
// src/diagnostics/lazyPromiseInEffectSync.ts
|
|
8026
|
+
var lazyPromiseInEffectSync = createDiagnostic({
|
|
8027
|
+
name: "lazyPromiseInEffectSync",
|
|
8028
|
+
code: 70,
|
|
8029
|
+
description: "Warns when Effect.sync lazily returns a Promise instead of using an async Effect constructor",
|
|
8030
|
+
group: "antipattern",
|
|
8031
|
+
severity: "warning",
|
|
8032
|
+
fixable: false,
|
|
8033
|
+
supportedEffect: ["v3", "v4"],
|
|
8034
|
+
apply: fn("lazyPromiseInEffectSync.apply")(function* (sourceFile, report) {
|
|
8035
|
+
const ts = yield* service(TypeScriptApi);
|
|
8036
|
+
const typeChecker = yield* service(TypeCheckerApi);
|
|
8037
|
+
const typeCheckerUtils = yield* service(TypeCheckerUtils);
|
|
8038
|
+
const typeParser = yield* service(TypeParser);
|
|
8039
|
+
const nodeToVisit = [];
|
|
8040
|
+
const appendNodeToVisit = (node) => {
|
|
8041
|
+
nodeToVisit.push(node);
|
|
8042
|
+
return void 0;
|
|
8043
|
+
};
|
|
8044
|
+
ts.forEachChild(sourceFile, appendNodeToVisit);
|
|
8045
|
+
while (nodeToVisit.length > 0) {
|
|
8046
|
+
const node = nodeToVisit.shift();
|
|
8047
|
+
ts.forEachChild(node, appendNodeToVisit);
|
|
8048
|
+
if (!ts.isCallExpression(node)) continue;
|
|
8049
|
+
const isSyncCall = yield* orUndefined(
|
|
8050
|
+
typeParser.isNodeReferenceToEffectModuleApi("sync")(node.expression)
|
|
8051
|
+
);
|
|
8052
|
+
if (!isSyncCall) continue;
|
|
8053
|
+
const lazyArg = node.arguments[0];
|
|
8054
|
+
if (!lazyArg) continue;
|
|
8055
|
+
const lazyArgType = typeCheckerUtils.getTypeAtLocation(lazyArg);
|
|
8056
|
+
if (!lazyArgType) continue;
|
|
8057
|
+
const entries = typeCheckerUtils.unrollUnionMembers(lazyArgType).flatMap(
|
|
8058
|
+
(member) => typeChecker.getSignaturesOfType(member, ts.SignatureKind.Call).map(
|
|
8059
|
+
(signature) => typeParser.promiseType(typeChecker.getReturnTypeOfSignature(signature), lazyArg)
|
|
8060
|
+
)
|
|
8061
|
+
);
|
|
8062
|
+
if (entries.length === 0) continue;
|
|
8063
|
+
const promiseReturn = yield* orUndefined(firstSuccessOf(entries));
|
|
8064
|
+
if (!promiseReturn) continue;
|
|
8065
|
+
report({
|
|
8066
|
+
location: lazyArg,
|
|
8067
|
+
messageText: "This `Effect.sync` thunk returns a Promise. Use `Effect.promise` or `Effect.tryPromise` to represent async work.",
|
|
8068
|
+
fixes: []
|
|
8069
|
+
});
|
|
8070
|
+
}
|
|
8071
|
+
})
|
|
8072
|
+
});
|
|
8073
|
+
|
|
7828
8074
|
// src/diagnostics/leakingRequirements.ts
|
|
7829
8075
|
var leakingRequirements = createDiagnostic({
|
|
7830
8076
|
name: "leakingRequirements",
|
|
@@ -7949,7 +8195,7 @@ var leakingRequirements = createDiagnostic({
|
|
|
7949
8195
|
location: node,
|
|
7950
8196
|
messageText: `Methods of this Service require \`${requirementsStr}\` from every caller.
|
|
7951
8197
|
|
|
7952
|
-
|
|
8198
|
+
The requirement becomes part of the public service surface instead of remaining internal to Layer implementation.
|
|
7953
8199
|
|
|
7954
8200
|
Resolve these dependencies at Layer creation and provide them to each method, so the service's type reflects its purpose, not its implementation.
|
|
7955
8201
|
|
|
@@ -8146,7 +8392,7 @@ var missedPipeableOpportunity = createDiagnostic({
|
|
|
8146
8392
|
).trim() : "";
|
|
8147
8393
|
report({
|
|
8148
8394
|
location: flow2.node,
|
|
8149
|
-
messageText: `
|
|
8395
|
+
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.`,
|
|
8150
8396
|
fixes: [{
|
|
8151
8397
|
fixName: "missedPipeableOpportunity_fix",
|
|
8152
8398
|
description: "Convert to pipe style",
|
|
@@ -8227,7 +8473,7 @@ var missingEffectContext = createDiagnostic({
|
|
|
8227
8473
|
(missingTypes) => missingTypes.length > 0 ? report(
|
|
8228
8474
|
{
|
|
8229
8475
|
location: node,
|
|
8230
|
-
messageText: `
|
|
8476
|
+
messageText: `This Effect requires a service that is missing from the expected Effect context: \`${sortTypes(missingTypes).map((_) => typeChecker.typeToString(_)).join(" | ")}\`.`,
|
|
8231
8477
|
fixes: []
|
|
8232
8478
|
}
|
|
8233
8479
|
) : void 0
|
|
@@ -8578,7 +8824,7 @@ var missingReturnYieldStar = createDiagnostic({
|
|
|
8578
8824
|
}];
|
|
8579
8825
|
report({
|
|
8580
8826
|
location: unwrapped,
|
|
8581
|
-
messageText:
|
|
8827
|
+
messageText: "This Effect never succeeds; using `return yield*` preserves a definitive generator exit point for type narrowing and tooling support.",
|
|
8582
8828
|
fixes: fix
|
|
8583
8829
|
});
|
|
8584
8830
|
}
|
|
@@ -8634,7 +8880,7 @@ var missingStarInYieldEffectGen = createDiagnostic({
|
|
|
8634
8880
|
brokenGenerators.forEach(
|
|
8635
8881
|
(pos) => report({
|
|
8636
8882
|
location: { pos, end: pos + "function".length },
|
|
8637
|
-
messageText: `
|
|
8883
|
+
messageText: "This uses `yield` for an `Effect` value. `yield*` is the Effect-aware form in this context.",
|
|
8638
8884
|
fixes: []
|
|
8639
8885
|
})
|
|
8640
8886
|
);
|
|
@@ -8656,7 +8902,7 @@ var missingStarInYieldEffectGen = createDiagnostic({
|
|
|
8656
8902
|
}] : [];
|
|
8657
8903
|
report({
|
|
8658
8904
|
location: node,
|
|
8659
|
-
messageText: `
|
|
8905
|
+
messageText: "This uses `yield` for an `Effect` value. `yield*` is the Effect-aware form in this context.",
|
|
8660
8906
|
fixes: fix
|
|
8661
8907
|
});
|
|
8662
8908
|
});
|
|
@@ -8724,7 +8970,7 @@ var multipleEffectProvide = createDiagnostic({
|
|
|
8724
8970
|
if (chunk.length < 2) continue;
|
|
8725
8971
|
report({
|
|
8726
8972
|
location: chunk[0].node,
|
|
8727
|
-
messageText: "
|
|
8973
|
+
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.",
|
|
8728
8974
|
fixes: [{
|
|
8729
8975
|
fixName: "multipleEffectProvide_fix",
|
|
8730
8976
|
description: "Combine into a single provide",
|
|
@@ -8758,6 +9004,74 @@ var multipleEffectProvide = createDiagnostic({
|
|
|
8758
9004
|
})
|
|
8759
9005
|
});
|
|
8760
9006
|
|
|
9007
|
+
// src/diagnostics/nestedEffectGenYield.ts
|
|
9008
|
+
var nestedEffectGenYield = createDiagnostic({
|
|
9009
|
+
name: "nestedEffectGenYield",
|
|
9010
|
+
code: 71,
|
|
9011
|
+
description: "Warns when yielding a nested bare Effect.gen inside an existing Effect generator context",
|
|
9012
|
+
group: "style",
|
|
9013
|
+
severity: "off",
|
|
9014
|
+
fixable: false,
|
|
9015
|
+
supportedEffect: ["v3", "v4"],
|
|
9016
|
+
apply: fn("nestedEffectGenYield.apply")(function* (sourceFile, report) {
|
|
9017
|
+
const ts = yield* service(TypeScriptApi);
|
|
9018
|
+
const typeParser = yield* service(TypeParser);
|
|
9019
|
+
const nodeToVisit = [];
|
|
9020
|
+
const appendNodeToVisit = (node) => {
|
|
9021
|
+
nodeToVisit.push(node);
|
|
9022
|
+
return void 0;
|
|
9023
|
+
};
|
|
9024
|
+
ts.forEachChild(sourceFile, appendNodeToVisit);
|
|
9025
|
+
while (nodeToVisit.length > 0) {
|
|
9026
|
+
const node = nodeToVisit.shift();
|
|
9027
|
+
ts.forEachChild(node, appendNodeToVisit);
|
|
9028
|
+
if (!ts.isYieldExpression(node) || !node.asteriskToken || !node.expression) continue;
|
|
9029
|
+
const { inEffect } = yield* typeParser.findEnclosingScopes(node);
|
|
9030
|
+
if (!inEffect) continue;
|
|
9031
|
+
const bareNestedEffectGen = yield* orUndefined(typeParser.effectGen(node.expression));
|
|
9032
|
+
if (!bareNestedEffectGen) continue;
|
|
9033
|
+
report({
|
|
9034
|
+
location: node.expression,
|
|
9035
|
+
messageText: "This `yield*` is applied to a nested `Effect.gen(...)` that can be inlined in the parent Effect generator context.",
|
|
9036
|
+
fixes: []
|
|
9037
|
+
});
|
|
9038
|
+
}
|
|
9039
|
+
})
|
|
9040
|
+
});
|
|
9041
|
+
|
|
9042
|
+
// src/diagnostics/newPromise.ts
|
|
9043
|
+
var newPromise = createDiagnostic({
|
|
9044
|
+
name: "newPromise",
|
|
9045
|
+
code: 68,
|
|
9046
|
+
description: "Warns when constructing promises with new Promise instead of using Effect APIs",
|
|
9047
|
+
group: "effectNative",
|
|
9048
|
+
severity: "off",
|
|
9049
|
+
fixable: false,
|
|
9050
|
+
supportedEffect: ["v3", "v4"],
|
|
9051
|
+
apply: fn("newPromise.apply")(function* (sourceFile, report) {
|
|
9052
|
+
const ts = yield* service(TypeScriptApi);
|
|
9053
|
+
const typeChecker = yield* service(TypeCheckerApi);
|
|
9054
|
+
const typeCheckerUtils = yield* service(TypeCheckerUtils);
|
|
9055
|
+
const promiseSymbol = typeChecker.resolveName("Promise", void 0, ts.SymbolFlags.Value, false);
|
|
9056
|
+
if (!promiseSymbol) return;
|
|
9057
|
+
const visit = (node) => {
|
|
9058
|
+
if (ts.isNewExpression(node)) {
|
|
9059
|
+
const symbol3 = typeChecker.getSymbolAtLocation(node.expression);
|
|
9060
|
+
if (symbol3 && typeCheckerUtils.resolveToGlobalSymbol(symbol3) === promiseSymbol) {
|
|
9061
|
+
report({
|
|
9062
|
+
location: node,
|
|
9063
|
+
messageText: "This code constructs `new Promise(...)`, prefer Effect APIs such as `Effect.async`, `Effect.promise`, or `Effect.tryPromise` instead of manual Promise construction.",
|
|
9064
|
+
fixes: []
|
|
9065
|
+
});
|
|
9066
|
+
}
|
|
9067
|
+
}
|
|
9068
|
+
ts.forEachChild(node, visit);
|
|
9069
|
+
return void 0;
|
|
9070
|
+
};
|
|
9071
|
+
ts.forEachChild(sourceFile, visit);
|
|
9072
|
+
})
|
|
9073
|
+
});
|
|
9074
|
+
|
|
8761
9075
|
// src/diagnostics/nodeBuiltinImport.ts
|
|
8762
9076
|
var moduleAlternativesV3 = /* @__PURE__ */ new Map([
|
|
8763
9077
|
["fs", { alternative: "FileSystem", module: "fs", package: "@effect/platform" }],
|
|
@@ -8814,7 +9128,7 @@ var nodeBuiltinImport = createDiagnostic({
|
|
|
8814
9128
|
if (match2) {
|
|
8815
9129
|
report({
|
|
8816
9130
|
location: statement.moduleSpecifier,
|
|
8817
|
-
messageText: `
|
|
9131
|
+
messageText: `This module reference uses the \`${match2.module}\` module, the corresponding Effect API is \`${match2.alternative}\` from \`${match2.package}\`.`,
|
|
8818
9132
|
fixes: []
|
|
8819
9133
|
});
|
|
8820
9134
|
}
|
|
@@ -8827,7 +9141,7 @@ var nodeBuiltinImport = createDiagnostic({
|
|
|
8827
9141
|
if (match2) {
|
|
8828
9142
|
report({
|
|
8829
9143
|
location: arg,
|
|
8830
|
-
messageText: `
|
|
9144
|
+
messageText: `This module reference uses the \`${match2.module}\` module, the corresponding Effect API is \`${match2.alternative}\` from \`${match2.package}\`.`,
|
|
8831
9145
|
fixes: []
|
|
8832
9146
|
});
|
|
8833
9147
|
}
|
|
@@ -8881,7 +9195,7 @@ var nonObjectEffectServiceType = createDiagnostic({
|
|
|
8881
9195
|
const propertyValue = property.initializer;
|
|
8882
9196
|
const errorToReport = {
|
|
8883
9197
|
location: property.name,
|
|
8884
|
-
messageText: "Effect.Service
|
|
9198
|
+
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.",
|
|
8885
9199
|
fixes: []
|
|
8886
9200
|
};
|
|
8887
9201
|
if (propertyName === "succeed") {
|
|
@@ -9101,6 +9415,14 @@ var effectModuleMigrationDb = {
|
|
|
9101
9415
|
"yieldNow": asUnchanged,
|
|
9102
9416
|
"zip": asUnchanged,
|
|
9103
9417
|
"zipWith": asUnchanged,
|
|
9418
|
+
"annotateLogsScoped": asUnchanged,
|
|
9419
|
+
"awaitAllChildren": asUnchanged,
|
|
9420
|
+
"bind": asUnchanged,
|
|
9421
|
+
"bindTo": asUnchanged,
|
|
9422
|
+
"Do": asUnchanged,
|
|
9423
|
+
"let": asUnchanged,
|
|
9424
|
+
"partition": asUnchanged,
|
|
9425
|
+
"validate": asUnchanged,
|
|
9104
9426
|
// Renamed APIs (v3 name → v4 name)
|
|
9105
9427
|
"catchAll": asRenamedSameBehaviour("catch"),
|
|
9106
9428
|
"catchAllCause": asRenamedSameBehaviour("catchCause"),
|
|
@@ -9120,14 +9442,6 @@ var effectModuleMigrationDb = {
|
|
|
9120
9442
|
"serviceOptional": asRenamedSameBehaviour("serviceOption"),
|
|
9121
9443
|
"tapErrorCause": asRenamedSameBehaviour("tapCause"),
|
|
9122
9444
|
// Removed APIs
|
|
9123
|
-
"annotateLogsScoped": asUnchanged,
|
|
9124
|
-
"awaitAllChildren": asUnchanged,
|
|
9125
|
-
"bind": asUnchanged,
|
|
9126
|
-
"bindTo": asUnchanged,
|
|
9127
|
-
"Do": asUnchanged,
|
|
9128
|
-
"let": asUnchanged,
|
|
9129
|
-
"partition": asUnchanged,
|
|
9130
|
-
"validate": asUnchanged,
|
|
9131
9445
|
"catchSomeDefect": asRemoved(
|
|
9132
9446
|
"Use Effect.catchDefect or Effect.matchCause to handle specific defects."
|
|
9133
9447
|
),
|
|
@@ -9643,7 +9957,7 @@ var outdatedApi = createDiagnostic({
|
|
|
9643
9957
|
hasReported = true;
|
|
9644
9958
|
report({
|
|
9645
9959
|
location: propertyAccess.name,
|
|
9646
|
-
messageText:
|
|
9960
|
+
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.`,
|
|
9647
9961
|
fixes: []
|
|
9648
9962
|
});
|
|
9649
9963
|
}
|
|
@@ -9686,7 +10000,7 @@ var outdatedApi = createDiagnostic({
|
|
|
9686
10000
|
if (hasReported) {
|
|
9687
10001
|
report({
|
|
9688
10002
|
location: { pos: 0, end: 0 },
|
|
9689
|
-
messageText: "This project targets Effect v4, but
|
|
10003
|
+
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.",
|
|
9690
10004
|
fixes: []
|
|
9691
10005
|
});
|
|
9692
10006
|
}
|
|
@@ -10931,7 +11245,7 @@ var overriddenSchemaConstructor = createDiagnostic({
|
|
|
10931
11245
|
};
|
|
10932
11246
|
report({
|
|
10933
11247
|
location: member,
|
|
10934
|
-
messageText: "
|
|
11248
|
+
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.",
|
|
10935
11249
|
fixes: (member.body ? [fixAsStaticNew] : []).concat([{
|
|
10936
11250
|
fixName: "overriddenSchemaConstructor_fix",
|
|
10937
11251
|
description: "Remove the constructor override",
|
|
@@ -11053,7 +11367,7 @@ var preferSchemaOverJson = createDiagnostic({
|
|
|
11053
11367
|
if (isSome2(match2)) {
|
|
11054
11368
|
report({
|
|
11055
11369
|
location: match2.value,
|
|
11056
|
-
messageText: "
|
|
11370
|
+
messageText: "This code uses `JSON.parse` or `JSON.stringify`. Effect Schema provides Effect-aware APIs for JSON parsing and stringifying.",
|
|
11057
11371
|
fixes: []
|
|
11058
11372
|
});
|
|
11059
11373
|
}
|
|
@@ -11061,6 +11375,63 @@ var preferSchemaOverJson = createDiagnostic({
|
|
|
11061
11375
|
})
|
|
11062
11376
|
});
|
|
11063
11377
|
|
|
11378
|
+
// src/diagnostics/processEnvInEffect.ts
|
|
11379
|
+
var isEnvPropertyAccess = (tsApi, node) => tsApi.isPropertyAccessExpression(node) && tsApi.idText(node.name) === "env";
|
|
11380
|
+
var isProcessEnvMemberAccess = (tsApi, node) => (tsApi.isPropertyAccessExpression(node) || tsApi.isElementAccessExpression(node)) && isEnvPropertyAccess(tsApi, node.expression);
|
|
11381
|
+
var makeProcessEnvApply = (checkInEffect) => fn(`processEnv${checkInEffect ? "InEffect" : ""}.apply`)(function* (sourceFile, report) {
|
|
11382
|
+
const ts = yield* service(TypeScriptApi);
|
|
11383
|
+
const typeChecker = yield* service(TypeCheckerApi);
|
|
11384
|
+
const typeCheckerUtils = yield* service(TypeCheckerUtils);
|
|
11385
|
+
const typeParser = yield* service(TypeParser);
|
|
11386
|
+
const processSymbol = typeChecker.resolveName("process", void 0, ts.SymbolFlags.Value, false);
|
|
11387
|
+
if (!processSymbol) return;
|
|
11388
|
+
const nodeToVisit = [];
|
|
11389
|
+
const appendNodeToVisit = (node) => {
|
|
11390
|
+
nodeToVisit.push(node);
|
|
11391
|
+
return void 0;
|
|
11392
|
+
};
|
|
11393
|
+
ts.forEachChild(sourceFile, appendNodeToVisit);
|
|
11394
|
+
while (nodeToVisit.length > 0) {
|
|
11395
|
+
const node = nodeToVisit.shift();
|
|
11396
|
+
ts.forEachChild(node, appendNodeToVisit);
|
|
11397
|
+
if (!isProcessEnvMemberAccess(ts, node)) continue;
|
|
11398
|
+
const processNode = node.expression.expression;
|
|
11399
|
+
if (!ts.isIdentifier(processNode) || ts.idText(processNode) !== "process") continue;
|
|
11400
|
+
const symbol3 = typeChecker.getSymbolAtLocation(processNode);
|
|
11401
|
+
if (!symbol3) continue;
|
|
11402
|
+
if (typeCheckerUtils.resolveToGlobalSymbol(symbol3) !== processSymbol) continue;
|
|
11403
|
+
const { inEffect } = yield* typeParser.findEnclosingScopes(node);
|
|
11404
|
+
if (inEffect !== checkInEffect) continue;
|
|
11405
|
+
report({
|
|
11406
|
+
location: node,
|
|
11407
|
+
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.",
|
|
11408
|
+
fixes: []
|
|
11409
|
+
});
|
|
11410
|
+
}
|
|
11411
|
+
});
|
|
11412
|
+
var processEnvInEffect = createDiagnostic({
|
|
11413
|
+
name: "processEnvInEffect",
|
|
11414
|
+
code: 65,
|
|
11415
|
+
description: "Warns when reading process.env inside Effect generators instead of using Effect Config",
|
|
11416
|
+
group: "effectNative",
|
|
11417
|
+
severity: "off",
|
|
11418
|
+
fixable: false,
|
|
11419
|
+
supportedEffect: ["v3", "v4"],
|
|
11420
|
+
apply: makeProcessEnvApply(true)
|
|
11421
|
+
});
|
|
11422
|
+
|
|
11423
|
+
// src/diagnostics/processEnv.ts
|
|
11424
|
+
var processEnv = createDiagnostic({
|
|
11425
|
+
name: "processEnv",
|
|
11426
|
+
code: 64,
|
|
11427
|
+
description: "Warns when reading process.env outside Effect generators instead of using Effect Config",
|
|
11428
|
+
group: "effectNative",
|
|
11429
|
+
severity: "off",
|
|
11430
|
+
fixable: false,
|
|
11431
|
+
supportedEffect: ["v3", "v4"],
|
|
11432
|
+
apply: makeProcessEnvApply(false)
|
|
11433
|
+
});
|
|
11434
|
+
|
|
11064
11435
|
// src/diagnostics/redundantSchemaTagIdentifier.ts
|
|
11065
11436
|
var redundantSchemaTagIdentifier = createDiagnostic({
|
|
11066
11437
|
name: "redundantSchemaTagIdentifier",
|
|
@@ -11171,9 +11542,7 @@ var returnEffectInGen = createDiagnostic({
|
|
|
11171
11542
|
}] : [];
|
|
11172
11543
|
report({
|
|
11173
11544
|
location: node,
|
|
11174
|
-
messageText:
|
|
11175
|
-
Maybe you wanted to return yield* instead?
|
|
11176
|
-
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.`,
|
|
11545
|
+
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.",
|
|
11177
11546
|
fixes: fix
|
|
11178
11547
|
});
|
|
11179
11548
|
}),
|
|
@@ -11328,9 +11697,7 @@ var runEffectInsideEffect = createDiagnostic({
|
|
|
11328
11697
|
);
|
|
11329
11698
|
});
|
|
11330
11699
|
const v4MethodName = `${isEffectRunCall.value.methodName}With`;
|
|
11331
|
-
const messageText = supportedEffect === "v4" ?
|
|
11332
|
-
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.
|
|
11333
|
-
Consider extracting the Runtime by using for example Effect.runtime and then use Runtime.${isEffectRunCall.value.methodName} with the extracted runtime instead.`;
|
|
11700
|
+
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}\`.`;
|
|
11334
11701
|
report({
|
|
11335
11702
|
location: node.expression,
|
|
11336
11703
|
messageText,
|
|
@@ -11343,7 +11710,7 @@ Consider extracting the Runtime by using for example Effect.runtime and then use
|
|
|
11343
11710
|
} else {
|
|
11344
11711
|
report({
|
|
11345
11712
|
location: node.expression,
|
|
11346
|
-
messageText:
|
|
11713
|
+
messageText: `\`${nodeText}\` is called inside an existing Effect context. Here, the inner Effect can be used directly.`,
|
|
11347
11714
|
fixes: []
|
|
11348
11715
|
});
|
|
11349
11716
|
}
|
|
@@ -11398,7 +11765,7 @@ var schemaStructWithTag = createDiagnostic({
|
|
|
11398
11765
|
const otherProperties = arg.properties.filter((prop) => prop !== tagProperty);
|
|
11399
11766
|
report({
|
|
11400
11767
|
location: node,
|
|
11401
|
-
messageText: "Schema.Struct
|
|
11768
|
+
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.",
|
|
11402
11769
|
fixes: [{
|
|
11403
11770
|
fixName: "schemaStructWithTag_fix",
|
|
11404
11771
|
description: "Replace with Schema.TaggedStruct",
|
|
@@ -11491,7 +11858,7 @@ var schemaSyncInEffect = createDiagnostic({
|
|
|
11491
11858
|
const effectMethodName = syncToEffectMethod[isSchemaSyncCall.value.methodName];
|
|
11492
11859
|
report({
|
|
11493
11860
|
location: node.expression,
|
|
11494
|
-
messageText:
|
|
11861
|
+
messageText: `\`${nodeText}\` is used inside an Effect generator. \`Schema.${effectMethodName}\` preserves the typed Effect error channel for this operation without throwing.`,
|
|
11495
11862
|
fixes: []
|
|
11496
11863
|
});
|
|
11497
11864
|
}
|
|
@@ -11551,7 +11918,7 @@ var schemaUnionOfLiterals = createDiagnostic({
|
|
|
11551
11918
|
const schemaLiteralExpression = firstLiteralCall.expression;
|
|
11552
11919
|
report({
|
|
11553
11920
|
location: node,
|
|
11554
|
-
messageText: "
|
|
11921
|
+
messageText: "This `Schema.Union` contains multiple `Schema.Literal` members and can be simplified to a single `Schema.Literal` call.",
|
|
11555
11922
|
fixes: [{
|
|
11556
11923
|
fixName: "schemaUnionOfLiterals_fix",
|
|
11557
11924
|
description: "Replace with a single Schema.Literal call",
|
|
@@ -11614,8 +11981,7 @@ var scopeInLayerEffect = createDiagnostic({
|
|
|
11614
11981
|
map4(
|
|
11615
11982
|
() => report({
|
|
11616
11983
|
location: node,
|
|
11617
|
-
messageText: `
|
|
11618
|
-
Consider using "scoped" instead to get rid of the scope in the requirements.`,
|
|
11984
|
+
messageText: "This layer construction leaves `Scope` in the requirement set. The scoped API removes `Scope` from the resulting requirements.",
|
|
11619
11985
|
fixes: methodIdentifier ? [{
|
|
11620
11986
|
fixName: "scopeInLayerEffect_scoped",
|
|
11621
11987
|
description: "Use scoped for Layer creation",
|
|
@@ -11715,7 +12081,7 @@ var serviceNotAsClass = createDiagnostic({
|
|
|
11715
12081
|
const shapeText = typeArgs.length > 0 ? typeArgs.map((t) => sourceFile.text.substring(ts.getTokenPosOfNode(t, sourceFile), t.end)).join(", ") : "Shape";
|
|
11716
12082
|
report({
|
|
11717
12083
|
location: callExpr,
|
|
11718
|
-
messageText:
|
|
12084
|
+
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, "")}") {}\`.`,
|
|
11719
12085
|
fixes: [{
|
|
11720
12086
|
fixName: "serviceNotAsClass",
|
|
11721
12087
|
description: `Convert to class declaration`,
|
|
@@ -11928,7 +12294,7 @@ var tryCatchInEffectGen = createDiagnostic({
|
|
|
11928
12294
|
map4(() => {
|
|
11929
12295
|
report({
|
|
11930
12296
|
location: node,
|
|
11931
|
-
messageText: `
|
|
12297
|
+
messageText: `This Effect generator contains \`try/catch\`; in this context, error handling is expressed with Effect APIs such as ${alternatives.join(", ")}).`,
|
|
11932
12298
|
fixes: []
|
|
11933
12299
|
});
|
|
11934
12300
|
}),
|
|
@@ -11989,8 +12355,7 @@ var unknownInEffectCatch = createDiagnostic({
|
|
|
11989
12355
|
);
|
|
11990
12356
|
report({
|
|
11991
12357
|
location: node.expression,
|
|
11992
|
-
messageText: `The
|
|
11993
|
-
Consider wrapping unknown errors into Effect's Data.TaggedError for example, or narrow down the type to the specific error raised.`,
|
|
12358
|
+
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\`.`,
|
|
11994
12359
|
fixes: []
|
|
11995
12360
|
});
|
|
11996
12361
|
}
|
|
@@ -12004,6 +12369,56 @@ Consider wrapping unknown errors into Effect's Data.TaggedError for example, or
|
|
|
12004
12369
|
})
|
|
12005
12370
|
});
|
|
12006
12371
|
|
|
12372
|
+
// src/diagnostics/unnecessaryArrowBlock.ts
|
|
12373
|
+
var unnecessaryArrowBlock = createDiagnostic({
|
|
12374
|
+
name: "unnecessaryArrowBlock",
|
|
12375
|
+
code: 72,
|
|
12376
|
+
description: "Suggests using a concise arrow body when the block only returns an expression",
|
|
12377
|
+
group: "style",
|
|
12378
|
+
severity: "off",
|
|
12379
|
+
fixable: true,
|
|
12380
|
+
supportedEffect: ["v3", "v4"],
|
|
12381
|
+
apply: fn("unnecessaryArrowBlock.apply")(function* (sourceFile, report) {
|
|
12382
|
+
const ts = yield* service(TypeScriptApi);
|
|
12383
|
+
const nodeToVisit = [];
|
|
12384
|
+
const appendNodeToVisit = (node) => {
|
|
12385
|
+
nodeToVisit.push(node);
|
|
12386
|
+
return void 0;
|
|
12387
|
+
};
|
|
12388
|
+
ts.forEachChild(sourceFile, appendNodeToVisit);
|
|
12389
|
+
while (nodeToVisit.length > 0) {
|
|
12390
|
+
const node = nodeToVisit.shift();
|
|
12391
|
+
ts.forEachChild(node, appendNodeToVisit);
|
|
12392
|
+
if (!ts.isArrowFunction(node) || !ts.isBlock(node.body)) continue;
|
|
12393
|
+
if (node.body.statements.length !== 1) continue;
|
|
12394
|
+
const [statement] = node.body.statements;
|
|
12395
|
+
if (!ts.isReturnStatement(statement) || !statement.expression) continue;
|
|
12396
|
+
const returnedExpression = statement.expression;
|
|
12397
|
+
report({
|
|
12398
|
+
location: node.body,
|
|
12399
|
+
messageText: "This arrow function block only returns an expression and can use a concise body.",
|
|
12400
|
+
fixes: [{
|
|
12401
|
+
fixName: "unnecessaryArrowBlock_fix",
|
|
12402
|
+
description: "Use a concise arrow body",
|
|
12403
|
+
apply: gen(function* () {
|
|
12404
|
+
const changeTracker = yield* service(ChangeTracker);
|
|
12405
|
+
const replacementNode = ts.factory.updateArrowFunction(
|
|
12406
|
+
node,
|
|
12407
|
+
node.modifiers,
|
|
12408
|
+
node.typeParameters,
|
|
12409
|
+
node.parameters,
|
|
12410
|
+
node.type,
|
|
12411
|
+
node.equalsGreaterThanToken,
|
|
12412
|
+
ts.factory.createParenthesizedExpression(returnedExpression)
|
|
12413
|
+
);
|
|
12414
|
+
changeTracker.replaceNode(sourceFile, node, replacementNode);
|
|
12415
|
+
})
|
|
12416
|
+
}]
|
|
12417
|
+
});
|
|
12418
|
+
}
|
|
12419
|
+
})
|
|
12420
|
+
});
|
|
12421
|
+
|
|
12007
12422
|
// src/diagnostics/unnecessaryEffectGen.ts
|
|
12008
12423
|
var unnecessaryEffectGen = createDiagnostic({
|
|
12009
12424
|
name: "unnecessaryEffectGen",
|
|
@@ -12087,7 +12502,7 @@ var unnecessaryFailYieldableError = createDiagnostic({
|
|
|
12087
12502
|
map4(
|
|
12088
12503
|
() => report({
|
|
12089
12504
|
location: node,
|
|
12090
|
-
messageText:
|
|
12505
|
+
messageText: "This `yield* Effect.fail(...)` passes a yieldable error value. `yield*` represents that value directly without wrapping it in `Effect.fail`.",
|
|
12091
12506
|
fixes: [{
|
|
12092
12507
|
fixName: "unnecessaryFailYieldableError_fix",
|
|
12093
12508
|
description: "Replace yield* Effect.fail with yield*",
|
|
@@ -12192,7 +12607,7 @@ var unnecessaryPipeChain = createDiagnostic({
|
|
|
12192
12607
|
map4(({ innerCall, pipeCall }) => {
|
|
12193
12608
|
report({
|
|
12194
12609
|
location: node,
|
|
12195
|
-
messageText: `
|
|
12610
|
+
messageText: "This expression contains chained `pipe` calls that can be simplified to a single `pipe` call.",
|
|
12196
12611
|
fixes: [{
|
|
12197
12612
|
fixName: "unnecessaryPipeChain_fix",
|
|
12198
12613
|
description: "Rewrite as single pipe call",
|
|
@@ -12304,12 +12719,17 @@ var unsupportedServiceAccessors = createDiagnostic({
|
|
|
12304
12719
|
var diagnostics = [
|
|
12305
12720
|
outdatedApi,
|
|
12306
12721
|
anyUnknownInErrorContext,
|
|
12722
|
+
asyncFunction,
|
|
12307
12723
|
instanceOfSchema,
|
|
12308
12724
|
catchAllToMapError,
|
|
12309
12725
|
catchUnfailableEffect,
|
|
12310
12726
|
classSelfMismatch,
|
|
12727
|
+
cryptoRandomUUID,
|
|
12728
|
+
cryptoRandomUUIDInEffect,
|
|
12311
12729
|
duplicatePackage,
|
|
12730
|
+
effectDoNotation,
|
|
12312
12731
|
effectFnImplicitAny,
|
|
12732
|
+
effectMapFlatten,
|
|
12313
12733
|
effectGenUsesAdapter,
|
|
12314
12734
|
missingEffectContext,
|
|
12315
12735
|
missingEffectError,
|
|
@@ -12318,6 +12738,8 @@ var diagnostics = [
|
|
|
12318
12738
|
floatingEffect,
|
|
12319
12739
|
effectInFailure,
|
|
12320
12740
|
missingStarInYieldEffectGen,
|
|
12741
|
+
newPromise,
|
|
12742
|
+
lazyPromiseInEffectSync,
|
|
12321
12743
|
unnecessaryEffectGen,
|
|
12322
12744
|
unnecessaryFailYieldableError,
|
|
12323
12745
|
missingReturnYieldStar,
|
|
@@ -12326,6 +12748,8 @@ var diagnostics = [
|
|
|
12326
12748
|
genericEffectServices,
|
|
12327
12749
|
globalFetch,
|
|
12328
12750
|
globalFetchInEffect,
|
|
12751
|
+
processEnv,
|
|
12752
|
+
processEnvInEffect,
|
|
12329
12753
|
returnEffectInGen,
|
|
12330
12754
|
tryCatchInEffectGen,
|
|
12331
12755
|
importFromBarrel,
|
|
@@ -12343,6 +12767,8 @@ var diagnostics = [
|
|
|
12343
12767
|
strictEffectProvide,
|
|
12344
12768
|
unknownInEffectCatch,
|
|
12345
12769
|
runEffectInsideEffect,
|
|
12770
|
+
nestedEffectGenYield,
|
|
12771
|
+
unnecessaryArrowBlock,
|
|
12346
12772
|
schemaUnionOfLiterals,
|
|
12347
12773
|
schemaStructWithTag,
|
|
12348
12774
|
globalErrorInEffectCatch,
|