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