@effect/language-service 0.64.0 → 0.65.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 +28 -39
- package/cli.js +3438 -1098
- package/cli.js.map +1 -1
- package/effect-lsp-patch-utils.js +851 -220
- package/effect-lsp-patch-utils.js.map +1 -1
- package/index.js +902 -252
- package/index.js.map +1 -1
- package/package.json +1 -1
- package/transform.js +849 -218
- package/transform.js.map +1 -1
|
@@ -26,7 +26,7 @@ __export(effect_lsp_patch_utils_exports, {
|
|
|
26
26
|
});
|
|
27
27
|
module.exports = __toCommonJS(effect_lsp_patch_utils_exports);
|
|
28
28
|
|
|
29
|
-
// node_modules/.pnpm/effect@3.19.
|
|
29
|
+
// node_modules/.pnpm/effect@3.19.14/node_modules/effect/dist/esm/Function.js
|
|
30
30
|
var isFunction = (input) => typeof input === "function";
|
|
31
31
|
var dual = function(arity, body) {
|
|
32
32
|
if (typeof arity === "function") {
|
|
@@ -122,7 +122,7 @@ function pipe(a, ab, bc, cd, de, ef, fg, gh, hi) {
|
|
|
122
122
|
}
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
-
// node_modules/.pnpm/effect@3.19.
|
|
125
|
+
// node_modules/.pnpm/effect@3.19.14/node_modules/effect/dist/esm/GlobalValue.js
|
|
126
126
|
var globalStoreId = `effect/GlobalValue`;
|
|
127
127
|
var globalStore;
|
|
128
128
|
var globalValue = (id, compute) => {
|
|
@@ -136,7 +136,7 @@ var globalValue = (id, compute) => {
|
|
|
136
136
|
return globalStore.get(id);
|
|
137
137
|
};
|
|
138
138
|
|
|
139
|
-
// node_modules/.pnpm/effect@3.19.
|
|
139
|
+
// node_modules/.pnpm/effect@3.19.14/node_modules/effect/dist/esm/Predicate.js
|
|
140
140
|
var isString = (input) => typeof input === "string";
|
|
141
141
|
var isNumber = (input) => typeof input === "number";
|
|
142
142
|
var isBoolean = (input) => typeof input === "boolean";
|
|
@@ -146,7 +146,7 @@ var isObject = (input) => isRecordOrArray(input) || isFunction2(input);
|
|
|
146
146
|
var hasProperty = /* @__PURE__ */ dual(2, (self, property) => isObject(self) && property in self);
|
|
147
147
|
var isRecord = (input) => isRecordOrArray(input) && !Array.isArray(input);
|
|
148
148
|
|
|
149
|
-
// node_modules/.pnpm/effect@3.19.
|
|
149
|
+
// node_modules/.pnpm/effect@3.19.14/node_modules/effect/dist/esm/Utils.js
|
|
150
150
|
var GenKindTypeId = /* @__PURE__ */ Symbol.for("effect/Gen/GenKind");
|
|
151
151
|
var GenKindImpl = class {
|
|
152
152
|
value;
|
|
@@ -268,7 +268,7 @@ var internalCall = isNotOptimizedAway ? standard.effect_internal_function : forc
|
|
|
268
268
|
var genConstructor = function* () {
|
|
269
269
|
}.constructor;
|
|
270
270
|
|
|
271
|
-
// node_modules/.pnpm/effect@3.19.
|
|
271
|
+
// node_modules/.pnpm/effect@3.19.14/node_modules/effect/dist/esm/Hash.js
|
|
272
272
|
var randomHashCache = /* @__PURE__ */ globalValue(/* @__PURE__ */ Symbol.for("effect/Hash/randomHashCache"), () => /* @__PURE__ */ new WeakMap());
|
|
273
273
|
var symbol = /* @__PURE__ */ Symbol.for("effect/Hash");
|
|
274
274
|
var hash = (self) => {
|
|
@@ -370,7 +370,7 @@ var cached = function() {
|
|
|
370
370
|
return hash2;
|
|
371
371
|
};
|
|
372
372
|
|
|
373
|
-
// node_modules/.pnpm/effect@3.19.
|
|
373
|
+
// node_modules/.pnpm/effect@3.19.14/node_modules/effect/dist/esm/Equal.js
|
|
374
374
|
var symbol2 = /* @__PURE__ */ Symbol.for("effect/Equal");
|
|
375
375
|
function equals() {
|
|
376
376
|
if (arguments.length === 1) {
|
|
@@ -426,7 +426,7 @@ function compareBoth(self, that) {
|
|
|
426
426
|
var isEqual = (u) => hasProperty(u, symbol2);
|
|
427
427
|
var equivalence = () => equals;
|
|
428
428
|
|
|
429
|
-
// node_modules/.pnpm/effect@3.19.
|
|
429
|
+
// node_modules/.pnpm/effect@3.19.14/node_modules/effect/dist/esm/Inspectable.js
|
|
430
430
|
var NodeInspectSymbol = /* @__PURE__ */ Symbol.for("nodejs.util.inspect.custom");
|
|
431
431
|
var toJSON = (x) => {
|
|
432
432
|
try {
|
|
@@ -478,7 +478,7 @@ var redact = (u) => {
|
|
|
478
478
|
return u;
|
|
479
479
|
};
|
|
480
480
|
|
|
481
|
-
// node_modules/.pnpm/effect@3.19.
|
|
481
|
+
// node_modules/.pnpm/effect@3.19.14/node_modules/effect/dist/esm/Pipeable.js
|
|
482
482
|
var pipeArguments = (self, args2) => {
|
|
483
483
|
switch (args2.length) {
|
|
484
484
|
case 0:
|
|
@@ -511,14 +511,14 @@ var pipeArguments = (self, args2) => {
|
|
|
511
511
|
}
|
|
512
512
|
};
|
|
513
513
|
|
|
514
|
-
// node_modules/.pnpm/effect@3.19.
|
|
514
|
+
// node_modules/.pnpm/effect@3.19.14/node_modules/effect/dist/esm/internal/opCodes/effect.js
|
|
515
515
|
var OP_COMMIT = "Commit";
|
|
516
516
|
|
|
517
|
-
// node_modules/.pnpm/effect@3.19.
|
|
518
|
-
var moduleVersion = "3.19.
|
|
517
|
+
// node_modules/.pnpm/effect@3.19.14/node_modules/effect/dist/esm/internal/version.js
|
|
518
|
+
var moduleVersion = "3.19.14";
|
|
519
519
|
var getCurrentVersion = () => moduleVersion;
|
|
520
520
|
|
|
521
|
-
// node_modules/.pnpm/effect@3.19.
|
|
521
|
+
// node_modules/.pnpm/effect@3.19.14/node_modules/effect/dist/esm/internal/effectable.js
|
|
522
522
|
var EffectTypeId = /* @__PURE__ */ Symbol.for("effect/Effect");
|
|
523
523
|
var StreamTypeId = /* @__PURE__ */ Symbol.for("effect/Stream");
|
|
524
524
|
var SinkTypeId = /* @__PURE__ */ Symbol.for("effect/Sink");
|
|
@@ -605,7 +605,7 @@ var StructuralCommitPrototype = {
|
|
|
605
605
|
...StructuralPrototype
|
|
606
606
|
};
|
|
607
607
|
|
|
608
|
-
// node_modules/.pnpm/effect@3.19.
|
|
608
|
+
// node_modules/.pnpm/effect@3.19.14/node_modules/effect/dist/esm/internal/option.js
|
|
609
609
|
var TypeId = /* @__PURE__ */ Symbol.for("effect/Option");
|
|
610
610
|
var CommonProto = {
|
|
611
611
|
...EffectPrototype,
|
|
@@ -663,7 +663,7 @@ var some = (value) => {
|
|
|
663
663
|
return a;
|
|
664
664
|
};
|
|
665
665
|
|
|
666
|
-
// node_modules/.pnpm/effect@3.19.
|
|
666
|
+
// node_modules/.pnpm/effect@3.19.14/node_modules/effect/dist/esm/internal/either.js
|
|
667
667
|
var TypeId2 = /* @__PURE__ */ Symbol.for("effect/Either");
|
|
668
668
|
var CommonProto2 = {
|
|
669
669
|
...EffectPrototype,
|
|
@@ -725,7 +725,7 @@ var right = (right3) => {
|
|
|
725
725
|
return a;
|
|
726
726
|
};
|
|
727
727
|
|
|
728
|
-
// node_modules/.pnpm/effect@3.19.
|
|
728
|
+
// node_modules/.pnpm/effect@3.19.14/node_modules/effect/dist/esm/Either.js
|
|
729
729
|
var right2 = right;
|
|
730
730
|
var left2 = left;
|
|
731
731
|
var isLeft2 = isLeft;
|
|
@@ -733,14 +733,14 @@ var isRight2 = isRight;
|
|
|
733
733
|
var map = /* @__PURE__ */ dual(2, (self, f) => isRight2(self) ? right2(f(self.right)) : left2(self.left));
|
|
734
734
|
var getOrElse = /* @__PURE__ */ dual(2, (self, onLeft) => isLeft2(self) ? onLeft(self.left) : self.right);
|
|
735
735
|
|
|
736
|
-
// node_modules/.pnpm/effect@3.19.
|
|
736
|
+
// node_modules/.pnpm/effect@3.19.14/node_modules/effect/dist/esm/internal/array.js
|
|
737
737
|
var isNonEmptyArray = (self) => self.length > 0;
|
|
738
738
|
|
|
739
|
-
// node_modules/.pnpm/effect@3.19.
|
|
739
|
+
// node_modules/.pnpm/effect@3.19.14/node_modules/effect/dist/esm/Order.js
|
|
740
740
|
var make = (compare) => (self, that) => self === that ? 0 : compare(self, that);
|
|
741
741
|
var string2 = /* @__PURE__ */ make((self, that) => self < that ? -1 : 1);
|
|
742
742
|
|
|
743
|
-
// node_modules/.pnpm/effect@3.19.
|
|
743
|
+
// node_modules/.pnpm/effect@3.19.14/node_modules/effect/dist/esm/Option.js
|
|
744
744
|
var none2 = () => none;
|
|
745
745
|
var some2 = some;
|
|
746
746
|
var isNone2 = isNone;
|
|
@@ -750,7 +750,7 @@ var orElse = /* @__PURE__ */ dual(2, (self, that) => isNone2(self) ? that() : se
|
|
|
750
750
|
var fromNullable = (nullableValue) => nullableValue == null ? none2() : some2(nullableValue);
|
|
751
751
|
var getOrUndefined = /* @__PURE__ */ getOrElse2(constUndefined);
|
|
752
752
|
|
|
753
|
-
// node_modules/.pnpm/effect@3.19.
|
|
753
|
+
// node_modules/.pnpm/effect@3.19.14/node_modules/effect/dist/esm/Record.js
|
|
754
754
|
var map2 = /* @__PURE__ */ dual(2, (self, f) => {
|
|
755
755
|
const out = {
|
|
756
756
|
...self
|
|
@@ -762,7 +762,7 @@ var map2 = /* @__PURE__ */ dual(2, (self, f) => {
|
|
|
762
762
|
});
|
|
763
763
|
var keys = (self) => Object.keys(self);
|
|
764
764
|
|
|
765
|
-
// node_modules/.pnpm/effect@3.19.
|
|
765
|
+
// node_modules/.pnpm/effect@3.19.14/node_modules/effect/dist/esm/Array.js
|
|
766
766
|
var fromIterable = (collection) => Array.isArray(collection) ? collection : Array.from(collection);
|
|
767
767
|
var append = /* @__PURE__ */ dual(2, (self, last) => [...self, last]);
|
|
768
768
|
var appendAll = /* @__PURE__ */ dual(2, (self, that) => fromIterable(self).concat(fromIterable(that)));
|
|
@@ -785,7 +785,6 @@ var unsafeGet = /* @__PURE__ */ dual(2, (self, index) => {
|
|
|
785
785
|
var head = /* @__PURE__ */ get(0);
|
|
786
786
|
var headNonEmpty = /* @__PURE__ */ unsafeGet(0);
|
|
787
787
|
var tailNonEmpty = (self) => self.slice(1);
|
|
788
|
-
var reverse = (self) => Array.from(self).reverse();
|
|
789
788
|
var sort = /* @__PURE__ */ dual(2, (self, O) => {
|
|
790
789
|
const out = Array.from(self);
|
|
791
790
|
out.sort(O);
|
|
@@ -1201,7 +1200,7 @@ var defaults = {
|
|
|
1201
1200
|
diagnosticSeverity: {},
|
|
1202
1201
|
diagnosticsName: true,
|
|
1203
1202
|
missingDiagnosticNextLine: "warning",
|
|
1204
|
-
|
|
1203
|
+
includeSuggestionsInTsc: true,
|
|
1205
1204
|
quickinfo: true,
|
|
1206
1205
|
quickinfoEffectParameters: "whentruncated",
|
|
1207
1206
|
quickinfoMaximumLength: -1,
|
|
@@ -1225,7 +1224,7 @@ var defaults = {
|
|
|
1225
1224
|
skipLeadingPath: ["src/"]
|
|
1226
1225
|
}],
|
|
1227
1226
|
extendedKeyDetection: false,
|
|
1228
|
-
pipeableMinArgCount:
|
|
1227
|
+
pipeableMinArgCount: 2,
|
|
1229
1228
|
layerGraphFollowDepth: 0,
|
|
1230
1229
|
mermaidProvider: "mermaid.live"
|
|
1231
1230
|
};
|
|
@@ -1248,7 +1247,7 @@ function parse(config) {
|
|
|
1248
1247
|
diagnosticSeverity: isObject(config) && hasProperty(config, "diagnosticSeverity") && isRecord(config.diagnosticSeverity) ? parseDiagnosticSeverity(config.diagnosticSeverity) : defaults.diagnosticSeverity,
|
|
1249
1248
|
diagnosticsName: isObject(config) && hasProperty(config, "diagnosticsName") && isBoolean(config.diagnosticsName) ? config.diagnosticsName : defaults.diagnosticsName,
|
|
1250
1249
|
missingDiagnosticNextLine: isObject(config) && hasProperty(config, "missingDiagnosticNextLine") && isString(config.missingDiagnosticNextLine) && isValidSeverityLevel(config.missingDiagnosticNextLine) ? config.missingDiagnosticNextLine : defaults.missingDiagnosticNextLine,
|
|
1251
|
-
|
|
1250
|
+
includeSuggestionsInTsc: isObject(config) && hasProperty(config, "includeSuggestionsInTsc") && isBoolean(config.includeSuggestionsInTsc) ? config.includeSuggestionsInTsc : defaults.includeSuggestionsInTsc,
|
|
1252
1251
|
quickinfo: isObject(config) && hasProperty(config, "quickinfo") && isBoolean(config.quickinfo) ? config.quickinfo : defaults.quickinfo,
|
|
1253
1252
|
quickinfoEffectParameters: isObject(config) && hasProperty(config, "quickinfoEffectParameters") && isString(config.quickinfoEffectParameters) && ["always", "never", "whentruncated"].includes(config.quickinfoEffectParameters.toLowerCase()) ? config.quickinfoEffectParameters.toLowerCase() : defaults.quickinfoEffectParameters,
|
|
1254
1253
|
quickinfoMaximumLength: isObject(config) && hasProperty(config, "quickinfoMaximumLength") && isNumber(config.quickinfoMaximumLength) ? config.quickinfoMaximumLength : defaults.quickinfoMaximumLength,
|
|
@@ -1818,6 +1817,16 @@ function makeTypeScriptUtils(ts) {
|
|
|
1818
1817
|
function isDeclarationKind(kind) {
|
|
1819
1818
|
return kind === ts.SyntaxKind.ArrowFunction || kind === ts.SyntaxKind.BindingElement || kind === ts.SyntaxKind.ClassDeclaration || kind === ts.SyntaxKind.ClassExpression || kind === ts.SyntaxKind.ClassStaticBlockDeclaration || kind === ts.SyntaxKind.Constructor || kind === ts.SyntaxKind.EnumDeclaration || kind === ts.SyntaxKind.EnumMember || kind === ts.SyntaxKind.ExportSpecifier || kind === ts.SyntaxKind.FunctionDeclaration || kind === ts.SyntaxKind.FunctionExpression || kind === ts.SyntaxKind.GetAccessor || kind === ts.SyntaxKind.ImportClause || kind === ts.SyntaxKind.ImportEqualsDeclaration || kind === ts.SyntaxKind.ImportSpecifier || kind === ts.SyntaxKind.InterfaceDeclaration || kind === ts.SyntaxKind.JsxAttribute || kind === ts.SyntaxKind.MethodDeclaration || kind === ts.SyntaxKind.MethodSignature || kind === ts.SyntaxKind.ModuleDeclaration || kind === ts.SyntaxKind.NamespaceExportDeclaration || kind === ts.SyntaxKind.NamespaceImport || kind === ts.SyntaxKind.NamespaceExport || kind === ts.SyntaxKind.Parameter || kind === ts.SyntaxKind.PropertyAssignment || kind === ts.SyntaxKind.PropertyDeclaration || kind === ts.SyntaxKind.PropertySignature || kind === ts.SyntaxKind.SetAccessor || kind === ts.SyntaxKind.ShorthandPropertyAssignment || kind === ts.SyntaxKind.TypeAliasDeclaration || kind === ts.SyntaxKind.TypeParameter || kind === ts.SyntaxKind.VariableDeclaration || kind === ts.SyntaxKind.JSDocTypedefTag || kind === ts.SyntaxKind.JSDocCallbackTag || kind === ts.SyntaxKind.JSDocPropertyTag || kind === ts.SyntaxKind.NamedTupleMember;
|
|
1820
1819
|
}
|
|
1820
|
+
function isVoidExpression(node) {
|
|
1821
|
+
const unwrapped = ts.isExpression(node) ? skipOuterExpressions(node) : node;
|
|
1822
|
+
if (ts.isVoidExpression(unwrapped) && ts.isNumericLiteral(unwrapped.expression) && unwrapped.expression.text === "0") {
|
|
1823
|
+
return true;
|
|
1824
|
+
}
|
|
1825
|
+
if (ts.isIdentifier(unwrapped) && ts.idText(unwrapped) === "undefined") {
|
|
1826
|
+
return true;
|
|
1827
|
+
}
|
|
1828
|
+
return false;
|
|
1829
|
+
}
|
|
1821
1830
|
return {
|
|
1822
1831
|
findNodeAtPositionIncludingTrivia,
|
|
1823
1832
|
parsePackageContentNameAndVersionFromScope,
|
|
@@ -1841,7 +1850,8 @@ function makeTypeScriptUtils(ts) {
|
|
|
1841
1850
|
getSourceFileOfNode,
|
|
1842
1851
|
isOuterExpression,
|
|
1843
1852
|
skipOuterExpressions,
|
|
1844
|
-
isDeclarationKind
|
|
1853
|
+
isDeclarationKind,
|
|
1854
|
+
isVoidExpression
|
|
1845
1855
|
};
|
|
1846
1856
|
}
|
|
1847
1857
|
|
|
@@ -3023,13 +3033,15 @@ function make2(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
|
|
|
3023
3033
|
);
|
|
3024
3034
|
}
|
|
3025
3035
|
const propertyAccess = node.expression;
|
|
3036
|
+
const pipeArguments2 = node.arguments.slice(1);
|
|
3026
3037
|
return pipe(
|
|
3027
3038
|
isNodeReferenceToEffectModuleApi("fnUntraced")(propertyAccess),
|
|
3028
3039
|
map4(() => ({
|
|
3029
3040
|
node,
|
|
3030
3041
|
effectModule: propertyAccess.expression,
|
|
3031
3042
|
generatorFunction,
|
|
3032
|
-
body: generatorFunction.body
|
|
3043
|
+
body: generatorFunction.body,
|
|
3044
|
+
pipeArguments: pipeArguments2
|
|
3033
3045
|
}))
|
|
3034
3046
|
);
|
|
3035
3047
|
},
|
|
@@ -3068,13 +3080,15 @@ function make2(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
|
|
|
3068
3080
|
);
|
|
3069
3081
|
}
|
|
3070
3082
|
const propertyAccess = expressionToTest;
|
|
3083
|
+
const pipeArguments2 = node.arguments.slice(1);
|
|
3071
3084
|
return pipe(
|
|
3072
3085
|
isNodeReferenceToEffectModuleApi("fn")(propertyAccess),
|
|
3073
3086
|
map4(() => ({
|
|
3074
3087
|
node,
|
|
3075
3088
|
generatorFunction,
|
|
3076
3089
|
effectModule: propertyAccess.expression,
|
|
3077
|
-
body: generatorFunction.body
|
|
3090
|
+
body: generatorFunction.body,
|
|
3091
|
+
pipeArguments: pipeArguments2
|
|
3078
3092
|
}))
|
|
3079
3093
|
);
|
|
3080
3094
|
},
|
|
@@ -3261,6 +3275,23 @@ function make2(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
|
|
|
3261
3275
|
"TypeParser.pipeCall",
|
|
3262
3276
|
(node) => node
|
|
3263
3277
|
);
|
|
3278
|
+
const singleArgCall = cachedBy(
|
|
3279
|
+
function(node) {
|
|
3280
|
+
if (!ts.isCallExpression(node)) {
|
|
3281
|
+
return typeParserIssue("Node is not a call expression", void 0, node);
|
|
3282
|
+
}
|
|
3283
|
+
if (node.arguments.length !== 1) {
|
|
3284
|
+
return typeParserIssue("Node must have exactly one argument", void 0, node);
|
|
3285
|
+
}
|
|
3286
|
+
return succeed({
|
|
3287
|
+
node,
|
|
3288
|
+
callee: node.expression,
|
|
3289
|
+
subject: node.arguments[0]
|
|
3290
|
+
});
|
|
3291
|
+
},
|
|
3292
|
+
"TypeParser.singleArgCall",
|
|
3293
|
+
(node) => node
|
|
3294
|
+
);
|
|
3264
3295
|
const scopeType = cachedBy(
|
|
3265
3296
|
fn("TypeParser.scopeType")(function* (type, atLocation) {
|
|
3266
3297
|
yield* pipeableType(type, atLocation);
|
|
@@ -3799,6 +3830,266 @@ function make2(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
|
|
|
3799
3830
|
`TypeParser.isNodeReferenceToEffectLayerModuleApi(${memberName})`,
|
|
3800
3831
|
(node) => node
|
|
3801
3832
|
);
|
|
3833
|
+
const lazyExpression = cachedBy(
|
|
3834
|
+
function(node) {
|
|
3835
|
+
if (!ts.isArrowFunction(node) && !ts.isFunctionExpression(node)) {
|
|
3836
|
+
return typeParserIssue("Node is not an arrow function or function expression", void 0, node);
|
|
3837
|
+
}
|
|
3838
|
+
if (node.parameters.length !== 0) {
|
|
3839
|
+
return typeParserIssue("Function must have zero parameters", void 0, node);
|
|
3840
|
+
}
|
|
3841
|
+
if (node.typeParameters && node.typeParameters.length > 0) {
|
|
3842
|
+
return typeParserIssue("Function must have no type parameters", void 0, node);
|
|
3843
|
+
}
|
|
3844
|
+
const body = node.body;
|
|
3845
|
+
const returnType = node.type;
|
|
3846
|
+
if (ts.isArrowFunction(node) && !ts.isBlock(body)) {
|
|
3847
|
+
return succeed({
|
|
3848
|
+
node,
|
|
3849
|
+
body,
|
|
3850
|
+
expression: body,
|
|
3851
|
+
returnType
|
|
3852
|
+
});
|
|
3853
|
+
}
|
|
3854
|
+
if (ts.isBlock(body)) {
|
|
3855
|
+
if (body.statements.length !== 1) {
|
|
3856
|
+
return typeParserIssue("Block must have exactly one statement", void 0, node);
|
|
3857
|
+
}
|
|
3858
|
+
const stmt = body.statements[0];
|
|
3859
|
+
if (!ts.isReturnStatement(stmt)) {
|
|
3860
|
+
return typeParserIssue("Statement must be a return statement", void 0, node);
|
|
3861
|
+
}
|
|
3862
|
+
if (!stmt.expression) {
|
|
3863
|
+
return typeParserIssue("Return statement must have an expression", void 0, node);
|
|
3864
|
+
}
|
|
3865
|
+
return succeed({
|
|
3866
|
+
node,
|
|
3867
|
+
body,
|
|
3868
|
+
expression: stmt.expression,
|
|
3869
|
+
returnType
|
|
3870
|
+
});
|
|
3871
|
+
}
|
|
3872
|
+
return typeParserIssue("Invalid function body", void 0, node);
|
|
3873
|
+
},
|
|
3874
|
+
"TypeParser.lazyExpression",
|
|
3875
|
+
(node) => node
|
|
3876
|
+
);
|
|
3877
|
+
const emptyFunction = cachedBy(
|
|
3878
|
+
function(node) {
|
|
3879
|
+
if (!ts.isArrowFunction(node) && !ts.isFunctionExpression(node)) {
|
|
3880
|
+
return typeParserIssue("Node is not an arrow function or function expression", void 0, node);
|
|
3881
|
+
}
|
|
3882
|
+
const body = node.body;
|
|
3883
|
+
const returnType = node.type;
|
|
3884
|
+
if (!ts.isBlock(body)) {
|
|
3885
|
+
return typeParserIssue("Body must be a block", void 0, node);
|
|
3886
|
+
}
|
|
3887
|
+
if (body.statements.length !== 0) {
|
|
3888
|
+
return typeParserIssue("Block must have zero statements", void 0, node);
|
|
3889
|
+
}
|
|
3890
|
+
return succeed({
|
|
3891
|
+
node,
|
|
3892
|
+
body,
|
|
3893
|
+
returnType
|
|
3894
|
+
});
|
|
3895
|
+
},
|
|
3896
|
+
"TypeParser.emptyFunction",
|
|
3897
|
+
(node) => node
|
|
3898
|
+
);
|
|
3899
|
+
const pipingFlows = (includeEffectFn) => cachedBy(
|
|
3900
|
+
fn("TypeParser.pipingFlows")(function* (sourceFile) {
|
|
3901
|
+
const result = [];
|
|
3902
|
+
const workQueue = [[sourceFile, void 0]];
|
|
3903
|
+
while (workQueue.length > 0) {
|
|
3904
|
+
const [node, parentFlow] = workQueue.pop();
|
|
3905
|
+
if (ts.isCallExpression(node)) {
|
|
3906
|
+
const parsed = yield* pipe(
|
|
3907
|
+
pipeCall(node),
|
|
3908
|
+
map4((p) => ({ _tag: "pipe", ...p })),
|
|
3909
|
+
orElse2(
|
|
3910
|
+
() => pipe(
|
|
3911
|
+
singleArgCall(node),
|
|
3912
|
+
map4((s) => ({ _tag: "call", ...s }))
|
|
3913
|
+
)
|
|
3914
|
+
),
|
|
3915
|
+
option
|
|
3916
|
+
);
|
|
3917
|
+
if (isSome2(parsed)) {
|
|
3918
|
+
const result2 = parsed.value;
|
|
3919
|
+
let transformations;
|
|
3920
|
+
let flowNode;
|
|
3921
|
+
let childrenToTraverse = [];
|
|
3922
|
+
if (result2._tag === "pipe") {
|
|
3923
|
+
const signature = typeChecker.getResolvedSignature(result2.node);
|
|
3924
|
+
const typeArguments = signature ? typeChecker.getTypeArgumentsForResolvedSignature(signature) : void 0;
|
|
3925
|
+
transformations = [];
|
|
3926
|
+
for (let i = 0; i < result2.args.length; i++) {
|
|
3927
|
+
const arg = result2.args[i];
|
|
3928
|
+
const outType = typeArguments?.[i + 1];
|
|
3929
|
+
if (ts.isCallExpression(arg)) {
|
|
3930
|
+
transformations.push({
|
|
3931
|
+
callee: arg.expression,
|
|
3932
|
+
// e.g., Effect.map
|
|
3933
|
+
args: Array.from(arg.arguments),
|
|
3934
|
+
// e.g., [(x) => x + 1]
|
|
3935
|
+
outType,
|
|
3936
|
+
kind: result2.kind
|
|
3937
|
+
});
|
|
3938
|
+
} else {
|
|
3939
|
+
transformations.push({
|
|
3940
|
+
callee: arg,
|
|
3941
|
+
// e.g., Effect.asVoid
|
|
3942
|
+
args: void 0,
|
|
3943
|
+
outType,
|
|
3944
|
+
kind: result2.kind
|
|
3945
|
+
});
|
|
3946
|
+
}
|
|
3947
|
+
}
|
|
3948
|
+
flowNode = result2.node;
|
|
3949
|
+
childrenToTraverse = result2.args;
|
|
3950
|
+
} else {
|
|
3951
|
+
const callSignature = typeChecker.getResolvedSignature(node);
|
|
3952
|
+
const outType = callSignature ? typeChecker.getReturnTypeOfSignature(callSignature) : void 0;
|
|
3953
|
+
transformations = [{
|
|
3954
|
+
callee: result2.callee,
|
|
3955
|
+
args: void 0,
|
|
3956
|
+
outType,
|
|
3957
|
+
kind: "call"
|
|
3958
|
+
}];
|
|
3959
|
+
flowNode = node;
|
|
3960
|
+
}
|
|
3961
|
+
if (parentFlow) {
|
|
3962
|
+
parentFlow.transformations.unshift(...transformations);
|
|
3963
|
+
parentFlow.subject = {
|
|
3964
|
+
node: result2.subject,
|
|
3965
|
+
outType: typeCheckerUtils.getTypeAtLocation(result2.subject)
|
|
3966
|
+
};
|
|
3967
|
+
workQueue.push([result2.subject, parentFlow]);
|
|
3968
|
+
} else {
|
|
3969
|
+
const newFlow = {
|
|
3970
|
+
node: flowNode,
|
|
3971
|
+
subject: {
|
|
3972
|
+
node: result2.subject,
|
|
3973
|
+
outType: typeCheckerUtils.getTypeAtLocation(result2.subject)
|
|
3974
|
+
},
|
|
3975
|
+
transformations
|
|
3976
|
+
};
|
|
3977
|
+
workQueue.push([result2.subject, newFlow]);
|
|
3978
|
+
}
|
|
3979
|
+
for (const child of childrenToTraverse) {
|
|
3980
|
+
ts.forEachChild(child, (c) => {
|
|
3981
|
+
workQueue.push([c, void 0]);
|
|
3982
|
+
});
|
|
3983
|
+
}
|
|
3984
|
+
continue;
|
|
3985
|
+
}
|
|
3986
|
+
if (includeEffectFn) {
|
|
3987
|
+
const effectFnGenParsed = yield* pipe(effectFnGen(node), option);
|
|
3988
|
+
const effectFnUntracedGenParsed = isNone2(effectFnGenParsed) ? yield* pipe(effectFnUntracedGen(node), option) : none2();
|
|
3989
|
+
const isEffectFn = isSome2(effectFnGenParsed);
|
|
3990
|
+
const effectFnParsed = isEffectFn ? effectFnGenParsed : effectFnUntracedGenParsed;
|
|
3991
|
+
const transformationKind = isEffectFn ? "effectFn" : "effectFnUntraced";
|
|
3992
|
+
if (isSome2(effectFnParsed) && effectFnParsed.value.pipeArguments.length > 0) {
|
|
3993
|
+
const fnResult = effectFnParsed.value;
|
|
3994
|
+
const pipeArgs = fnResult.pipeArguments;
|
|
3995
|
+
const transformations = [];
|
|
3996
|
+
let subjectType;
|
|
3997
|
+
for (let i = 0; i < pipeArgs.length; i++) {
|
|
3998
|
+
const arg = pipeArgs[i];
|
|
3999
|
+
const contextualType = typeChecker.getContextualType(arg);
|
|
4000
|
+
const callSigs = contextualType ? typeChecker.getSignaturesOfType(contextualType, ts.SignatureKind.Call) : [];
|
|
4001
|
+
const outType = callSigs.length > 0 ? typeChecker.getReturnTypeOfSignature(callSigs[0]) : void 0;
|
|
4002
|
+
if (i === 0 && callSigs.length > 0) {
|
|
4003
|
+
const params = callSigs[0].parameters;
|
|
4004
|
+
if (params.length > 0) {
|
|
4005
|
+
subjectType = typeChecker.getTypeOfSymbol(params[0]);
|
|
4006
|
+
}
|
|
4007
|
+
}
|
|
4008
|
+
if (ts.isCallExpression(arg)) {
|
|
4009
|
+
transformations.push({
|
|
4010
|
+
callee: arg.expression,
|
|
4011
|
+
args: Array.from(arg.arguments),
|
|
4012
|
+
outType,
|
|
4013
|
+
kind: transformationKind
|
|
4014
|
+
});
|
|
4015
|
+
} else {
|
|
4016
|
+
transformations.push({
|
|
4017
|
+
callee: arg,
|
|
4018
|
+
args: void 0,
|
|
4019
|
+
outType,
|
|
4020
|
+
kind: transformationKind
|
|
4021
|
+
});
|
|
4022
|
+
}
|
|
4023
|
+
}
|
|
4024
|
+
const newFlow = {
|
|
4025
|
+
node,
|
|
4026
|
+
subject: {
|
|
4027
|
+
node,
|
|
4028
|
+
outType: subjectType
|
|
4029
|
+
},
|
|
4030
|
+
transformations
|
|
4031
|
+
};
|
|
4032
|
+
result.push(newFlow);
|
|
4033
|
+
workQueue.push([fnResult.body, void 0]);
|
|
4034
|
+
for (const arg of pipeArgs) {
|
|
4035
|
+
ts.forEachChild(arg, (c) => {
|
|
4036
|
+
workQueue.push([c, void 0]);
|
|
4037
|
+
});
|
|
4038
|
+
}
|
|
4039
|
+
continue;
|
|
4040
|
+
}
|
|
4041
|
+
}
|
|
4042
|
+
}
|
|
4043
|
+
if (parentFlow && parentFlow.transformations.length > 0) {
|
|
4044
|
+
result.push(parentFlow);
|
|
4045
|
+
}
|
|
4046
|
+
ts.forEachChild(node, (child) => {
|
|
4047
|
+
workQueue.push([child, void 0]);
|
|
4048
|
+
});
|
|
4049
|
+
}
|
|
4050
|
+
result.sort((a, b) => a.node.pos - b.node.pos);
|
|
4051
|
+
return result;
|
|
4052
|
+
}),
|
|
4053
|
+
`TypeParser.pipingFlows(${includeEffectFn})`,
|
|
4054
|
+
(sourceFile) => sourceFile
|
|
4055
|
+
);
|
|
4056
|
+
const reconstructPipingFlow = (flow2) => {
|
|
4057
|
+
if (flow2.transformations.length > 0 && flow2.transformations.every((t) => t.kind === "effectFn" || t.kind === "effectFnUntraced")) {
|
|
4058
|
+
return flow2.subject.node;
|
|
4059
|
+
}
|
|
4060
|
+
let result = flow2.subject.node;
|
|
4061
|
+
for (const t of flow2.transformations) {
|
|
4062
|
+
if (t.kind === "call") {
|
|
4063
|
+
result = ts.factory.createCallExpression(
|
|
4064
|
+
t.callee,
|
|
4065
|
+
void 0,
|
|
4066
|
+
[result]
|
|
4067
|
+
);
|
|
4068
|
+
} else if (t.kind === "effectFn" || t.kind === "effectFnUntraced") {
|
|
4069
|
+
continue;
|
|
4070
|
+
} else {
|
|
4071
|
+
if (t.args) {
|
|
4072
|
+
const transformCall = ts.factory.createCallExpression(
|
|
4073
|
+
t.callee,
|
|
4074
|
+
void 0,
|
|
4075
|
+
t.args
|
|
4076
|
+
);
|
|
4077
|
+
result = ts.factory.createCallExpression(
|
|
4078
|
+
transformCall,
|
|
4079
|
+
void 0,
|
|
4080
|
+
[result]
|
|
4081
|
+
);
|
|
4082
|
+
} else {
|
|
4083
|
+
result = ts.factory.createCallExpression(
|
|
4084
|
+
t.callee,
|
|
4085
|
+
void 0,
|
|
4086
|
+
[result]
|
|
4087
|
+
);
|
|
4088
|
+
}
|
|
4089
|
+
}
|
|
4090
|
+
}
|
|
4091
|
+
return result;
|
|
4092
|
+
};
|
|
3802
4093
|
return {
|
|
3803
4094
|
isNodeReferenceToEffectModuleApi,
|
|
3804
4095
|
isNodeReferenceToEffectSchemaModuleApi,
|
|
@@ -3821,6 +4112,7 @@ function make2(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
|
|
|
3821
4112
|
contextTag,
|
|
3822
4113
|
pipeableType,
|
|
3823
4114
|
pipeCall,
|
|
4115
|
+
singleArgCall,
|
|
3824
4116
|
scopeType,
|
|
3825
4117
|
promiseLike,
|
|
3826
4118
|
extendsEffectTag,
|
|
@@ -3832,7 +4124,11 @@ function make2(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
|
|
|
3832
4124
|
extendsDataTaggedError,
|
|
3833
4125
|
extendsDataTaggedClass,
|
|
3834
4126
|
extendsSchemaTaggedRequest,
|
|
3835
|
-
extendsEffectSqlModelClass
|
|
4127
|
+
extendsEffectSqlModelClass,
|
|
4128
|
+
lazyExpression,
|
|
4129
|
+
emptyFunction,
|
|
4130
|
+
pipingFlows,
|
|
4131
|
+
reconstructPipingFlow
|
|
3836
4132
|
};
|
|
3837
4133
|
}
|
|
3838
4134
|
|
|
@@ -3986,48 +4282,45 @@ var catchAllToMapError = createDiagnostic({
|
|
|
3986
4282
|
return none2();
|
|
3987
4283
|
});
|
|
3988
4284
|
};
|
|
3989
|
-
const
|
|
3990
|
-
const
|
|
3991
|
-
|
|
3992
|
-
|
|
3993
|
-
|
|
3994
|
-
|
|
3995
|
-
while (nodeToVisit.length > 0) {
|
|
3996
|
-
const node = nodeToVisit.shift();
|
|
3997
|
-
ts.forEachChild(node, appendNodeToVisit);
|
|
3998
|
-
if (ts.isCallExpression(node)) {
|
|
4285
|
+
const flows = yield* typeParser.pipingFlows(true)(sourceFile);
|
|
4286
|
+
for (const flow2 of flows) {
|
|
4287
|
+
for (const transformation of flow2.transformations) {
|
|
4288
|
+
if (!transformation.args || transformation.args.length === 0) {
|
|
4289
|
+
continue;
|
|
4290
|
+
}
|
|
3999
4291
|
const isCatchAllCall = yield* pipe(
|
|
4000
|
-
typeParser.isNodeReferenceToEffectModuleApi("catchAll")(
|
|
4292
|
+
typeParser.isNodeReferenceToEffectModuleApi("catchAll")(transformation.callee),
|
|
4001
4293
|
option
|
|
4002
4294
|
);
|
|
4003
|
-
if (
|
|
4004
|
-
|
|
4005
|
-
if (!callback) continue;
|
|
4006
|
-
const functionBody = getFunctionBody(callback);
|
|
4007
|
-
if (!functionBody) continue;
|
|
4008
|
-
const failCallInfo = yield* getEffectFailCallInfo(functionBody);
|
|
4009
|
-
if (isNone2(failCallInfo)) continue;
|
|
4010
|
-
const { failArg, failCall } = failCallInfo.value;
|
|
4011
|
-
report({
|
|
4012
|
-
location: node.expression,
|
|
4013
|
-
messageText: `You can use Effect.mapError instead of Effect.catchAll + Effect.fail to transform the error type.`,
|
|
4014
|
-
fixes: [{
|
|
4015
|
-
fixName: "catchAllToMapError_fix",
|
|
4016
|
-
description: "Replace with Effect.mapError",
|
|
4017
|
-
apply: gen(function* () {
|
|
4018
|
-
const changeTracker = yield* service(ChangeTracker);
|
|
4019
|
-
if (ts.isPropertyAccessExpression(node.expression)) {
|
|
4020
|
-
changeTracker.replaceNode(
|
|
4021
|
-
sourceFile,
|
|
4022
|
-
node.expression.name,
|
|
4023
|
-
ts.factory.createIdentifier("mapError")
|
|
4024
|
-
);
|
|
4025
|
-
}
|
|
4026
|
-
changeTracker.replaceNode(sourceFile, failCall, failArg);
|
|
4027
|
-
})
|
|
4028
|
-
}]
|
|
4029
|
-
});
|
|
4295
|
+
if (isNone2(isCatchAllCall)) {
|
|
4296
|
+
continue;
|
|
4030
4297
|
}
|
|
4298
|
+
const callback = transformation.args[0];
|
|
4299
|
+
if (!callback) continue;
|
|
4300
|
+
const functionBody = getFunctionBody(callback);
|
|
4301
|
+
if (!functionBody) continue;
|
|
4302
|
+
const failCallInfo = yield* getEffectFailCallInfo(functionBody);
|
|
4303
|
+
if (isNone2(failCallInfo)) continue;
|
|
4304
|
+
const { failArg, failCall } = failCallInfo.value;
|
|
4305
|
+
report({
|
|
4306
|
+
location: transformation.callee,
|
|
4307
|
+
messageText: `You can use Effect.mapError instead of Effect.catchAll + Effect.fail to transform the error type.`,
|
|
4308
|
+
fixes: [{
|
|
4309
|
+
fixName: "catchAllToMapError_fix",
|
|
4310
|
+
description: "Replace with Effect.mapError",
|
|
4311
|
+
apply: gen(function* () {
|
|
4312
|
+
const changeTracker = yield* service(ChangeTracker);
|
|
4313
|
+
if (ts.isPropertyAccessExpression(transformation.callee)) {
|
|
4314
|
+
changeTracker.replaceNode(
|
|
4315
|
+
sourceFile,
|
|
4316
|
+
transformation.callee.name,
|
|
4317
|
+
ts.factory.createIdentifier("mapError")
|
|
4318
|
+
);
|
|
4319
|
+
}
|
|
4320
|
+
changeTracker.replaceNode(sourceFile, failCall, failArg);
|
|
4321
|
+
})
|
|
4322
|
+
}]
|
|
4323
|
+
});
|
|
4031
4324
|
}
|
|
4032
4325
|
}
|
|
4033
4326
|
})
|
|
@@ -4042,66 +4335,39 @@ var catchUnfailableEffect = createDiagnostic({
|
|
|
4042
4335
|
apply: fn("catchUnfailableEffect.apply")(function* (sourceFile, report) {
|
|
4043
4336
|
const ts = yield* service(TypeScriptApi);
|
|
4044
4337
|
const typeParser = yield* service(TypeParser);
|
|
4045
|
-
const
|
|
4046
|
-
const
|
|
4047
|
-
const
|
|
4048
|
-
|
|
4049
|
-
|
|
4050
|
-
|
|
4051
|
-
|
|
4052
|
-
|
|
4053
|
-
while (nodeToVisit.length > 0) {
|
|
4054
|
-
const node = nodeToVisit.shift();
|
|
4055
|
-
ts.forEachChild(node, appendNodeToVisit);
|
|
4056
|
-
if (ts.isCallExpression(node)) {
|
|
4057
|
-
const catchFunctions = ["catchAll", "catch", "catchIf", "catchSome", "catchTag", "catchTags"];
|
|
4338
|
+
const catchFunctions = ["catchAll", "catch", "catchIf", "catchSome", "catchTag", "catchTags"];
|
|
4339
|
+
const flows = yield* typeParser.pipingFlows(true)(sourceFile);
|
|
4340
|
+
for (const flow2 of flows) {
|
|
4341
|
+
for (let i = 0; i < flow2.transformations.length; i++) {
|
|
4342
|
+
const transformation = flow2.transformations[i];
|
|
4343
|
+
if (!transformation.args || transformation.args.length === 0) {
|
|
4344
|
+
continue;
|
|
4345
|
+
}
|
|
4058
4346
|
const isCatchCall = yield* pipe(
|
|
4059
4347
|
firstSuccessOf(
|
|
4060
|
-
catchFunctions.map((catchFn) => typeParser.isNodeReferenceToEffectModuleApi(catchFn)(
|
|
4348
|
+
catchFunctions.map((catchFn) => typeParser.isNodeReferenceToEffectModuleApi(catchFn)(transformation.callee))
|
|
4061
4349
|
),
|
|
4062
4350
|
option
|
|
4063
4351
|
);
|
|
4064
|
-
if (
|
|
4065
|
-
|
|
4066
|
-
|
|
4067
|
-
|
|
4068
|
-
|
|
4069
|
-
|
|
4070
|
-
|
|
4071
|
-
|
|
4072
|
-
|
|
4073
|
-
|
|
4074
|
-
|
|
4075
|
-
|
|
4076
|
-
|
|
4077
|
-
|
|
4078
|
-
|
|
4079
|
-
|
|
4080
|
-
|
|
4081
|
-
|
|
4082
|
-
|
|
4083
|
-
effectTypeToCheck = typeArguments[argIndex];
|
|
4084
|
-
}
|
|
4085
|
-
}
|
|
4086
|
-
}
|
|
4087
|
-
if (effectTypeToCheck) {
|
|
4088
|
-
const effectType = yield* pipe(
|
|
4089
|
-
typeParser.effectType(effectTypeToCheck, node),
|
|
4090
|
-
option
|
|
4091
|
-
);
|
|
4092
|
-
if (isSome2(effectType)) {
|
|
4093
|
-
const { E } = effectType.value;
|
|
4094
|
-
if (E.flags & ts.TypeFlags.Never) {
|
|
4095
|
-
report({
|
|
4096
|
-
location: node.expression,
|
|
4097
|
-
messageText: `Looks like the previous effect never fails, so probably this error handling will never be triggered.`,
|
|
4098
|
-
fixes: []
|
|
4099
|
-
});
|
|
4100
|
-
}
|
|
4101
|
-
}
|
|
4102
|
-
}
|
|
4103
|
-
}
|
|
4104
|
-
}
|
|
4352
|
+
if (isNone2(isCatchCall)) {
|
|
4353
|
+
continue;
|
|
4354
|
+
}
|
|
4355
|
+
const inputType = i === 0 ? flow2.subject.outType : flow2.transformations[i - 1].outType;
|
|
4356
|
+
if (!inputType) {
|
|
4357
|
+
continue;
|
|
4358
|
+
}
|
|
4359
|
+
const effectType = yield* pipe(
|
|
4360
|
+
typeParser.effectType(inputType, transformation.callee),
|
|
4361
|
+
option
|
|
4362
|
+
);
|
|
4363
|
+
if (isSome2(effectType)) {
|
|
4364
|
+
const { E } = effectType.value;
|
|
4365
|
+
if (E.flags & ts.TypeFlags.Never) {
|
|
4366
|
+
report({
|
|
4367
|
+
location: transformation.callee,
|
|
4368
|
+
messageText: `Looks like the previous effect never fails, so probably this error handling will never be triggered.`,
|
|
4369
|
+
fixes: []
|
|
4370
|
+
});
|
|
4105
4371
|
}
|
|
4106
4372
|
}
|
|
4107
4373
|
}
|
|
@@ -4410,6 +4676,245 @@ ${versions.map((version) => `- found ${version} at ${resolvedPackages[packageNam
|
|
|
4410
4676
|
})
|
|
4411
4677
|
});
|
|
4412
4678
|
|
|
4679
|
+
// src/diagnostics/effectFnOpportunity.ts
|
|
4680
|
+
var parseEffectFnOpportunityTarget = (node, sourceFile) => gen(function* () {
|
|
4681
|
+
const ts = yield* service(TypeScriptApi);
|
|
4682
|
+
const typeChecker = yield* service(TypeCheckerApi);
|
|
4683
|
+
const typeParser = yield* service(TypeParser);
|
|
4684
|
+
const tsUtils = yield* service(TypeScriptUtils);
|
|
4685
|
+
if (!ts.isFunctionExpression(node) && !ts.isArrowFunction(node) && !ts.isFunctionDeclaration(node)) {
|
|
4686
|
+
return yield* TypeParserIssue.issue;
|
|
4687
|
+
}
|
|
4688
|
+
if ((ts.isFunctionExpression(node) || ts.isFunctionDeclaration(node)) && node.asteriskToken) {
|
|
4689
|
+
return yield* TypeParserIssue.issue;
|
|
4690
|
+
}
|
|
4691
|
+
if (ts.isFunctionExpression(node) && node.name) {
|
|
4692
|
+
return yield* TypeParserIssue.issue;
|
|
4693
|
+
}
|
|
4694
|
+
let bodyExpression;
|
|
4695
|
+
if (ts.isArrowFunction(node)) {
|
|
4696
|
+
if (ts.isBlock(node.body)) {
|
|
4697
|
+
const returnStatement = findSingleReturnStatement(ts, node.body);
|
|
4698
|
+
if (returnStatement?.expression) {
|
|
4699
|
+
bodyExpression = returnStatement.expression;
|
|
4700
|
+
}
|
|
4701
|
+
} else {
|
|
4702
|
+
bodyExpression = node.body;
|
|
4703
|
+
}
|
|
4704
|
+
} else if ((ts.isFunctionExpression(node) || ts.isFunctionDeclaration(node)) && node.body) {
|
|
4705
|
+
const returnStatement = findSingleReturnStatement(ts, node.body);
|
|
4706
|
+
if (returnStatement?.expression) {
|
|
4707
|
+
bodyExpression = returnStatement.expression;
|
|
4708
|
+
}
|
|
4709
|
+
}
|
|
4710
|
+
if (!bodyExpression) return yield* TypeParserIssue.issue;
|
|
4711
|
+
const { pipeArguments: pipeArguments2, subject } = yield* pipe(
|
|
4712
|
+
typeParser.pipeCall(bodyExpression),
|
|
4713
|
+
map4(({ args: args2, subject: subject2 }) => ({ subject: subject2, pipeArguments: args2 })),
|
|
4714
|
+
orElse2(() => succeed({ subject: bodyExpression, pipeArguments: [] }))
|
|
4715
|
+
);
|
|
4716
|
+
const { effectModule, generatorFunction } = yield* typeParser.effectGen(subject);
|
|
4717
|
+
const functionType = typeChecker.getTypeAtLocation(node);
|
|
4718
|
+
if (!functionType) return yield* TypeParserIssue.issue;
|
|
4719
|
+
const callSignatures = typeChecker.getSignaturesOfType(functionType, ts.SignatureKind.Call);
|
|
4720
|
+
if (callSignatures.length !== 1) return yield* TypeParserIssue.issue;
|
|
4721
|
+
const signature = callSignatures[0];
|
|
4722
|
+
const returnType = typeChecker.getReturnTypeOfSignature(signature);
|
|
4723
|
+
const { A, E, R } = yield* typeParser.strictEffectType(returnType, node);
|
|
4724
|
+
const effectModuleName = ts.isIdentifier(effectModule) ? ts.idText(effectModule) : tsUtils.findImportedModuleIdentifierByPackageAndNameOrBarrel(
|
|
4725
|
+
sourceFile,
|
|
4726
|
+
"effect",
|
|
4727
|
+
"Effect"
|
|
4728
|
+
) || "Effect";
|
|
4729
|
+
const nameIdentifier = getNameIdentifier(ts, node);
|
|
4730
|
+
const traceName = nameIdentifier ? ts.isIdentifier(nameIdentifier) ? ts.idText(nameIdentifier) : nameIdentifier.text : void 0;
|
|
4731
|
+
const hasReturnTypeAnnotation = !!node.type;
|
|
4732
|
+
return {
|
|
4733
|
+
node,
|
|
4734
|
+
nameIdentifier,
|
|
4735
|
+
effectModule,
|
|
4736
|
+
generatorFunction,
|
|
4737
|
+
effectModuleName,
|
|
4738
|
+
traceName,
|
|
4739
|
+
hasReturnTypeAnnotation,
|
|
4740
|
+
effectTypes: { A, E, R },
|
|
4741
|
+
pipeArguments: pipeArguments2
|
|
4742
|
+
};
|
|
4743
|
+
});
|
|
4744
|
+
var effectFnOpportunity = createDiagnostic({
|
|
4745
|
+
name: "effectFnOpportunity",
|
|
4746
|
+
code: 41,
|
|
4747
|
+
description: "Suggests using Effect.fn for functions that return Effect.gen",
|
|
4748
|
+
severity: "suggestion",
|
|
4749
|
+
apply: fn("effectFnOpportunity.apply")(function* (sourceFile, report) {
|
|
4750
|
+
const ts = yield* service(TypeScriptApi);
|
|
4751
|
+
const typeChecker = yield* service(TypeCheckerApi);
|
|
4752
|
+
const tsUtils = yield* service(TypeScriptUtils);
|
|
4753
|
+
const createReturnTypeAnnotation = (effectModuleName, effectTypes, enclosingNode) => {
|
|
4754
|
+
const { A, E, R } = effectTypes;
|
|
4755
|
+
const aTypeNode = typeChecker.typeToTypeNode(A, enclosingNode, ts.NodeBuilderFlags.NoTruncation);
|
|
4756
|
+
const eTypeNode = typeChecker.typeToTypeNode(E, enclosingNode, ts.NodeBuilderFlags.NoTruncation);
|
|
4757
|
+
const rTypeNode = typeChecker.typeToTypeNode(R, enclosingNode, ts.NodeBuilderFlags.NoTruncation);
|
|
4758
|
+
if (!aTypeNode || !eTypeNode || !rTypeNode) return void 0;
|
|
4759
|
+
return ts.factory.createTypeReferenceNode(
|
|
4760
|
+
ts.factory.createQualifiedName(
|
|
4761
|
+
ts.factory.createQualifiedName(
|
|
4762
|
+
ts.factory.createIdentifier(effectModuleName),
|
|
4763
|
+
"fn"
|
|
4764
|
+
),
|
|
4765
|
+
"Return"
|
|
4766
|
+
),
|
|
4767
|
+
[aTypeNode, eTypeNode, rTypeNode]
|
|
4768
|
+
);
|
|
4769
|
+
};
|
|
4770
|
+
const createEffectFnNode = (originalNode, generatorFunction, effectModuleName, traceName, effectTypes, pipeArguments2) => {
|
|
4771
|
+
const returnTypeAnnotation = effectTypes ? createReturnTypeAnnotation(effectModuleName, effectTypes, originalNode) : void 0;
|
|
4772
|
+
const newGeneratorFunction = ts.factory.createFunctionExpression(
|
|
4773
|
+
void 0,
|
|
4774
|
+
ts.factory.createToken(ts.SyntaxKind.AsteriskToken),
|
|
4775
|
+
void 0,
|
|
4776
|
+
originalNode.typeParameters,
|
|
4777
|
+
originalNode.parameters,
|
|
4778
|
+
returnTypeAnnotation,
|
|
4779
|
+
generatorFunction.body
|
|
4780
|
+
);
|
|
4781
|
+
let fnExpression = ts.factory.createPropertyAccessExpression(
|
|
4782
|
+
ts.factory.createIdentifier(effectModuleName),
|
|
4783
|
+
"fn"
|
|
4784
|
+
);
|
|
4785
|
+
if (traceName) {
|
|
4786
|
+
fnExpression = ts.factory.createCallExpression(
|
|
4787
|
+
fnExpression,
|
|
4788
|
+
void 0,
|
|
4789
|
+
[ts.factory.createStringLiteral(traceName)]
|
|
4790
|
+
);
|
|
4791
|
+
}
|
|
4792
|
+
const effectFnCall = ts.factory.createCallExpression(
|
|
4793
|
+
fnExpression,
|
|
4794
|
+
void 0,
|
|
4795
|
+
[newGeneratorFunction, ...pipeArguments2]
|
|
4796
|
+
);
|
|
4797
|
+
if (ts.isFunctionDeclaration(originalNode)) {
|
|
4798
|
+
return tsUtils.tryPreserveDeclarationSemantics(originalNode, effectFnCall, false);
|
|
4799
|
+
}
|
|
4800
|
+
return effectFnCall;
|
|
4801
|
+
};
|
|
4802
|
+
const createEffectFnUntracedNode = (originalNode, generatorFunction, effectModuleName, effectTypes, pipeArguments2) => {
|
|
4803
|
+
const returnTypeAnnotation = effectTypes ? createReturnTypeAnnotation(effectModuleName, effectTypes, originalNode) : void 0;
|
|
4804
|
+
const newGeneratorFunction = ts.factory.createFunctionExpression(
|
|
4805
|
+
void 0,
|
|
4806
|
+
ts.factory.createToken(ts.SyntaxKind.AsteriskToken),
|
|
4807
|
+
void 0,
|
|
4808
|
+
originalNode.typeParameters,
|
|
4809
|
+
originalNode.parameters,
|
|
4810
|
+
returnTypeAnnotation,
|
|
4811
|
+
generatorFunction.body
|
|
4812
|
+
);
|
|
4813
|
+
const effectFnCall = ts.factory.createCallExpression(
|
|
4814
|
+
ts.factory.createPropertyAccessExpression(
|
|
4815
|
+
ts.factory.createIdentifier(effectModuleName),
|
|
4816
|
+
"fnUntraced"
|
|
4817
|
+
),
|
|
4818
|
+
void 0,
|
|
4819
|
+
[newGeneratorFunction, ...pipeArguments2]
|
|
4820
|
+
);
|
|
4821
|
+
if (ts.isFunctionDeclaration(originalNode)) {
|
|
4822
|
+
return tsUtils.tryPreserveDeclarationSemantics(originalNode, effectFnCall, false);
|
|
4823
|
+
}
|
|
4824
|
+
return effectFnCall;
|
|
4825
|
+
};
|
|
4826
|
+
const nodeToVisit = [];
|
|
4827
|
+
const appendNodeToVisit = (node) => {
|
|
4828
|
+
nodeToVisit.push(node);
|
|
4829
|
+
return void 0;
|
|
4830
|
+
};
|
|
4831
|
+
ts.forEachChild(sourceFile, appendNodeToVisit);
|
|
4832
|
+
while (nodeToVisit.length > 0) {
|
|
4833
|
+
const node = nodeToVisit.shift();
|
|
4834
|
+
ts.forEachChild(node, appendNodeToVisit);
|
|
4835
|
+
const target = yield* pipe(
|
|
4836
|
+
parseEffectFnOpportunityTarget(node, sourceFile),
|
|
4837
|
+
option
|
|
4838
|
+
);
|
|
4839
|
+
if (isNone2(target)) continue;
|
|
4840
|
+
const {
|
|
4841
|
+
effectModuleName,
|
|
4842
|
+
effectTypes,
|
|
4843
|
+
generatorFunction,
|
|
4844
|
+
hasReturnTypeAnnotation,
|
|
4845
|
+
nameIdentifier,
|
|
4846
|
+
node: targetNode,
|
|
4847
|
+
pipeArguments: pipeArguments2,
|
|
4848
|
+
traceName
|
|
4849
|
+
} = target.value;
|
|
4850
|
+
const fixes = [];
|
|
4851
|
+
fixes.push({
|
|
4852
|
+
fixName: "effectFnOpportunity_toEffectFn",
|
|
4853
|
+
description: traceName ? `Convert to Effect.fn("${traceName}")` : "Convert to Effect.fn",
|
|
4854
|
+
apply: gen(function* () {
|
|
4855
|
+
const changeTracker = yield* service(ChangeTracker);
|
|
4856
|
+
const newNode = createEffectFnNode(
|
|
4857
|
+
targetNode,
|
|
4858
|
+
generatorFunction,
|
|
4859
|
+
effectModuleName,
|
|
4860
|
+
traceName,
|
|
4861
|
+
hasReturnTypeAnnotation ? effectTypes : void 0,
|
|
4862
|
+
pipeArguments2
|
|
4863
|
+
);
|
|
4864
|
+
changeTracker.replaceNode(sourceFile, targetNode, newNode);
|
|
4865
|
+
})
|
|
4866
|
+
});
|
|
4867
|
+
fixes.push({
|
|
4868
|
+
fixName: "effectFnOpportunity_toEffectFnUntraced",
|
|
4869
|
+
description: "Convert to Effect.fnUntraced",
|
|
4870
|
+
apply: gen(function* () {
|
|
4871
|
+
const changeTracker = yield* service(ChangeTracker);
|
|
4872
|
+
const newNode = createEffectFnUntracedNode(
|
|
4873
|
+
targetNode,
|
|
4874
|
+
generatorFunction,
|
|
4875
|
+
effectModuleName,
|
|
4876
|
+
hasReturnTypeAnnotation ? effectTypes : void 0,
|
|
4877
|
+
pipeArguments2
|
|
4878
|
+
);
|
|
4879
|
+
changeTracker.replaceNode(sourceFile, targetNode, newNode);
|
|
4880
|
+
})
|
|
4881
|
+
});
|
|
4882
|
+
report({
|
|
4883
|
+
location: nameIdentifier ?? targetNode,
|
|
4884
|
+
messageText: `This function could benefit from Effect.fn's automatic tracing and concise syntax, or Effect.fnUntraced to get just a more concise syntax.`,
|
|
4885
|
+
fixes
|
|
4886
|
+
});
|
|
4887
|
+
}
|
|
4888
|
+
})
|
|
4889
|
+
});
|
|
4890
|
+
function findSingleReturnStatement(ts, block) {
|
|
4891
|
+
if (block.statements.length !== 1) return void 0;
|
|
4892
|
+
const statement = block.statements[0];
|
|
4893
|
+
if (!ts.isReturnStatement(statement)) return void 0;
|
|
4894
|
+
return statement;
|
|
4895
|
+
}
|
|
4896
|
+
function getNameIdentifier(ts, node) {
|
|
4897
|
+
if (ts.isFunctionDeclaration(node) && node.name) {
|
|
4898
|
+
return node.name;
|
|
4899
|
+
}
|
|
4900
|
+
if (node.parent && ts.isVariableDeclaration(node.parent) && ts.isIdentifier(node.parent.name)) {
|
|
4901
|
+
return node.parent.name;
|
|
4902
|
+
}
|
|
4903
|
+
if (node.parent && ts.isPropertyAssignment(node.parent)) {
|
|
4904
|
+
const name = node.parent.name;
|
|
4905
|
+
if (ts.isIdentifier(name) || ts.isStringLiteral(name)) {
|
|
4906
|
+
return name;
|
|
4907
|
+
}
|
|
4908
|
+
}
|
|
4909
|
+
if (node.parent && ts.isPropertyDeclaration(node.parent)) {
|
|
4910
|
+
const name = node.parent.name;
|
|
4911
|
+
if (ts.isIdentifier(name)) {
|
|
4912
|
+
return name;
|
|
4913
|
+
}
|
|
4914
|
+
}
|
|
4915
|
+
return void 0;
|
|
4916
|
+
}
|
|
4917
|
+
|
|
4413
4918
|
// src/diagnostics/effectGenUsesAdapter.ts
|
|
4414
4919
|
var effectGenUsesAdapter = createDiagnostic({
|
|
4415
4920
|
name: "effectGenUsesAdapter",
|
|
@@ -4497,6 +5002,70 @@ var effectInVoidSuccess = createDiagnostic({
|
|
|
4497
5002
|
})
|
|
4498
5003
|
});
|
|
4499
5004
|
|
|
5005
|
+
// src/diagnostics/effectMapVoid.ts
|
|
5006
|
+
var effectMapVoid = createDiagnostic({
|
|
5007
|
+
name: "effectMapVoid",
|
|
5008
|
+
code: 40,
|
|
5009
|
+
description: "Suggests using Effect.asVoid instead of Effect.map(() => void 0), Effect.map(() => undefined), or Effect.map(() => {})",
|
|
5010
|
+
severity: "suggestion",
|
|
5011
|
+
apply: fn("effectMapVoid.apply")(function* (sourceFile, report) {
|
|
5012
|
+
const ts = yield* service(TypeScriptApi);
|
|
5013
|
+
const typeParser = yield* service(TypeParser);
|
|
5014
|
+
const tsUtils = yield* service(TypeScriptUtils);
|
|
5015
|
+
const nodeToVisit = [];
|
|
5016
|
+
const appendNodeToVisit = (node) => {
|
|
5017
|
+
nodeToVisit.push(node);
|
|
5018
|
+
return void 0;
|
|
5019
|
+
};
|
|
5020
|
+
ts.forEachChild(sourceFile, appendNodeToVisit);
|
|
5021
|
+
while (nodeToVisit.length > 0) {
|
|
5022
|
+
const node = nodeToVisit.shift();
|
|
5023
|
+
ts.forEachChild(node, appendNodeToVisit);
|
|
5024
|
+
if (ts.isCallExpression(node)) {
|
|
5025
|
+
const isMapCall = yield* pipe(
|
|
5026
|
+
typeParser.isNodeReferenceToEffectModuleApi("map")(node.expression),
|
|
5027
|
+
option
|
|
5028
|
+
);
|
|
5029
|
+
if (isSome2(isMapCall)) {
|
|
5030
|
+
const callback = node.arguments[0];
|
|
5031
|
+
if (!callback) continue;
|
|
5032
|
+
const match2 = yield* pipe(
|
|
5033
|
+
typeParser.emptyFunction(callback),
|
|
5034
|
+
orElse2(
|
|
5035
|
+
() => pipe(
|
|
5036
|
+
typeParser.lazyExpression(callback),
|
|
5037
|
+
flatMap2(
|
|
5038
|
+
(lazy) => tsUtils.isVoidExpression(lazy.expression) ? succeed(lazy) : typeParserIssue("Expression is not void")
|
|
5039
|
+
)
|
|
5040
|
+
)
|
|
5041
|
+
),
|
|
5042
|
+
option
|
|
5043
|
+
);
|
|
5044
|
+
if (isNone2(match2)) continue;
|
|
5045
|
+
report({
|
|
5046
|
+
location: node.expression,
|
|
5047
|
+
messageText: "Effect.asVoid can be used instead to discard the success value",
|
|
5048
|
+
fixes: [{
|
|
5049
|
+
fixName: "effectMapVoid_fix",
|
|
5050
|
+
description: "Replace with Effect.asVoid",
|
|
5051
|
+
apply: gen(function* () {
|
|
5052
|
+
const changeTracker = yield* service(ChangeTracker);
|
|
5053
|
+
if (ts.isPropertyAccessExpression(node.expression)) {
|
|
5054
|
+
const newNode = ts.factory.createPropertyAccessExpression(
|
|
5055
|
+
node.expression.expression,
|
|
5056
|
+
ts.factory.createIdentifier("asVoid")
|
|
5057
|
+
);
|
|
5058
|
+
changeTracker.replaceNode(sourceFile, node, newNode);
|
|
5059
|
+
}
|
|
5060
|
+
})
|
|
5061
|
+
}]
|
|
5062
|
+
});
|
|
5063
|
+
}
|
|
5064
|
+
}
|
|
5065
|
+
}
|
|
5066
|
+
})
|
|
5067
|
+
});
|
|
5068
|
+
|
|
4500
5069
|
// src/diagnostics/floatingEffect.ts
|
|
4501
5070
|
var floatingEffect = createDiagnostic({
|
|
4502
5071
|
name: "floatingEffect",
|
|
@@ -5105,75 +5674,91 @@ var missedPipeableOpportunity = createDiagnostic({
|
|
|
5105
5674
|
apply: fn("missedPipeableOpportunity.apply")(function* (sourceFile, report) {
|
|
5106
5675
|
const ts = yield* service(TypeScriptApi);
|
|
5107
5676
|
const typeChecker = yield* service(TypeCheckerApi);
|
|
5108
|
-
const typeCheckerUtils = yield* service(TypeCheckerUtils);
|
|
5109
5677
|
const typeParser = yield* service(TypeParser);
|
|
5110
5678
|
const options = yield* service(LanguageServicePluginOptions);
|
|
5111
|
-
const
|
|
5112
|
-
const
|
|
5113
|
-
|
|
5114
|
-
|
|
5115
|
-
|
|
5116
|
-
|
|
5117
|
-
|
|
5118
|
-
|
|
5119
|
-
|
|
5120
|
-
|
|
5121
|
-
|
|
5122
|
-
|
|
5123
|
-
|
|
5124
|
-
|
|
5125
|
-
|
|
5126
|
-
|
|
5127
|
-
|
|
5128
|
-
|
|
5129
|
-
|
|
5130
|
-
|
|
5679
|
+
const flows = yield* typeParser.pipingFlows(false)(sourceFile);
|
|
5680
|
+
for (const flow2 of flows) {
|
|
5681
|
+
if (flow2.transformations.length < options.pipeableMinArgCount) {
|
|
5682
|
+
continue;
|
|
5683
|
+
}
|
|
5684
|
+
const finalType = flow2.transformations[flow2.transformations.length - 1].outType;
|
|
5685
|
+
if (!finalType) {
|
|
5686
|
+
continue;
|
|
5687
|
+
}
|
|
5688
|
+
const callSigs = typeChecker.getSignaturesOfType(finalType, ts.SignatureKind.Call);
|
|
5689
|
+
if (callSigs.length > 0) {
|
|
5690
|
+
continue;
|
|
5691
|
+
}
|
|
5692
|
+
let firstPipeableIndex = -1;
|
|
5693
|
+
const subjectType = flow2.subject.outType;
|
|
5694
|
+
if (!subjectType) {
|
|
5695
|
+
continue;
|
|
5696
|
+
}
|
|
5697
|
+
const subjectIsPipeable = yield* pipe(
|
|
5698
|
+
typeParser.pipeableType(subjectType, flow2.subject.node),
|
|
5699
|
+
option
|
|
5700
|
+
);
|
|
5701
|
+
if (subjectIsPipeable._tag === "Some") {
|
|
5702
|
+
firstPipeableIndex = 0;
|
|
5703
|
+
} else {
|
|
5704
|
+
for (let i = 0; i < flow2.transformations.length; i++) {
|
|
5705
|
+
const t = flow2.transformations[i];
|
|
5706
|
+
if (t.outType) {
|
|
5707
|
+
const isPipeable = yield* pipe(
|
|
5708
|
+
typeParser.pipeableType(t.outType, flow2.node),
|
|
5709
|
+
option
|
|
5710
|
+
);
|
|
5711
|
+
if (isPipeable._tag === "Some") {
|
|
5712
|
+
firstPipeableIndex = i + 1;
|
|
5713
|
+
break;
|
|
5131
5714
|
}
|
|
5132
5715
|
}
|
|
5133
5716
|
}
|
|
5134
|
-
} else if (callChainNodes.has(node) && ts.isExpression(node)) {
|
|
5135
|
-
const parentChain = (callChainNodes.get(node) || []).slice();
|
|
5136
|
-
const originalParentChain = parentChain.slice();
|
|
5137
|
-
while (parentChain.length > options.pipeableMinArgCount) {
|
|
5138
|
-
const subject = parentChain.pop();
|
|
5139
|
-
const resultType = typeCheckerUtils.getTypeAtLocation(subject);
|
|
5140
|
-
if (!resultType) continue;
|
|
5141
|
-
const pipeableType = yield* pipe(typeParser.pipeableType(resultType, subject), orElse2(() => void_));
|
|
5142
|
-
if (pipeableType) {
|
|
5143
|
-
report({
|
|
5144
|
-
location: parentChain[0],
|
|
5145
|
-
messageText: `Nested function calls can be converted to pipeable style for better readability.`,
|
|
5146
|
-
fixes: [{
|
|
5147
|
-
fixName: "missedPipeableOpportunity_fix",
|
|
5148
|
-
description: "Convert to pipe style",
|
|
5149
|
-
apply: gen(function* () {
|
|
5150
|
-
const changeTracker = yield* service(ChangeTracker);
|
|
5151
|
-
changeTracker.replaceNode(
|
|
5152
|
-
sourceFile,
|
|
5153
|
-
parentChain[0],
|
|
5154
|
-
ts.factory.createCallExpression(
|
|
5155
|
-
ts.factory.createPropertyAccessExpression(
|
|
5156
|
-
subject,
|
|
5157
|
-
"pipe"
|
|
5158
|
-
),
|
|
5159
|
-
void 0,
|
|
5160
|
-
pipe(
|
|
5161
|
-
parentChain,
|
|
5162
|
-
filter(ts.isCallExpression),
|
|
5163
|
-
map3((call) => call.expression),
|
|
5164
|
-
reverse
|
|
5165
|
-
)
|
|
5166
|
-
)
|
|
5167
|
-
);
|
|
5168
|
-
})
|
|
5169
|
-
}]
|
|
5170
|
-
});
|
|
5171
|
-
originalParentChain.forEach((node2) => callChainNodes.delete(node2));
|
|
5172
|
-
break;
|
|
5173
|
-
}
|
|
5174
|
-
}
|
|
5175
5717
|
}
|
|
5176
|
-
|
|
5718
|
+
if (firstPipeableIndex === -1) {
|
|
5719
|
+
continue;
|
|
5720
|
+
}
|
|
5721
|
+
const transformationsAfterPipeable = flow2.transformations.slice(firstPipeableIndex);
|
|
5722
|
+
const callKindCount = transformationsAfterPipeable.filter((t) => t.kind === "call").length;
|
|
5723
|
+
if (callKindCount < options.pipeableMinArgCount) {
|
|
5724
|
+
continue;
|
|
5725
|
+
}
|
|
5726
|
+
const pipeableSubjectNode = firstPipeableIndex === 0 ? flow2.subject.node : typeParser.reconstructPipingFlow({
|
|
5727
|
+
subject: flow2.subject,
|
|
5728
|
+
transformations: flow2.transformations.slice(0, firstPipeableIndex)
|
|
5729
|
+
});
|
|
5730
|
+
const pipeableTransformations = flow2.transformations.slice(firstPipeableIndex);
|
|
5731
|
+
report({
|
|
5732
|
+
location: flow2.node,
|
|
5733
|
+
messageText: `Nested function calls can be converted to pipeable style for better readability.`,
|
|
5734
|
+
fixes: [{
|
|
5735
|
+
fixName: "missedPipeableOpportunity_fix",
|
|
5736
|
+
description: "Convert to pipe style",
|
|
5737
|
+
apply: gen(function* () {
|
|
5738
|
+
const changeTracker = yield* service(ChangeTracker);
|
|
5739
|
+
const pipeArgs = pipeableTransformations.map((t) => {
|
|
5740
|
+
if (t.args) {
|
|
5741
|
+
return ts.factory.createCallExpression(
|
|
5742
|
+
t.callee,
|
|
5743
|
+
void 0,
|
|
5744
|
+
t.args
|
|
5745
|
+
);
|
|
5746
|
+
} else {
|
|
5747
|
+
return t.callee;
|
|
5748
|
+
}
|
|
5749
|
+
});
|
|
5750
|
+
const newNode = ts.factory.createCallExpression(
|
|
5751
|
+
ts.factory.createPropertyAccessExpression(
|
|
5752
|
+
pipeableSubjectNode,
|
|
5753
|
+
"pipe"
|
|
5754
|
+
),
|
|
5755
|
+
void 0,
|
|
5756
|
+
pipeArgs
|
|
5757
|
+
);
|
|
5758
|
+
changeTracker.replaceNode(sourceFile, flow2.node, newNode);
|
|
5759
|
+
})
|
|
5760
|
+
}]
|
|
5761
|
+
});
|
|
5177
5762
|
}
|
|
5178
5763
|
})
|
|
5179
5764
|
});
|
|
@@ -5672,28 +6257,34 @@ var multipleEffectProvide = createDiagnostic({
|
|
|
5672
6257
|
"effect",
|
|
5673
6258
|
"Layer"
|
|
5674
6259
|
) || "Layer";
|
|
5675
|
-
const
|
|
5676
|
-
|
|
5677
|
-
const layer = node.arguments[0];
|
|
5678
|
-
const type = typeCheckerUtils.getTypeAtLocation(layer);
|
|
5679
|
-
if (!type) return void_;
|
|
5680
|
-
return pipe(
|
|
5681
|
-
typeParser.isNodeReferenceToEffectModuleApi("provide")(node.expression),
|
|
5682
|
-
flatMap2(() => typeParser.layerType(type, layer)),
|
|
5683
|
-
map4(() => ({ layer, node })),
|
|
5684
|
-
orElse2(() => void_)
|
|
5685
|
-
);
|
|
5686
|
-
}
|
|
5687
|
-
return void_;
|
|
5688
|
-
};
|
|
5689
|
-
const parsePipeCall = (node) => gen(function* () {
|
|
5690
|
-
const { args: args2 } = yield* typeParser.pipeCall(node);
|
|
6260
|
+
const flows = yield* typeParser.pipingFlows(true)(sourceFile);
|
|
6261
|
+
for (const flow2 of flows) {
|
|
5691
6262
|
let currentChunk = 0;
|
|
5692
6263
|
const previousLayers = [[]];
|
|
5693
|
-
for (const
|
|
5694
|
-
|
|
5695
|
-
|
|
5696
|
-
previousLayers
|
|
6264
|
+
for (const transformation of flow2.transformations) {
|
|
6265
|
+
if (!transformation.args || transformation.args.length === 0) {
|
|
6266
|
+
currentChunk++;
|
|
6267
|
+
previousLayers.push([]);
|
|
6268
|
+
continue;
|
|
6269
|
+
}
|
|
6270
|
+
const isProvideCall = yield* pipe(
|
|
6271
|
+
typeParser.isNodeReferenceToEffectModuleApi("provide")(transformation.callee),
|
|
6272
|
+
option
|
|
6273
|
+
);
|
|
6274
|
+
if (isSome2(isProvideCall)) {
|
|
6275
|
+
const layer = transformation.args[0];
|
|
6276
|
+
const type = typeCheckerUtils.getTypeAtLocation(layer);
|
|
6277
|
+
const node = ts.findAncestor(transformation.callee, ts.isCallExpression);
|
|
6278
|
+
const isLayerType = type ? yield* pipe(
|
|
6279
|
+
typeParser.layerType(type, layer),
|
|
6280
|
+
option
|
|
6281
|
+
) : none2();
|
|
6282
|
+
if (isSome2(isLayerType) && node) {
|
|
6283
|
+
previousLayers[currentChunk].push({ layer, node });
|
|
6284
|
+
} else {
|
|
6285
|
+
currentChunk++;
|
|
6286
|
+
previousLayers.push([]);
|
|
6287
|
+
}
|
|
5697
6288
|
} else {
|
|
5698
6289
|
currentChunk++;
|
|
5699
6290
|
previousLayers.push([]);
|
|
@@ -5733,19 +6324,6 @@ var multipleEffectProvide = createDiagnostic({
|
|
|
5733
6324
|
}]
|
|
5734
6325
|
});
|
|
5735
6326
|
}
|
|
5736
|
-
});
|
|
5737
|
-
const nodeToVisit = [];
|
|
5738
|
-
const appendNodeToVisit = (node) => {
|
|
5739
|
-
nodeToVisit.push(node);
|
|
5740
|
-
return void 0;
|
|
5741
|
-
};
|
|
5742
|
-
ts.forEachChild(sourceFile, appendNodeToVisit);
|
|
5743
|
-
while (nodeToVisit.length > 0) {
|
|
5744
|
-
const node = nodeToVisit.shift();
|
|
5745
|
-
ts.forEachChild(node, appendNodeToVisit);
|
|
5746
|
-
if (ts.isCallExpression(node)) {
|
|
5747
|
-
yield* pipe(parsePipeCall(node), ignore);
|
|
5748
|
-
}
|
|
5749
6327
|
}
|
|
5750
6328
|
})
|
|
5751
6329
|
});
|
|
@@ -7059,6 +7637,56 @@ var overriddenSchemaConstructor = createDiagnostic({
|
|
|
7059
7637
|
})
|
|
7060
7638
|
});
|
|
7061
7639
|
|
|
7640
|
+
// src/diagnostics/redundantSchemaTagIdentifier.ts
|
|
7641
|
+
var redundantSchemaTagIdentifier = createDiagnostic({
|
|
7642
|
+
name: "redundantSchemaTagIdentifier",
|
|
7643
|
+
code: 42,
|
|
7644
|
+
description: "Suggests removing redundant identifier argument when it equals the tag value in Schema.TaggedClass/TaggedError/TaggedRequest",
|
|
7645
|
+
severity: "suggestion",
|
|
7646
|
+
apply: fn("redundantSchemaTagIdentifier.apply")(function* (sourceFile, report) {
|
|
7647
|
+
const ts = yield* service(TypeScriptApi);
|
|
7648
|
+
const typeParser = yield* service(TypeParser);
|
|
7649
|
+
const nodeToVisit = [];
|
|
7650
|
+
const appendNodeToVisit = (node) => {
|
|
7651
|
+
nodeToVisit.push(node);
|
|
7652
|
+
return void 0;
|
|
7653
|
+
};
|
|
7654
|
+
ts.forEachChild(sourceFile, appendNodeToVisit);
|
|
7655
|
+
while (nodeToVisit.length > 0) {
|
|
7656
|
+
const node = nodeToVisit.shift();
|
|
7657
|
+
if (ts.isClassDeclaration(node) && node.name && node.heritageClauses) {
|
|
7658
|
+
const result = yield* pipe(
|
|
7659
|
+
typeParser.extendsSchemaTaggedClass(node),
|
|
7660
|
+
orElse2(() => typeParser.extendsSchemaTaggedError(node)),
|
|
7661
|
+
orElse2(() => typeParser.extendsSchemaTaggedRequest(node)),
|
|
7662
|
+
orElse2(() => void_)
|
|
7663
|
+
);
|
|
7664
|
+
if (result && result.keyStringLiteral && result.tagStringLiteral) {
|
|
7665
|
+
const { keyStringLiteral, tagStringLiteral } = result;
|
|
7666
|
+
if (keyStringLiteral.text === tagStringLiteral.text) {
|
|
7667
|
+
report({
|
|
7668
|
+
location: keyStringLiteral,
|
|
7669
|
+
messageText: `Identifier '${keyStringLiteral.text}' is redundant since it equals the _tag value`,
|
|
7670
|
+
fixes: [{
|
|
7671
|
+
fixName: "redundantSchemaTagIdentifier_removeIdentifier",
|
|
7672
|
+
description: `Remove redundant identifier '${keyStringLiteral.text}'`,
|
|
7673
|
+
apply: gen(function* () {
|
|
7674
|
+
const changeTracker = yield* service(ChangeTracker);
|
|
7675
|
+
changeTracker.deleteRange(sourceFile, {
|
|
7676
|
+
pos: ts.getTokenPosOfNode(keyStringLiteral, sourceFile),
|
|
7677
|
+
end: keyStringLiteral.end
|
|
7678
|
+
});
|
|
7679
|
+
})
|
|
7680
|
+
}]
|
|
7681
|
+
});
|
|
7682
|
+
}
|
|
7683
|
+
}
|
|
7684
|
+
}
|
|
7685
|
+
ts.forEachChild(node, appendNodeToVisit);
|
|
7686
|
+
}
|
|
7687
|
+
})
|
|
7688
|
+
});
|
|
7689
|
+
|
|
7062
7690
|
// src/diagnostics/returnEffectInGen.ts
|
|
7063
7691
|
var returnEffectInGen = createDiagnostic({
|
|
7064
7692
|
name: "returnEffectInGen",
|
|
@@ -8059,7 +8687,10 @@ var diagnostics = [
|
|
|
8059
8687
|
schemaStructWithTag,
|
|
8060
8688
|
globalErrorInEffectCatch,
|
|
8061
8689
|
globalErrorInEffectFailure,
|
|
8062
|
-
layerMergeAllWithDependencies
|
|
8690
|
+
layerMergeAllWithDependencies,
|
|
8691
|
+
effectMapVoid,
|
|
8692
|
+
effectFnOpportunity,
|
|
8693
|
+
redundantSchemaTagIdentifier
|
|
8063
8694
|
];
|
|
8064
8695
|
|
|
8065
8696
|
// src/effect-lsp-patch-utils.ts
|
|
@@ -8106,12 +8737,12 @@ function checkSourceFileWorker(tsInstance, program, sourceFile, compilerOptions,
|
|
|
8106
8737
|
map((_) => _.diagnostics),
|
|
8107
8738
|
map(
|
|
8108
8739
|
filter(
|
|
8109
|
-
(_) => _.category === tsInstance.DiagnosticCategory.Error || _.category === tsInstance.DiagnosticCategory.Warning || moduleName === "tsc" && parsedOptions.
|
|
8740
|
+
(_) => _.category === tsInstance.DiagnosticCategory.Error || _.category === tsInstance.DiagnosticCategory.Warning || moduleName === "tsc" && parsedOptions.includeSuggestionsInTsc && (_.category === tsInstance.DiagnosticCategory.Message || _.category === tsInstance.DiagnosticCategory.Suggestion)
|
|
8110
8741
|
)
|
|
8111
8742
|
),
|
|
8112
8743
|
map(
|
|
8113
8744
|
map3((_) => {
|
|
8114
|
-
if (moduleName === "tsc" && parsedOptions.
|
|
8745
|
+
if (moduleName === "tsc" && parsedOptions.includeSuggestionsInTsc && _.category === tsInstance.DiagnosticCategory.Suggestion) {
|
|
8115
8746
|
return { ..._, category: tsInstance.DiagnosticCategory.Message };
|
|
8116
8747
|
}
|
|
8117
8748
|
return _;
|