@effect/language-service 0.83.1 → 0.84.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -2
- package/cli.js +587 -61
- package/cli.js.map +1 -1
- package/effect-lsp-patch-utils.js +388 -55
- package/effect-lsp-patch-utils.js.map +1 -1
- package/index.js +388 -55
- package/index.js.map +1 -1
- package/package.json +1 -1
- package/schema.json +110 -2
- package/transform.js +388 -55
- package/transform.js.map +1 -1
package/index.js
CHANGED
|
@@ -3775,6 +3775,25 @@ function makeTypeCheckerUtils(ts, typeChecker, tsUtils) {
|
|
|
3775
3775
|
return typeChecker.getTypeAtLocation(node);
|
|
3776
3776
|
}
|
|
3777
3777
|
}
|
|
3778
|
+
function resolveToGlobalSymbol(symbol3) {
|
|
3779
|
+
if (symbol3.flags & ts.SymbolFlags.Alias) {
|
|
3780
|
+
symbol3 = typeChecker.getAliasedSymbol(symbol3);
|
|
3781
|
+
}
|
|
3782
|
+
let depth = 0;
|
|
3783
|
+
while (depth < 2 && symbol3.valueDeclaration && ts.isVariableDeclaration(symbol3.valueDeclaration)) {
|
|
3784
|
+
const initializer = symbol3.valueDeclaration.initializer;
|
|
3785
|
+
if (!initializer) break;
|
|
3786
|
+
let nextSymbol = typeChecker.getSymbolAtLocation(initializer);
|
|
3787
|
+
if (!nextSymbol) break;
|
|
3788
|
+
if (nextSymbol.flags & ts.SymbolFlags.Alias) {
|
|
3789
|
+
nextSymbol = typeChecker.getAliasedSymbol(nextSymbol);
|
|
3790
|
+
}
|
|
3791
|
+
if (nextSymbol === symbol3) break;
|
|
3792
|
+
symbol3 = nextSymbol;
|
|
3793
|
+
depth++;
|
|
3794
|
+
}
|
|
3795
|
+
return symbol3;
|
|
3796
|
+
}
|
|
3778
3797
|
return {
|
|
3779
3798
|
isUnion,
|
|
3780
3799
|
isReadonlyArrayType,
|
|
@@ -3788,7 +3807,8 @@ function makeTypeCheckerUtils(ts, typeChecker, tsUtils) {
|
|
|
3788
3807
|
expectedAndRealType,
|
|
3789
3808
|
typeToSimplifiedTypeNode,
|
|
3790
3809
|
isGlobalErrorType,
|
|
3791
|
-
getTypeAtLocation
|
|
3810
|
+
getTypeAtLocation,
|
|
3811
|
+
resolveToGlobalSymbol
|
|
3792
3812
|
};
|
|
3793
3813
|
}
|
|
3794
3814
|
|
|
@@ -4476,7 +4496,11 @@ function make3(ts, tsUtils, typeChecker, typeCheckerUtils, program) {
|
|
|
4476
4496
|
}
|
|
4477
4497
|
currentParent = nodeToCheck.parent;
|
|
4478
4498
|
}
|
|
4479
|
-
return {
|
|
4499
|
+
return {
|
|
4500
|
+
inEffect: effectGenResult !== void 0 && effectGenResult.body.statements.length > 0 && scopeNode === effectGenResult.generatorFunction,
|
|
4501
|
+
scopeNode,
|
|
4502
|
+
effectGen: effectGenResult
|
|
4503
|
+
};
|
|
4480
4504
|
});
|
|
4481
4505
|
const effectFn = cachedBy(
|
|
4482
4506
|
function(node) {
|
|
@@ -7727,12 +7751,14 @@ var getParameterName = (typescript, name) => {
|
|
|
7727
7751
|
}
|
|
7728
7752
|
return "parameter";
|
|
7729
7753
|
};
|
|
7730
|
-
var hasOuterContextualFunctionType = (typescript, typeChecker, node) => {
|
|
7754
|
+
var hasOuterContextualFunctionType = (typescript, typeChecker, typeCheckerUtils, node) => {
|
|
7731
7755
|
const contextualType = typeChecker.getContextualType(node);
|
|
7732
7756
|
if (!contextualType) {
|
|
7733
7757
|
return false;
|
|
7734
7758
|
}
|
|
7735
|
-
return
|
|
7759
|
+
return typeCheckerUtils.unrollUnionMembers(contextualType).some(
|
|
7760
|
+
(type) => typeChecker.getSignaturesOfType(type, typescript.SignatureKind.Call).length > 0
|
|
7761
|
+
);
|
|
7736
7762
|
};
|
|
7737
7763
|
var effectFnImplicitAny = createDiagnostic({
|
|
7738
7764
|
name: "effectFnImplicitAny",
|
|
@@ -7746,6 +7772,7 @@ var effectFnImplicitAny = createDiagnostic({
|
|
|
7746
7772
|
const ts = yield* service(TypeScriptApi);
|
|
7747
7773
|
const program = yield* service(TypeScriptProgram);
|
|
7748
7774
|
const typeChecker = yield* service(TypeCheckerApi);
|
|
7775
|
+
const typeCheckerUtils = yield* service(TypeCheckerUtils);
|
|
7749
7776
|
const typeParser = yield* service(TypeParser);
|
|
7750
7777
|
const noImplicitAny = program.getCompilerOptions().noImplicitAny ?? program.getCompilerOptions().strict ?? false;
|
|
7751
7778
|
if (!noImplicitAny) {
|
|
@@ -7785,7 +7812,7 @@ var effectFnImplicitAny = createDiagnostic({
|
|
|
7785
7812
|
),
|
|
7786
7813
|
orUndefined
|
|
7787
7814
|
);
|
|
7788
|
-
if (!parsed || hasOuterContextualFunctionType(ts, typeChecker, parsed.call)) {
|
|
7815
|
+
if (!parsed || hasOuterContextualFunctionType(ts, typeChecker, typeCheckerUtils, parsed.call)) {
|
|
7789
7816
|
continue;
|
|
7790
7817
|
}
|
|
7791
7818
|
for (const parameter of parsed.fn.parameters) {
|
|
@@ -8836,6 +8863,128 @@ var genericEffectServices = createDiagnostic({
|
|
|
8836
8863
|
})
|
|
8837
8864
|
});
|
|
8838
8865
|
|
|
8866
|
+
// src/diagnostics/globalConsoleInEffect.ts
|
|
8867
|
+
var consoleMethodAlternatives = {
|
|
8868
|
+
"log": "Effect.log or Logger",
|
|
8869
|
+
"warn": "Effect.logWarning or Logger",
|
|
8870
|
+
"error": "Effect.logError or Logger",
|
|
8871
|
+
"info": "Effect.logInfo or Logger",
|
|
8872
|
+
"debug": "Effect.logDebug or Logger",
|
|
8873
|
+
"trace": "Effect.logTrace or Logger"
|
|
8874
|
+
};
|
|
8875
|
+
var makeGlobalConsoleApply = (checkInEffect) => fn(`globalConsole${checkInEffect ? "InEffect" : ""}.apply`)(function* (sourceFile, report) {
|
|
8876
|
+
const ts = yield* service(TypeScriptApi);
|
|
8877
|
+
const typeChecker = yield* service(TypeCheckerApi);
|
|
8878
|
+
const typeCheckerUtils = yield* service(TypeCheckerUtils);
|
|
8879
|
+
const typeParser = yield* service(TypeParser);
|
|
8880
|
+
const consoleSymbol = typeChecker.resolveName("console", void 0, ts.SymbolFlags.Value, false);
|
|
8881
|
+
if (!consoleSymbol) return;
|
|
8882
|
+
const nodeToVisit = [];
|
|
8883
|
+
const appendNodeToVisit = (node) => {
|
|
8884
|
+
nodeToVisit.push(node);
|
|
8885
|
+
return void 0;
|
|
8886
|
+
};
|
|
8887
|
+
ts.forEachChild(sourceFile, appendNodeToVisit);
|
|
8888
|
+
while (nodeToVisit.length > 0) {
|
|
8889
|
+
const node = nodeToVisit.shift();
|
|
8890
|
+
ts.forEachChild(node, appendNodeToVisit);
|
|
8891
|
+
if (!ts.isCallExpression(node) || !ts.isPropertyAccessExpression(node.expression)) continue;
|
|
8892
|
+
const method = ts.idText(node.expression.name);
|
|
8893
|
+
const alternative = consoleMethodAlternatives[method];
|
|
8894
|
+
if (!alternative) continue;
|
|
8895
|
+
const symbol3 = typeChecker.getSymbolAtLocation(node.expression.expression);
|
|
8896
|
+
if (!symbol3) continue;
|
|
8897
|
+
if (typeCheckerUtils.resolveToGlobalSymbol(symbol3) !== consoleSymbol) continue;
|
|
8898
|
+
const { inEffect } = yield* typeParser.findEnclosingScopes(node);
|
|
8899
|
+
if (inEffect !== checkInEffect) continue;
|
|
8900
|
+
report({
|
|
8901
|
+
location: node,
|
|
8902
|
+
messageText: checkInEffect ? `Prefer using ${alternative} instead of console.${method} inside Effect generators.` : `Prefer using ${alternative} instead of console.${method}.`,
|
|
8903
|
+
fixes: []
|
|
8904
|
+
});
|
|
8905
|
+
}
|
|
8906
|
+
});
|
|
8907
|
+
var globalConsoleInEffect = createDiagnostic({
|
|
8908
|
+
name: "globalConsoleInEffect",
|
|
8909
|
+
code: 56,
|
|
8910
|
+
description: "Warns when using console methods inside Effect generators instead of Effect.log/Logger",
|
|
8911
|
+
group: "effectNative",
|
|
8912
|
+
severity: "off",
|
|
8913
|
+
fixable: false,
|
|
8914
|
+
supportedEffect: ["v3", "v4"],
|
|
8915
|
+
apply: makeGlobalConsoleApply(true)
|
|
8916
|
+
});
|
|
8917
|
+
|
|
8918
|
+
// src/diagnostics/globalConsole.ts
|
|
8919
|
+
var globalConsole = createDiagnostic({
|
|
8920
|
+
name: "globalConsole",
|
|
8921
|
+
code: 60,
|
|
8922
|
+
description: "Warns when using console methods outside Effect generators instead of Effect.log/Logger",
|
|
8923
|
+
group: "effectNative",
|
|
8924
|
+
severity: "off",
|
|
8925
|
+
fixable: false,
|
|
8926
|
+
supportedEffect: ["v3", "v4"],
|
|
8927
|
+
apply: makeGlobalConsoleApply(false)
|
|
8928
|
+
});
|
|
8929
|
+
|
|
8930
|
+
// src/diagnostics/globalDateInEffect.ts
|
|
8931
|
+
var makeGlobalDateApply = (checkInEffect) => fn(`globalDate${checkInEffect ? "InEffect" : ""}.apply`)(function* (sourceFile, report) {
|
|
8932
|
+
const ts = yield* service(TypeScriptApi);
|
|
8933
|
+
const typeChecker = yield* service(TypeCheckerApi);
|
|
8934
|
+
const typeCheckerUtils = yield* service(TypeCheckerUtils);
|
|
8935
|
+
const typeParser = yield* service(TypeParser);
|
|
8936
|
+
const dateSymbol = typeChecker.resolveName("Date", void 0, ts.SymbolFlags.Value, false);
|
|
8937
|
+
if (!dateSymbol) return;
|
|
8938
|
+
const nodeToVisit = [];
|
|
8939
|
+
const appendNodeToVisit = (node) => {
|
|
8940
|
+
nodeToVisit.push(node);
|
|
8941
|
+
return void 0;
|
|
8942
|
+
};
|
|
8943
|
+
ts.forEachChild(sourceFile, appendNodeToVisit);
|
|
8944
|
+
while (nodeToVisit.length > 0) {
|
|
8945
|
+
const node = nodeToVisit.shift();
|
|
8946
|
+
ts.forEachChild(node, appendNodeToVisit);
|
|
8947
|
+
let messageText;
|
|
8948
|
+
let objectNode;
|
|
8949
|
+
if (ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression) && ts.idText(node.expression.name) === "now") {
|
|
8950
|
+
objectNode = node.expression.expression;
|
|
8951
|
+
messageText = checkInEffect ? "Prefer using Clock or DateTime from Effect instead of Date.now() inside Effect generators." : "Prefer using Clock or DateTime from Effect instead of Date.now().";
|
|
8952
|
+
} else if (ts.isNewExpression(node)) {
|
|
8953
|
+
objectNode = node.expression;
|
|
8954
|
+
messageText = checkInEffect ? "Prefer using DateTime from Effect instead of new Date() inside Effect generators." : "Prefer using DateTime from Effect instead of new Date().";
|
|
8955
|
+
}
|
|
8956
|
+
if (!messageText || !objectNode) continue;
|
|
8957
|
+
const symbol3 = typeChecker.getSymbolAtLocation(objectNode);
|
|
8958
|
+
if (!symbol3) continue;
|
|
8959
|
+
if (typeCheckerUtils.resolveToGlobalSymbol(symbol3) !== dateSymbol) continue;
|
|
8960
|
+
const { inEffect } = yield* typeParser.findEnclosingScopes(node);
|
|
8961
|
+
if (inEffect !== checkInEffect) continue;
|
|
8962
|
+
report({ location: node, messageText, fixes: [] });
|
|
8963
|
+
}
|
|
8964
|
+
});
|
|
8965
|
+
var globalDateInEffect = createDiagnostic({
|
|
8966
|
+
name: "globalDateInEffect",
|
|
8967
|
+
code: 55,
|
|
8968
|
+
description: "Warns when using Date.now() or new Date() inside Effect generators instead of Clock/DateTime",
|
|
8969
|
+
group: "effectNative",
|
|
8970
|
+
severity: "off",
|
|
8971
|
+
fixable: false,
|
|
8972
|
+
supportedEffect: ["v3", "v4"],
|
|
8973
|
+
apply: makeGlobalDateApply(true)
|
|
8974
|
+
});
|
|
8975
|
+
|
|
8976
|
+
// src/diagnostics/globalDate.ts
|
|
8977
|
+
var globalDate = createDiagnostic({
|
|
8978
|
+
name: "globalDate",
|
|
8979
|
+
code: 59,
|
|
8980
|
+
description: "Warns when using Date.now() or new Date() outside Effect generators instead of Clock/DateTime",
|
|
8981
|
+
group: "effectNative",
|
|
8982
|
+
severity: "off",
|
|
8983
|
+
fixable: false,
|
|
8984
|
+
supportedEffect: ["v3", "v4"],
|
|
8985
|
+
apply: makeGlobalDateApply(false)
|
|
8986
|
+
});
|
|
8987
|
+
|
|
8839
8988
|
// src/diagnostics/globalErrorInEffectCatch.ts
|
|
8840
8989
|
var globalErrorInEffectCatch = createDiagnostic({
|
|
8841
8990
|
name: "globalErrorInEffectCatch",
|
|
@@ -8955,46 +9104,186 @@ var globalErrorInEffectFailure = createDiagnostic({
|
|
|
8955
9104
|
})
|
|
8956
9105
|
});
|
|
8957
9106
|
|
|
9107
|
+
// src/diagnostics/globalFetchInEffect.ts
|
|
9108
|
+
var makeGlobalFetchApply = (checkInEffect) => fn(`globalFetch${checkInEffect ? "InEffect" : ""}.apply`)(function* (sourceFile, report) {
|
|
9109
|
+
const ts = yield* service(TypeScriptApi);
|
|
9110
|
+
const typeChecker = yield* service(TypeCheckerApi);
|
|
9111
|
+
const typeCheckerUtils = yield* service(TypeCheckerUtils);
|
|
9112
|
+
const typeParser = yield* service(TypeParser);
|
|
9113
|
+
const fetchSymbol = typeChecker.resolveName("fetch", void 0, ts.SymbolFlags.Value, false);
|
|
9114
|
+
if (!fetchSymbol) return;
|
|
9115
|
+
const effectVersion = typeParser.supportedEffect();
|
|
9116
|
+
const packageName = effectVersion === "v3" ? "@effect/platform" : "effect/unstable/http";
|
|
9117
|
+
const messageText = checkInEffect ? `Prefer using HttpClient from ${packageName} instead of the global 'fetch' function inside Effect generators.` : `Prefer using HttpClient from ${packageName} instead of the global 'fetch' function.`;
|
|
9118
|
+
const nodeToVisit = [];
|
|
9119
|
+
const appendNodeToVisit = (node) => {
|
|
9120
|
+
nodeToVisit.push(node);
|
|
9121
|
+
return void 0;
|
|
9122
|
+
};
|
|
9123
|
+
ts.forEachChild(sourceFile, appendNodeToVisit);
|
|
9124
|
+
while (nodeToVisit.length > 0) {
|
|
9125
|
+
const node = nodeToVisit.shift();
|
|
9126
|
+
if (ts.isCallExpression(node)) {
|
|
9127
|
+
const symbol3 = typeChecker.getSymbolAtLocation(node.expression);
|
|
9128
|
+
if (symbol3 && typeCheckerUtils.resolveToGlobalSymbol(symbol3) === fetchSymbol) {
|
|
9129
|
+
const { inEffect } = yield* typeParser.findEnclosingScopes(node);
|
|
9130
|
+
if (inEffect === checkInEffect) {
|
|
9131
|
+
report({
|
|
9132
|
+
location: node.expression,
|
|
9133
|
+
messageText,
|
|
9134
|
+
fixes: []
|
|
9135
|
+
});
|
|
9136
|
+
}
|
|
9137
|
+
}
|
|
9138
|
+
}
|
|
9139
|
+
ts.forEachChild(node, appendNodeToVisit);
|
|
9140
|
+
}
|
|
9141
|
+
});
|
|
9142
|
+
var globalFetchInEffect = createDiagnostic({
|
|
9143
|
+
name: "globalFetchInEffect",
|
|
9144
|
+
code: 63,
|
|
9145
|
+
description: "Warns when using the global fetch function inside Effect generators instead of the Effect HTTP client",
|
|
9146
|
+
group: "effectNative",
|
|
9147
|
+
severity: "off",
|
|
9148
|
+
fixable: false,
|
|
9149
|
+
supportedEffect: ["v3", "v4"],
|
|
9150
|
+
apply: makeGlobalFetchApply(true)
|
|
9151
|
+
});
|
|
9152
|
+
|
|
8958
9153
|
// src/diagnostics/globalFetch.ts
|
|
8959
9154
|
var globalFetch = createDiagnostic({
|
|
8960
9155
|
name: "globalFetch",
|
|
8961
9156
|
code: 53,
|
|
8962
|
-
description: "Warns when using the global fetch function instead of the Effect HTTP client",
|
|
9157
|
+
description: "Warns when using the global fetch function outside Effect generators instead of the Effect HTTP client",
|
|
8963
9158
|
group: "effectNative",
|
|
8964
9159
|
severity: "off",
|
|
8965
9160
|
fixable: false,
|
|
8966
9161
|
supportedEffect: ["v3", "v4"],
|
|
8967
|
-
apply:
|
|
8968
|
-
|
|
8969
|
-
|
|
8970
|
-
|
|
8971
|
-
|
|
8972
|
-
|
|
8973
|
-
|
|
8974
|
-
|
|
8975
|
-
|
|
8976
|
-
|
|
8977
|
-
|
|
8978
|
-
|
|
8979
|
-
|
|
8980
|
-
|
|
8981
|
-
|
|
8982
|
-
|
|
8983
|
-
|
|
8984
|
-
|
|
8985
|
-
|
|
8986
|
-
|
|
8987
|
-
|
|
8988
|
-
|
|
8989
|
-
|
|
8990
|
-
|
|
8991
|
-
|
|
8992
|
-
|
|
8993
|
-
|
|
9162
|
+
apply: makeGlobalFetchApply(false)
|
|
9163
|
+
});
|
|
9164
|
+
|
|
9165
|
+
// src/diagnostics/globalRandomInEffect.ts
|
|
9166
|
+
var makeGlobalRandomApply = (checkInEffect) => fn(`globalRandom${checkInEffect ? "InEffect" : ""}.apply`)(function* (sourceFile, report) {
|
|
9167
|
+
const ts = yield* service(TypeScriptApi);
|
|
9168
|
+
const typeChecker = yield* service(TypeCheckerApi);
|
|
9169
|
+
const typeCheckerUtils = yield* service(TypeCheckerUtils);
|
|
9170
|
+
const typeParser = yield* service(TypeParser);
|
|
9171
|
+
const mathSymbol = typeChecker.resolveName("Math", void 0, ts.SymbolFlags.Value, false);
|
|
9172
|
+
if (!mathSymbol) return;
|
|
9173
|
+
const nodeToVisit = [];
|
|
9174
|
+
const appendNodeToVisit = (node) => {
|
|
9175
|
+
nodeToVisit.push(node);
|
|
9176
|
+
return void 0;
|
|
9177
|
+
};
|
|
9178
|
+
ts.forEachChild(sourceFile, appendNodeToVisit);
|
|
9179
|
+
while (nodeToVisit.length > 0) {
|
|
9180
|
+
const node = nodeToVisit.shift();
|
|
9181
|
+
ts.forEachChild(node, appendNodeToVisit);
|
|
9182
|
+
if (!ts.isCallExpression(node) || !ts.isPropertyAccessExpression(node.expression) || ts.idText(node.expression.name) !== "random") continue;
|
|
9183
|
+
const symbol3 = typeChecker.getSymbolAtLocation(node.expression.expression);
|
|
9184
|
+
if (!symbol3) continue;
|
|
9185
|
+
if (typeCheckerUtils.resolveToGlobalSymbol(symbol3) !== mathSymbol) continue;
|
|
9186
|
+
const { inEffect } = yield* typeParser.findEnclosingScopes(node);
|
|
9187
|
+
if (inEffect !== checkInEffect) continue;
|
|
9188
|
+
report({
|
|
9189
|
+
location: node,
|
|
9190
|
+
messageText: checkInEffect ? "Prefer using the Random service from Effect instead of Math.random() inside Effect generators." : "Prefer using the Random service from Effect instead of Math.random().",
|
|
9191
|
+
fixes: []
|
|
9192
|
+
});
|
|
9193
|
+
}
|
|
9194
|
+
});
|
|
9195
|
+
var globalRandomInEffect = createDiagnostic({
|
|
9196
|
+
name: "globalRandomInEffect",
|
|
9197
|
+
code: 57,
|
|
9198
|
+
description: "Warns when using Math.random() inside Effect generators instead of the Random service",
|
|
9199
|
+
group: "effectNative",
|
|
9200
|
+
severity: "off",
|
|
9201
|
+
fixable: false,
|
|
9202
|
+
supportedEffect: ["v3", "v4"],
|
|
9203
|
+
apply: makeGlobalRandomApply(true)
|
|
9204
|
+
});
|
|
9205
|
+
|
|
9206
|
+
// src/diagnostics/globalRandom.ts
|
|
9207
|
+
var globalRandom = createDiagnostic({
|
|
9208
|
+
name: "globalRandom",
|
|
9209
|
+
code: 61,
|
|
9210
|
+
description: "Warns when using Math.random() outside Effect generators instead of the Random service",
|
|
9211
|
+
group: "effectNative",
|
|
9212
|
+
severity: "off",
|
|
9213
|
+
fixable: false,
|
|
9214
|
+
supportedEffect: ["v3", "v4"],
|
|
9215
|
+
apply: makeGlobalRandomApply(false)
|
|
9216
|
+
});
|
|
9217
|
+
|
|
9218
|
+
// src/diagnostics/globalTimersInEffect.ts
|
|
9219
|
+
var timerAlternatives = {
|
|
9220
|
+
"setTimeout": {
|
|
9221
|
+
inEffect: "Prefer using Effect.sleep or Schedule from Effect instead of setTimeout inside Effect generators.",
|
|
9222
|
+
outsideEffect: "Prefer using Effect.sleep or Schedule from Effect instead of setTimeout."
|
|
9223
|
+
},
|
|
9224
|
+
"setInterval": {
|
|
9225
|
+
inEffect: "Prefer using Schedule or Effect.repeat from Effect instead of setInterval inside Effect generators.",
|
|
9226
|
+
outsideEffect: "Prefer using Schedule or Effect.repeat from Effect instead of setInterval."
|
|
9227
|
+
}
|
|
9228
|
+
};
|
|
9229
|
+
var makeGlobalTimersApply = (checkInEffect) => fn(`globalTimers${checkInEffect ? "InEffect" : ""}.apply`)(function* (sourceFile, report) {
|
|
9230
|
+
const ts = yield* service(TypeScriptApi);
|
|
9231
|
+
const typeChecker = yield* service(TypeCheckerApi);
|
|
9232
|
+
const typeCheckerUtils = yield* service(TypeCheckerUtils);
|
|
9233
|
+
const typeParser = yield* service(TypeParser);
|
|
9234
|
+
const globalSymbols = /* @__PURE__ */ new Map();
|
|
9235
|
+
for (const name of Object.keys(timerAlternatives)) {
|
|
9236
|
+
const symbol3 = typeChecker.resolveName(name, void 0, ts.SymbolFlags.Value, false);
|
|
9237
|
+
if (symbol3) globalSymbols.set(name, symbol3);
|
|
9238
|
+
}
|
|
9239
|
+
if (globalSymbols.size === 0) return;
|
|
9240
|
+
const nodeToVisit = [];
|
|
9241
|
+
const appendNodeToVisit = (node) => {
|
|
9242
|
+
nodeToVisit.push(node);
|
|
9243
|
+
return void 0;
|
|
9244
|
+
};
|
|
9245
|
+
ts.forEachChild(sourceFile, appendNodeToVisit);
|
|
9246
|
+
while (nodeToVisit.length > 0) {
|
|
9247
|
+
const node = nodeToVisit.shift();
|
|
9248
|
+
ts.forEachChild(node, appendNodeToVisit);
|
|
9249
|
+
if (!ts.isCallExpression(node)) continue;
|
|
9250
|
+
const symbol3 = typeChecker.getSymbolAtLocation(node.expression);
|
|
9251
|
+
if (!symbol3) continue;
|
|
9252
|
+
const resolvedSymbol = typeCheckerUtils.resolveToGlobalSymbol(symbol3);
|
|
9253
|
+
let messageText;
|
|
9254
|
+
for (const [name, symbol4] of globalSymbols) {
|
|
9255
|
+
if (resolvedSymbol === symbol4) {
|
|
9256
|
+
messageText = checkInEffect ? timerAlternatives[name].inEffect : timerAlternatives[name].outsideEffect;
|
|
9257
|
+
break;
|
|
8994
9258
|
}
|
|
8995
|
-
ts.forEachChild(node, appendNodeToVisit);
|
|
8996
9259
|
}
|
|
8997
|
-
|
|
9260
|
+
if (!messageText) continue;
|
|
9261
|
+
const { inEffect } = yield* typeParser.findEnclosingScopes(node);
|
|
9262
|
+
if (inEffect !== checkInEffect) continue;
|
|
9263
|
+
report({ location: node, messageText, fixes: [] });
|
|
9264
|
+
}
|
|
9265
|
+
});
|
|
9266
|
+
var globalTimersInEffect = createDiagnostic({
|
|
9267
|
+
name: "globalTimersInEffect",
|
|
9268
|
+
code: 58,
|
|
9269
|
+
description: "Warns when using setTimeout/setInterval inside Effect generators instead of Effect.sleep/Schedule",
|
|
9270
|
+
group: "effectNative",
|
|
9271
|
+
severity: "off",
|
|
9272
|
+
fixable: false,
|
|
9273
|
+
supportedEffect: ["v3", "v4"],
|
|
9274
|
+
apply: makeGlobalTimersApply(true)
|
|
9275
|
+
});
|
|
9276
|
+
|
|
9277
|
+
// src/diagnostics/globalTimers.ts
|
|
9278
|
+
var globalTimers = createDiagnostic({
|
|
9279
|
+
name: "globalTimers",
|
|
9280
|
+
code: 62,
|
|
9281
|
+
description: "Warns when using setTimeout/setInterval outside Effect generators instead of Effect.sleep/Schedule",
|
|
9282
|
+
group: "effectNative",
|
|
9283
|
+
severity: "off",
|
|
9284
|
+
fixable: false,
|
|
9285
|
+
supportedEffect: ["v3", "v4"],
|
|
9286
|
+
apply: makeGlobalTimersApply(false)
|
|
8998
9287
|
});
|
|
8999
9288
|
|
|
9000
9289
|
// src/diagnostics/importFromBarrel.ts
|
|
@@ -10059,8 +10348,8 @@ var missingReturnYieldStar = createDiagnostic({
|
|
|
10059
10348
|
if (!type) continue;
|
|
10060
10349
|
const maybeEffect = yield* option(typeParser.effectYieldableType(type, unwrapped.expression));
|
|
10061
10350
|
if (!(isSome2(maybeEffect) && maybeEffect.value.A.flags & ts.TypeFlags.Never)) continue;
|
|
10062
|
-
const {
|
|
10063
|
-
if (!
|
|
10351
|
+
const { inEffect } = yield* typeParser.findEnclosingScopes(node);
|
|
10352
|
+
if (!inEffect) continue;
|
|
10064
10353
|
const fix = [{
|
|
10065
10354
|
fixName: "missingReturnYieldStar_fix",
|
|
10066
10355
|
description: "Add return statement",
|
|
@@ -11454,11 +11743,8 @@ var preferSchemaOverJson = createDiagnostic({
|
|
|
11454
11743
|
});
|
|
11455
11744
|
const jsonMethodInEffectGen = fn("preferSchemaOverJson.jsonMethodInEffectGen")(
|
|
11456
11745
|
function* (jsonCall) {
|
|
11457
|
-
const {
|
|
11458
|
-
if (!
|
|
11459
|
-
return yield* TypeParserIssue.issue;
|
|
11460
|
-
}
|
|
11461
|
-
if (scopeNode && scopeNode !== effectGen.generatorFunction) {
|
|
11746
|
+
const { inEffect } = yield* typeParser.findEnclosingScopes(jsonCall);
|
|
11747
|
+
if (!inEffect) {
|
|
11462
11748
|
return yield* TypeParserIssue.issue;
|
|
11463
11749
|
}
|
|
11464
11750
|
return jsonCall;
|
|
@@ -11621,16 +11907,16 @@ Nested Effect-able types may be intended if you plan to later manually flatten o
|
|
|
11621
11907
|
var runEffectInsideEffect = createDiagnostic({
|
|
11622
11908
|
name: "runEffectInsideEffect",
|
|
11623
11909
|
code: 32,
|
|
11624
|
-
description: "Suggests using Runtime methods instead of Effect.run* inside Effect contexts",
|
|
11910
|
+
description: "Suggests using Runtime or Effect.run*With methods instead of Effect.run* inside Effect contexts",
|
|
11625
11911
|
group: "antipattern",
|
|
11626
11912
|
severity: "suggestion",
|
|
11627
11913
|
fixable: true,
|
|
11628
|
-
supportedEffect: ["v3"],
|
|
11914
|
+
supportedEffect: ["v3", "v4"],
|
|
11629
11915
|
apply: fn("runEffectInsideEffect.apply")(function* (sourceFile, report) {
|
|
11630
11916
|
const ts = yield* service(TypeScriptApi);
|
|
11631
11917
|
const typeParser = yield* service(TypeParser);
|
|
11632
11918
|
const tsUtils = yield* service(TypeScriptUtils);
|
|
11633
|
-
|
|
11919
|
+
const supportedEffect = typeParser.supportedEffect();
|
|
11634
11920
|
const parseEffectMethod = (node, methodName) => pipe(
|
|
11635
11921
|
typeParser.isNodeReferenceToEffectModuleApi(methodName)(node),
|
|
11636
11922
|
map5(() => ({ node, methodName }))
|
|
@@ -11663,9 +11949,10 @@ var runEffectInsideEffect = createDiagnostic({
|
|
|
11663
11949
|
if (scopeNode && scopeNode !== effectGen.generatorFunction) {
|
|
11664
11950
|
const fixAddRuntime = gen(function* () {
|
|
11665
11951
|
const changeTracker = yield* service(ChangeTracker);
|
|
11666
|
-
const runtimeModuleIdentifier = tsUtils.findImportedModuleIdentifierByPackageAndNameOrBarrel(sourceFile, "effect", "Runtime") || "Runtime";
|
|
11667
11952
|
const effectModuleIdentifier = tsUtils.findImportedModuleIdentifierByPackageAndNameOrBarrel(sourceFile, "effect", "Effect") || "Effect";
|
|
11953
|
+
const runtimeModuleIdentifier = tsUtils.findImportedModuleIdentifierByPackageAndNameOrBarrel(sourceFile, "effect", "Runtime") || "Runtime";
|
|
11668
11954
|
let runtimeIdentifier = void 0;
|
|
11955
|
+
let servicesIdentifier = void 0;
|
|
11669
11956
|
for (const statement of effectGen.generatorFunction.body.statements) {
|
|
11670
11957
|
if (ts.isVariableStatement(statement) && statement.declarationList.declarations.length === 1) {
|
|
11671
11958
|
const declaration = statement.declarationList.declarations[0];
|
|
@@ -11679,11 +11966,46 @@ var runEffectInsideEffect = createDiagnostic({
|
|
|
11679
11966
|
if (isSome2(maybeEffectRuntime) && ts.isIdentifier(declaration.name)) {
|
|
11680
11967
|
runtimeIdentifier = ts.idText(declaration.name);
|
|
11681
11968
|
}
|
|
11969
|
+
const maybeEffectServices = yield* pipe(
|
|
11970
|
+
typeParser.isNodeReferenceToEffectModuleApi("services")(yieldedExpression.expression),
|
|
11971
|
+
option
|
|
11972
|
+
);
|
|
11973
|
+
if (isSome2(maybeEffectServices) && ts.isIdentifier(declaration.name)) {
|
|
11974
|
+
servicesIdentifier = ts.idText(declaration.name);
|
|
11975
|
+
}
|
|
11682
11976
|
}
|
|
11683
11977
|
}
|
|
11684
11978
|
}
|
|
11685
11979
|
}
|
|
11686
|
-
if (!
|
|
11980
|
+
if (supportedEffect === "v4" && !servicesIdentifier) {
|
|
11981
|
+
changeTracker.insertNodeAt(
|
|
11982
|
+
sourceFile,
|
|
11983
|
+
effectGen.body.statements[0].pos,
|
|
11984
|
+
ts.factory.createVariableStatement(
|
|
11985
|
+
void 0,
|
|
11986
|
+
ts.factory.createVariableDeclarationList([ts.factory.createVariableDeclaration(
|
|
11987
|
+
"effectServices",
|
|
11988
|
+
void 0,
|
|
11989
|
+
void 0,
|
|
11990
|
+
ts.factory.createYieldExpression(
|
|
11991
|
+
ts.factory.createToken(ts.SyntaxKind.AsteriskToken),
|
|
11992
|
+
ts.factory.createCallExpression(
|
|
11993
|
+
ts.factory.createPropertyAccessExpression(
|
|
11994
|
+
ts.factory.createIdentifier(effectModuleIdentifier),
|
|
11995
|
+
"services"
|
|
11996
|
+
),
|
|
11997
|
+
[ts.factory.createKeywordTypeNode(ts.SyntaxKind.NeverKeyword)],
|
|
11998
|
+
[]
|
|
11999
|
+
)
|
|
12000
|
+
)
|
|
12001
|
+
)], ts.NodeFlags.Const)
|
|
12002
|
+
),
|
|
12003
|
+
{
|
|
12004
|
+
prefix: "\n",
|
|
12005
|
+
suffix: "\n"
|
|
12006
|
+
}
|
|
12007
|
+
);
|
|
12008
|
+
} else if (supportedEffect === "v3" && !runtimeIdentifier) {
|
|
11687
12009
|
changeTracker.insertNodeAt(
|
|
11688
12010
|
sourceFile,
|
|
11689
12011
|
effectGen.body.statements[0].pos,
|
|
@@ -11719,16 +12041,19 @@ var runEffectInsideEffect = createDiagnostic({
|
|
|
11719
12041
|
changeTracker.insertText(
|
|
11720
12042
|
sourceFile,
|
|
11721
12043
|
node.arguments[0].pos,
|
|
11722
|
-
`${runtimeModuleIdentifier}.${isEffectRunCall.value.methodName}(${runtimeIdentifier || "effectRuntime"}, `
|
|
12044
|
+
supportedEffect === "v4" ? `${effectModuleIdentifier}.${isEffectRunCall.value.methodName}With(${servicesIdentifier || "effectServices"})(` : `${runtimeModuleIdentifier}.${isEffectRunCall.value.methodName}(${runtimeIdentifier || "effectRuntime"}, `
|
|
11723
12045
|
);
|
|
11724
12046
|
});
|
|
12047
|
+
const v4MethodName = `${isEffectRunCall.value.methodName}With`;
|
|
12048
|
+
const messageText = supportedEffect === "v4" ? `Using ${nodeText} inside an Effect is not recommended. The same services should generally be used instead to run child effects.
|
|
12049
|
+
Consider extracting the current services by using for example Effect.services and then use Effect.${v4MethodName} with the extracted services instead.` : `Using ${nodeText} inside an Effect is not recommended. The same runtime should generally be used instead to run child effects.
|
|
12050
|
+
Consider extracting the Runtime by using for example Effect.runtime and then use Runtime.${isEffectRunCall.value.methodName} with the extracted runtime instead.`;
|
|
11725
12051
|
report({
|
|
11726
12052
|
location: node.expression,
|
|
11727
|
-
messageText
|
|
11728
|
-
Consider extracting the Runtime by using for example Effect.runtime and then use Runtime.${isEffectRunCall.value.methodName} with the extracted runtime instead.`,
|
|
12053
|
+
messageText,
|
|
11729
12054
|
fixes: [{
|
|
11730
12055
|
fixName: "runEffectInsideEffect_fix",
|
|
11731
|
-
description: "Use a runtime to run the Effect",
|
|
12056
|
+
description: supportedEffect === "v4" ? "Use the current services to run the Effect" : "Use a runtime to run the Effect",
|
|
11732
12057
|
apply: fixAddRuntime
|
|
11733
12058
|
}]
|
|
11734
12059
|
});
|
|
@@ -11874,9 +12199,8 @@ var schemaSyncInEffect = createDiagnostic({
|
|
|
11874
12199
|
option
|
|
11875
12200
|
);
|
|
11876
12201
|
if (isNone2(isSchemaSyncCall)) continue;
|
|
11877
|
-
const {
|
|
11878
|
-
if (!
|
|
11879
|
-
if (scopeNode && scopeNode !== effectGen.generatorFunction) continue;
|
|
12202
|
+
const { inEffect } = yield* typeParser.findEnclosingScopes(node);
|
|
12203
|
+
if (!inEffect) continue;
|
|
11880
12204
|
const nodeText = sourceFile.text.substring(
|
|
11881
12205
|
ts.getTokenPosOfNode(node.expression, sourceFile),
|
|
11882
12206
|
node.expression.end
|
|
@@ -12718,6 +13042,7 @@ var diagnostics = [
|
|
|
12718
13042
|
unnecessaryPipe,
|
|
12719
13043
|
genericEffectServices,
|
|
12720
13044
|
globalFetch,
|
|
13045
|
+
globalFetchInEffect,
|
|
12721
13046
|
returnEffectInGen,
|
|
12722
13047
|
tryCatchInEffectGen,
|
|
12723
13048
|
importFromBarrel,
|
|
@@ -12749,7 +13074,15 @@ var diagnostics = [
|
|
|
12749
13074
|
preferSchemaOverJson,
|
|
12750
13075
|
extendsNativeError,
|
|
12751
13076
|
serviceNotAsClass,
|
|
12752
|
-
nodeBuiltinImport
|
|
13077
|
+
nodeBuiltinImport,
|
|
13078
|
+
globalDate,
|
|
13079
|
+
globalDateInEffect,
|
|
13080
|
+
globalConsole,
|
|
13081
|
+
globalConsoleInEffect,
|
|
13082
|
+
globalRandom,
|
|
13083
|
+
globalRandomInEffect,
|
|
13084
|
+
globalTimers,
|
|
13085
|
+
globalTimersInEffect
|
|
12753
13086
|
];
|
|
12754
13087
|
|
|
12755
13088
|
// src/completions/effectDiagnosticsComment.ts
|