@effect/language-service 0.22.1 → 0.22.3

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.
Files changed (4) hide show
  1. package/README.md +1 -0
  2. package/index.js +1398 -1352
  3. package/index.js.map +1 -1
  4. package/package.json +1 -1
package/index.js CHANGED
@@ -729,6 +729,7 @@ var getOrElse = /* @__PURE__ */ dual(2, (self, onLeft) => isLeft2(self) ? onLeft
729
729
 
730
730
  // node_modules/.pnpm/effect@3.16.5/node_modules/effect/dist/esm/Order.js
731
731
  var make2 = (compare) => (self, that) => self === that ? 0 : compare(self, that);
732
+ var string2 = /* @__PURE__ */ make2((self, that) => self < that ? -1 : 1);
732
733
 
733
734
  // node_modules/.pnpm/effect@3.16.5/node_modules/effect/dist/esm/Option.js
734
735
  var none2 = () => none;
@@ -1732,139 +1733,364 @@ var effectDataClasses = createCompletion({
1732
1733
  })
1733
1734
  });
1734
1735
 
1735
- // src/completions/effectSchemaSelfInClasses.ts
1736
- var effectSchemaSelfInClasses = createCompletion({
1737
- name: "effectSchemaSelfInClasses",
1738
- apply: fn("effectSchemaSelfInClasses")(function* (sourceFile, position) {
1739
- const ts = yield* service(TypeScriptApi);
1740
- const maybeInfos = yield* option(
1741
- parseDataForExtendsClassCompletion(sourceFile, position)
1742
- );
1743
- if (isNone2(maybeInfos)) return [];
1744
- const { accessedObject, className, replacementSpan } = maybeInfos.value;
1745
- const effectSchemaName = yield* option(
1746
- findImportedModuleIdentifierByPackageAndNameOrBarrel(
1747
- sourceFile,
1748
- "effect",
1749
- "Schema"
1750
- )
1751
- );
1752
- const schemaIdentifier = match(effectSchemaName, {
1753
- onNone: () => "Schema",
1754
- onSome: (_) => _.text
1755
- });
1756
- if (schemaIdentifier !== accessedObject.text) return [];
1757
- const name = className.text;
1758
- return [{
1759
- name: `Class<${name}>`,
1760
- kind: ts.ScriptElementKind.constElement,
1761
- insertText: `${schemaIdentifier}.Class<${name}>("${name}")({${"${0}"}}){}`,
1762
- replacementSpan,
1763
- isSnippet: true
1764
- }, {
1765
- name: `TaggedError<${name}>`,
1766
- kind: ts.ScriptElementKind.constElement,
1767
- insertText: `${schemaIdentifier}.TaggedError<${name}>("${name}")("${name}", {${"${0}"}}){}`,
1768
- replacementSpan,
1769
- isSnippet: true
1770
- }, {
1771
- name: `TaggedClass<${name}>`,
1772
- kind: ts.ScriptElementKind.constElement,
1773
- insertText: `${schemaIdentifier}.TaggedClass<${name}>("${name}")("${name}", {${"${0}"}}){}`,
1774
- replacementSpan,
1775
- isSnippet: true
1776
- }, {
1777
- name: `TaggedRequest<${name}>`,
1778
- kind: ts.ScriptElementKind.constElement,
1779
- insertText: `${schemaIdentifier}.TaggedRequest<${name}>("${name}")("${name}", {${"${0}"}}){}`,
1780
- replacementSpan,
1781
- isSnippet: true
1782
- }];
1783
- })
1784
- });
1736
+ // src/diagnostics/duplicatePackage.ts
1737
+ var checkedPackagesCache = /* @__PURE__ */ new Map();
1738
+ var programResolvedCacheSize = /* @__PURE__ */ new Map();
1739
+ var duplicatePackage = createDiagnostic({
1740
+ name: "duplicatePackage",
1741
+ code: 6,
1742
+ severity: "warning",
1743
+ apply: fn("duplicatePackage.apply")(function* (sourceFile, report) {
1744
+ const program = yield* service(TypeScriptProgram);
1745
+ const options = yield* service(LanguageServicePluginOptions);
1746
+ if (sourceFile.statements.length < 1) return;
1747
+ let resolvedPackages = checkedPackagesCache.get(sourceFile.fileName) || {};
1748
+ const newResolvedModuleSize = hasProperty(program, "resolvedModules") && hasProperty(program.resolvedModules, "size") && isNumber(program.resolvedModules.size) ? program.resolvedModules.size : 0;
1749
+ const oldResolvedSize = programResolvedCacheSize.get(sourceFile.fileName) || -1;
1750
+ if (newResolvedModuleSize !== oldResolvedSize) {
1751
+ const seenPackages = /* @__PURE__ */ new Set();
1752
+ resolvedPackages = {};
1753
+ program.getSourceFiles().map((_) => {
1754
+ const packageInfo = parsePackageContentNameAndVersionFromScope(_);
1755
+ if (!packageInfo) return;
1756
+ const packageNameAndVersion = packageInfo.name + "@" + packageInfo.version;
1757
+ if (seenPackages.has(packageNameAndVersion)) return;
1758
+ seenPackages.add(packageNameAndVersion);
1759
+ if (!(packageInfo.name === "effect" || packageInfo.hasEffectInPeerDependencies)) return;
1760
+ if (options.allowedDuplicatedPackages.indexOf(packageInfo.name) > -1) return;
1761
+ resolvedPackages[packageInfo.name] = resolvedPackages[packageInfo.name] || {};
1762
+ resolvedPackages[packageInfo.name][packageInfo.version] = packageInfo.packageDirectory;
1763
+ });
1764
+ checkedPackagesCache.set(sourceFile.fileName, resolvedPackages);
1765
+ programResolvedCacheSize.set(sourceFile.fileName, newResolvedModuleSize);
1766
+ }
1767
+ for (const packageName of Object.keys(resolvedPackages)) {
1768
+ if (Object.keys(resolvedPackages[packageName]).length > 1) {
1769
+ const versions = Object.keys(resolvedPackages[packageName]);
1770
+ report({
1771
+ node: sourceFile.statements[0],
1772
+ messageText: `Package ${packageName} is referenced multiple times with different versions (${versions.join(", ")}) and may cause unexpected type errors.
1773
+ Cleanup your dependencies and your package lockfile to avoid multiple instances of this package and reload the project.
1774
+ If this is intended set the LSP config "allowedDuplicatedPackages" to ${JSON.stringify(options.allowedDuplicatedPackages.concat([packageName]))}.
1785
1775
 
1786
- // src/completions/effectSelfInClasses.ts
1787
- var effectSelfInClasses = createCompletion({
1788
- name: "effectSelfInClasses",
1789
- apply: fn("effectSelfInClasses")(function* (sourceFile, position) {
1790
- const ts = yield* service(TypeScriptApi);
1791
- const maybeInfos = yield* option(
1792
- parseDataForExtendsClassCompletion(sourceFile, position)
1793
- );
1794
- if (isNone2(maybeInfos)) return [];
1795
- const { accessedObject, className, replacementSpan } = maybeInfos.value;
1796
- const effectName = yield* option(
1797
- findImportedModuleIdentifierByPackageAndNameOrBarrel(
1798
- sourceFile,
1799
- "effect",
1800
- "Effect"
1801
- )
1802
- );
1803
- const effectIdentifier = match(effectName, {
1804
- onNone: () => "Effect",
1805
- onSome: (_) => _.text
1806
- });
1807
- if (effectIdentifier !== accessedObject.text) return [];
1808
- const name = className.text;
1809
- return [{
1810
- name: `Service<${name}>`,
1811
- kind: ts.ScriptElementKind.constElement,
1812
- insertText: `${effectIdentifier}.Service<${name}>()("${name}", {${"${0}"}}){}`,
1813
- replacementSpan,
1814
- isSnippet: true
1815
- }];
1776
+ ${versions.map((version) => `- found ${version} at ${resolvedPackages[packageName][version]}`).join("\n")}`,
1777
+ fixes: []
1778
+ });
1779
+ }
1780
+ }
1816
1781
  })
1817
1782
  });
1818
1783
 
1819
- // src/core/TypeParser.ts
1820
- var TypeParser = Tag("@effect/language-service/TypeParser");
1821
- var TypeParserIssue = class _TypeParserIssue {
1822
- _tag = "@effect/language-service/TypeParserIssue";
1823
- static issue = fail(new _TypeParserIssue());
1824
- };
1825
- function make4(ts, typeChecker) {
1826
- function typeParserIssue(_message, _type, _node) {
1827
- return TypeParserIssue.issue;
1828
- }
1829
- function covariantTypeArgument(type) {
1830
- const signatures = type.getCallSignatures();
1831
- if (signatures.length !== 1) {
1832
- return typeParserIssue("Covariant type has no call signature", type);
1784
+ // src/core/TypeCheckerApi.ts
1785
+ var TypeCheckerApi = Tag("TypeChecker");
1786
+ var TypeCheckerApiCache = Tag("TypeCheckerApiCache");
1787
+ function makeTypeCheckerApiCache() {
1788
+ return {
1789
+ expectedAndRealType: /* @__PURE__ */ new WeakMap()
1790
+ };
1791
+ }
1792
+ var deterministicTypeOrder = gen(function* () {
1793
+ const typeChecker = yield* service(TypeCheckerApi);
1794
+ return make2((a, b) => {
1795
+ const aName = typeChecker.typeToString(a);
1796
+ const bName = typeChecker.typeToString(b);
1797
+ if (aName < bName) return -1;
1798
+ if (aName > bName) return 1;
1799
+ return 0;
1800
+ });
1801
+ });
1802
+ var getMissingTypeEntriesInTargetType = fn(
1803
+ "TypeCheckerApi.getMissingTypeEntriesInTargetType"
1804
+ )(
1805
+ function* (realType, expectedType) {
1806
+ if (realType === expectedType) return [];
1807
+ const typeChecker = yield* service(TypeCheckerApi);
1808
+ const result = [];
1809
+ let toTest = [realType];
1810
+ while (toTest.length > 0) {
1811
+ const type = toTest.pop();
1812
+ if (!type) return result;
1813
+ if (type.isUnion()) {
1814
+ toTest = toTest.concat(type.types);
1815
+ } else {
1816
+ const assignable = typeChecker.isTypeAssignableTo(type, expectedType);
1817
+ if (!assignable) {
1818
+ result.push(type);
1819
+ }
1820
+ }
1833
1821
  }
1834
- return succeed(signatures[0].getReturnType());
1822
+ return result;
1835
1823
  }
1836
- function contravariantTypeArgument(type) {
1837
- const signatures = type.getCallSignatures();
1838
- if (signatures.length !== 1) {
1839
- return typeParserIssue("Contravariant type has no call signature", type);
1824
+ );
1825
+ var CannotFindAncestorConvertibleDeclarationError = class {
1826
+ constructor(node) {
1827
+ this.node = node;
1828
+ }
1829
+ _tag = "@effect/language-service/CannotFindAncestorConvertibleDeclarationError";
1830
+ };
1831
+ var getAncestorConvertibleDeclaration = fn(
1832
+ "TypeCheckerApi.getAncestorConvertibleDeclaration"
1833
+ )(function* (node) {
1834
+ const ts = yield* service(TypeScriptApi);
1835
+ let current = node;
1836
+ while (current) {
1837
+ if (ts.isFunctionDeclaration(current) || ts.isFunctionExpression(current) || ts.isArrowFunction(current) || ts.isMethodDeclaration(current)) {
1838
+ return current;
1840
1839
  }
1841
- return succeed(signatures[0].getTypeParameterAtPosition(0));
1840
+ current = current.parent;
1842
1841
  }
1843
- function invariantTypeArgument(type) {
1844
- const signatures = type.getCallSignatures();
1845
- if (signatures.length !== 1) {
1846
- return typeParserIssue("Invariant type has no call signature", type);
1842
+ return yield* fail(new CannotFindAncestorConvertibleDeclarationError(node));
1843
+ });
1844
+ var CannotInferReturnTypeFromEmptyBody = class {
1845
+ constructor(declaration) {
1846
+ this.declaration = declaration;
1847
+ }
1848
+ _tag = "@effect/language-service/CannotInferReturnTypeFromEmptyBody";
1849
+ };
1850
+ var CannotInferReturnType = class {
1851
+ constructor(declaration) {
1852
+ this.declaration = declaration;
1853
+ }
1854
+ _tag = "@effect/language-service/CannotInferReturnType";
1855
+ };
1856
+ var getInferredReturnType = fn("TypeCheckerApi.getInferredReturnType")(function* (declaration) {
1857
+ const typeChecker = yield* service(TypeCheckerApi);
1858
+ if (!declaration.body) {
1859
+ return yield* fail(
1860
+ new CannotInferReturnTypeFromEmptyBody(declaration)
1861
+ );
1862
+ }
1863
+ let returnType;
1864
+ if (typeChecker.isImplementationOfOverload(declaration)) {
1865
+ const signatures = typeChecker.getTypeAtLocation(declaration).getCallSignatures();
1866
+ if (signatures.length > 1) {
1867
+ returnType = typeChecker.getUnionType(
1868
+ signatures.map((s) => s.getReturnType()).filter((_) => !!_)
1869
+ );
1847
1870
  }
1848
- return succeed(signatures[0].getReturnType());
1849
1871
  }
1850
- const pipeableType = cachedBy(
1851
- function(type, atLocation) {
1852
- const pipeSymbol = typeChecker.getPropertyOfType(type, "pipe");
1853
- if (!pipeSymbol) {
1854
- return typeParserIssue("Type has no 'pipe' property", type, atLocation);
1855
- }
1856
- const pipeType = typeChecker.getTypeOfSymbolAtLocation(pipeSymbol, atLocation);
1857
- const signatures = pipeType.getCallSignatures();
1858
- if (signatures.length === 0) {
1859
- return typeParserIssue("'pipe' property is not callable", type, atLocation);
1872
+ if (!returnType) {
1873
+ const signature = typeChecker.getSignatureFromDeclaration(declaration);
1874
+ if (signature) {
1875
+ const typePredicate = typeChecker.getTypePredicateOfSignature(signature);
1876
+ if (typePredicate && typePredicate.type) {
1877
+ return typePredicate.type;
1878
+ } else {
1879
+ returnType = typeChecker.getReturnTypeOfSignature(signature);
1860
1880
  }
1861
- return succeed(type);
1862
- },
1863
- "TypeParser.pipeableType",
1864
- (type) => type
1865
- );
1866
- const varianceStructCovariantType = (type, atLocation, propertyName) => {
1867
- const propertySymbol = typeChecker.getPropertyOfType(type, propertyName);
1881
+ }
1882
+ }
1883
+ if (!returnType) {
1884
+ return yield* fail(
1885
+ new CannotInferReturnType(declaration)
1886
+ );
1887
+ }
1888
+ return returnType;
1889
+ });
1890
+ var expectedAndRealType = fn("TypeCheckerApi.expectedAndRealType")(function* (sourceFile) {
1891
+ const cache = yield* service(TypeCheckerApiCache);
1892
+ const resultCached = cache.expectedAndRealType.get(sourceFile);
1893
+ if (resultCached) return resultCached;
1894
+ const typeChecker = yield* service(TypeCheckerApi);
1895
+ const ts = yield* service(TypeScriptApi);
1896
+ const result = [];
1897
+ const nodeToVisit = [sourceFile];
1898
+ const appendNodeToVisit = (node) => {
1899
+ nodeToVisit.push(node);
1900
+ return void 0;
1901
+ };
1902
+ while (nodeToVisit.length > 0) {
1903
+ const node = nodeToVisit.shift();
1904
+ if (ts.isVariableDeclaration(node) && node.initializer) {
1905
+ const expectedType = typeChecker.getTypeAtLocation(node.name);
1906
+ const realType = typeChecker.getTypeAtLocation(node.initializer);
1907
+ result.push([node.name, expectedType, node.initializer, realType]);
1908
+ appendNodeToVisit(node.initializer);
1909
+ continue;
1910
+ } else if (ts.isCallExpression(node)) {
1911
+ const resolvedSignature = typeChecker.getResolvedSignature(node);
1912
+ if (resolvedSignature) {
1913
+ resolvedSignature.getParameters().map((parameter, index) => {
1914
+ const expectedType = typeChecker.getTypeOfSymbolAtLocation(parameter, node);
1915
+ const realType = typeChecker.getTypeAtLocation(node.arguments[index]);
1916
+ result.push([
1917
+ node.arguments[index],
1918
+ expectedType,
1919
+ node.arguments[index],
1920
+ realType
1921
+ ]);
1922
+ });
1923
+ }
1924
+ ts.forEachChild(node, appendNodeToVisit);
1925
+ continue;
1926
+ } else if (ts.isIdentifier(node) || ts.isStringLiteral(node) || ts.isNumericLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node)) {
1927
+ const parent = node.parent;
1928
+ if (ts.isObjectLiteralElement(parent)) {
1929
+ if (ts.isObjectLiteralExpression(parent.parent) && parent.name === node) {
1930
+ const type = typeChecker.getContextualType(parent.parent);
1931
+ if (type) {
1932
+ const symbol3 = typeChecker.getPropertyOfType(type, node.text);
1933
+ if (symbol3) {
1934
+ const expectedType = typeChecker.getTypeOfSymbolAtLocation(symbol3, node);
1935
+ const realType = typeChecker.getTypeAtLocation(node);
1936
+ result.push([node, expectedType, node, realType]);
1937
+ }
1938
+ }
1939
+ }
1940
+ }
1941
+ ts.forEachChild(node, appendNodeToVisit);
1942
+ continue;
1943
+ } else if (ts.isBinaryExpression(node) && node.operatorToken.kind === ts.SyntaxKind.EqualsToken) {
1944
+ const expectedType = typeChecker.getTypeAtLocation(node.left);
1945
+ const realType = typeChecker.getTypeAtLocation(node.right);
1946
+ result.push([node.left, expectedType, node.right, realType]);
1947
+ appendNodeToVisit(node.right);
1948
+ continue;
1949
+ } else if (ts.isReturnStatement(node) && node.expression) {
1950
+ const parentDeclaration = yield* option(getAncestorConvertibleDeclaration(node));
1951
+ if (isSome2(parentDeclaration)) {
1952
+ const expectedType = yield* option(getInferredReturnType(parentDeclaration.value));
1953
+ const realType = typeChecker.getTypeAtLocation(node.expression);
1954
+ if (isSome2(expectedType)) {
1955
+ result.push([node, expectedType.value, node, realType]);
1956
+ }
1957
+ }
1958
+ ts.forEachChild(node, appendNodeToVisit);
1959
+ continue;
1960
+ } else if (ts.isArrowFunction(node) && (node.typeParameters || []).length === 0 && ts.isExpression(node.body)) {
1961
+ const body = node.body;
1962
+ const expectedType = typeChecker.getContextualType(body);
1963
+ const realType = typeChecker.getTypeAtLocation(body);
1964
+ if (expectedType) {
1965
+ result.push([body, expectedType, body, realType]);
1966
+ }
1967
+ ts.forEachChild(body, appendNodeToVisit);
1968
+ continue;
1969
+ } else if (ts.isArrowFunction(node) && (node.typeParameters || []).length > 0 && ts.isExpression(node.body)) {
1970
+ const body = node.body;
1971
+ const expectedType = yield* option(getInferredReturnType(node));
1972
+ const realType = typeChecker.getTypeAtLocation(body);
1973
+ if (isSome2(expectedType)) {
1974
+ result.push([body, expectedType.value, body, realType]);
1975
+ }
1976
+ ts.forEachChild(body, appendNodeToVisit);
1977
+ continue;
1978
+ } else if (ts.isSatisfiesExpression(node)) {
1979
+ const expectedType = typeChecker.getTypeAtLocation(node.type);
1980
+ const realType = typeChecker.getTypeAtLocation(node.expression);
1981
+ result.push([node.expression, expectedType, node.expression, realType]);
1982
+ appendNodeToVisit(node.expression);
1983
+ continue;
1984
+ }
1985
+ ts.forEachChild(node, appendNodeToVisit);
1986
+ }
1987
+ cache.expectedAndRealType.set(sourceFile, result);
1988
+ return result;
1989
+ });
1990
+ var appendToUniqueTypesMap = fn(
1991
+ "TypeCheckerApi.appendToUniqueTypesMap"
1992
+ )(
1993
+ function* (memory, initialType, excludeNever) {
1994
+ const ts = yield* service(TypeScriptApi);
1995
+ const typeChecker = yield* service(TypeCheckerApi);
1996
+ const newIndexes = /* @__PURE__ */ new Set();
1997
+ const knownIndexes = /* @__PURE__ */ new Set();
1998
+ let toTest = [initialType];
1999
+ while (toTest.length > 0) {
2000
+ const type = toTest.pop();
2001
+ if (!type) break;
2002
+ if (excludeNever && type.flags & ts.TypeFlags.Never) {
2003
+ continue;
2004
+ }
2005
+ if (type.isUnion()) {
2006
+ toTest = toTest.concat(type.types);
2007
+ } else {
2008
+ const foundMatch = [];
2009
+ for (const [typeId, knownType] of memory.entries()) {
2010
+ const areSame = typeChecker.isTypeAssignableTo(knownType, type) && typeChecker.isTypeAssignableTo(type, knownType);
2011
+ if (areSame) {
2012
+ foundMatch.push(typeId);
2013
+ break;
2014
+ }
2015
+ }
2016
+ if (foundMatch.length === 0) {
2017
+ const newId = "t" + (memory.size + 1);
2018
+ memory.set(newId, type);
2019
+ newIndexes.add(newId);
2020
+ } else {
2021
+ knownIndexes.add(foundMatch[0]);
2022
+ }
2023
+ }
2024
+ }
2025
+ return {
2026
+ newIndexes,
2027
+ knownIndexes,
2028
+ allIndexes: pipe(
2029
+ fromIterable(newIndexes),
2030
+ appendAll(fromIterable(knownIndexes))
2031
+ )
2032
+ };
2033
+ }
2034
+ );
2035
+ function makeResolveExternalModuleName(typeChecker) {
2036
+ if (!(hasProperty(typeChecker, "resolveExternalModuleName") && isFunction(typeChecker.resolveExternalModuleName))) {
2037
+ return;
2038
+ }
2039
+ const _internal = typeChecker.resolveExternalModuleName;
2040
+ return (moduleSpecifier) => {
2041
+ return _internal(moduleSpecifier);
2042
+ };
2043
+ }
2044
+
2045
+ // src/core/TypeParser.ts
2046
+ var TypeParser = Tag("@effect/language-service/TypeParser");
2047
+ var TypeParserIssue = class _TypeParserIssue {
2048
+ _tag = "@effect/language-service/TypeParserIssue";
2049
+ static issue = fail(new _TypeParserIssue());
2050
+ };
2051
+ function make4(ts, typeChecker) {
2052
+ function typeParserIssue(_message, _type, _node) {
2053
+ return TypeParserIssue.issue;
2054
+ }
2055
+ function covariantTypeArgument(type) {
2056
+ const signatures = type.getCallSignatures();
2057
+ if (signatures.length !== 1) {
2058
+ return typeParserIssue("Covariant type has no call signature", type);
2059
+ }
2060
+ return succeed(signatures[0].getReturnType());
2061
+ }
2062
+ function contravariantTypeArgument(type) {
2063
+ const signatures = type.getCallSignatures();
2064
+ if (signatures.length !== 1) {
2065
+ return typeParserIssue("Contravariant type has no call signature", type);
2066
+ }
2067
+ return succeed(signatures[0].getTypeParameterAtPosition(0));
2068
+ }
2069
+ function invariantTypeArgument(type) {
2070
+ const signatures = type.getCallSignatures();
2071
+ if (signatures.length !== 1) {
2072
+ return typeParserIssue("Invariant type has no call signature", type);
2073
+ }
2074
+ return succeed(signatures[0].getReturnType());
2075
+ }
2076
+ const pipeableType = cachedBy(
2077
+ function(type, atLocation) {
2078
+ const pipeSymbol = typeChecker.getPropertyOfType(type, "pipe");
2079
+ if (!pipeSymbol) {
2080
+ return typeParserIssue("Type has no 'pipe' property", type, atLocation);
2081
+ }
2082
+ const pipeType = typeChecker.getTypeOfSymbolAtLocation(pipeSymbol, atLocation);
2083
+ const signatures = pipeType.getCallSignatures();
2084
+ if (signatures.length === 0) {
2085
+ return typeParserIssue("'pipe' property is not callable", type, atLocation);
2086
+ }
2087
+ return succeed(type);
2088
+ },
2089
+ "TypeParser.pipeableType",
2090
+ (type) => type
2091
+ );
2092
+ const varianceStructCovariantType = (type, atLocation, propertyName) => {
2093
+ const propertySymbol = typeChecker.getPropertyOfType(type, propertyName);
1868
2094
  if (!propertySymbol) {
1869
2095
  return typeParserIssue(`Type has no '${propertyName}' property`, type, atLocation);
1870
2096
  }
@@ -2247,686 +2473,59 @@ function make4(ts, typeChecker) {
2247
2473
  varianceStructInvariantType(type, atLocation, "_Service")
2248
2474
  ),
2249
2475
  ([Identifier, Service]) => ({ Identifier, Service })
2250
- );
2251
- const contextTag = cachedBy(
2252
- fn("TypeParser.contextTag")(function* (type, atLocation) {
2253
- yield* pipeableType(type, atLocation);
2254
- const propertiesSymbols = typeChecker.getPropertiesOfType(type).filter(
2255
- (_) => _.flags & ts.SymbolFlags.Property && !(_.flags & ts.SymbolFlags.Optional)
2256
- );
2257
- propertiesSymbols.sort((a, b) => b.name.indexOf("TypeId") - a.name.indexOf("TypeId"));
2258
- for (const propertySymbol of propertiesSymbols) {
2259
- const propertyType = typeChecker.getTypeOfSymbolAtLocation(propertySymbol, atLocation);
2260
- const varianceArgs = yield* option(contextTagVarianceStruct(
2261
- propertyType,
2262
- atLocation
2263
- ));
2264
- if (isSome2(varianceArgs)) {
2265
- return varianceArgs.value;
2266
- }
2267
- }
2268
- return yield* typeParserIssue("Type has no tag variance struct", type, atLocation);
2269
- }),
2270
- "TypeParser.contextTag",
2271
- (type) => type
2272
- );
2273
- const pipeCall = cachedBy(
2274
- function(node) {
2275
- if (ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression) && ts.isIdentifier(node.expression.name) && node.expression.name.text === "pipe") {
2276
- return succeed({ node, subject: node.expression.expression, args: Array.from(node.arguments) });
2277
- }
2278
- if (ts.isCallExpression(node) && ts.isIdentifier(node.expression) && node.expression.text === "pipe" && node.arguments.length > 0) {
2279
- const [subject, ...args] = node.arguments;
2280
- return succeed({ node, subject, args });
2281
- }
2282
- return typeParserIssue("Node is not a pipe call", void 0, node);
2283
- },
2284
- "TypeParser.pipeCall",
2285
- (node) => node
2286
- );
2287
- return {
2288
- effectType,
2289
- strictEffectType,
2290
- layerType,
2291
- fiberType,
2292
- effectSubtype,
2293
- importedEffectModule,
2294
- effectGen,
2295
- effectFnUntracedGen,
2296
- effectFnGen,
2297
- unnecessaryEffectGen: unnecessaryEffectGen2,
2298
- effectSchemaType,
2299
- contextTag,
2300
- pipeCall
2301
- };
2302
- }
2303
-
2304
- // src/completions/fnFunctionStar.ts
2305
- var fnFunctionStar = createCompletion({
2306
- name: "fnFunctionStar",
2307
- apply: fn("fnFunctionStar")(function* (sourceFile, position) {
2308
- const ts = yield* service(TypeScriptApi);
2309
- const typeParser = yield* service(TypeParser);
2310
- const maybeInfos = yield* option(
2311
- parseAccessedExpressionForCompletion(sourceFile, position)
2312
- );
2313
- if (isNone2(maybeInfos)) return [];
2314
- const { accessedObject } = maybeInfos.value;
2315
- const isEffectModule = yield* option(typeParser.importedEffectModule(accessedObject));
2316
- if (isNone2(isEffectModule)) return [];
2317
- const span = ts.createTextSpan(
2318
- accessedObject.end + 1,
2319
- Math.max(0, position - accessedObject.end - 1)
2320
- );
2321
- const maybeFnName = pipe(
2322
- yield* getAncestorNodesInRange(sourceFile, toTextRange(accessedObject.pos)),
2323
- filter(ts.isVariableDeclaration),
2324
- map3((_) => _.name && ts.isIdentifier(_.name) ? _.name.text : ""),
2325
- filter((_) => _.length > 0),
2326
- head,
2327
- map2((name) => [
2328
- {
2329
- name: `fn("${name}")`,
2330
- kind: ts.ScriptElementKind.constElement,
2331
- insertText: `fn("${name}")(function*(${"${1}"}){${"${0}"}})`,
2332
- replacementSpan: span,
2333
- isSnippet: true
2334
- }
2335
- ]),
2336
- getOrElse2(() => [])
2337
- );
2338
- return maybeFnName.concat([{
2339
- name: `fn(function*(){})`,
2340
- kind: ts.ScriptElementKind.constElement,
2341
- insertText: `fn(function*(${"${1}"}){${"${0}"}})`,
2342
- replacementSpan: span,
2343
- isSnippet: true
2344
- }, {
2345
- name: `fnUntraced(function*(){})`,
2346
- kind: ts.ScriptElementKind.constElement,
2347
- insertText: `fnUntraced(function*(${"${1}"}){${"${0}"}})`,
2348
- replacementSpan: span,
2349
- isSnippet: true
2350
- }]);
2351
- })
2352
- });
2353
-
2354
- // src/core/TypeCheckerApi.ts
2355
- var TypeCheckerApi = Tag("TypeChecker");
2356
- var TypeCheckerApiCache = Tag("TypeCheckerApiCache");
2357
- function makeTypeCheckerApiCache() {
2358
- return {
2359
- expectedAndRealType: /* @__PURE__ */ new WeakMap()
2360
- };
2361
- }
2362
- var deterministicTypeOrder = gen(function* () {
2363
- const typeChecker = yield* service(TypeCheckerApi);
2364
- return make2((a, b) => {
2365
- const aName = typeChecker.typeToString(a);
2366
- const bName = typeChecker.typeToString(b);
2367
- if (aName < bName) return -1;
2368
- if (aName > bName) return 1;
2369
- return 0;
2370
- });
2371
- });
2372
- var getMissingTypeEntriesInTargetType = fn(
2373
- "TypeCheckerApi.getMissingTypeEntriesInTargetType"
2374
- )(
2375
- function* (realType, expectedType) {
2376
- if (realType === expectedType) return [];
2377
- const typeChecker = yield* service(TypeCheckerApi);
2378
- const result = [];
2379
- let toTest = [realType];
2380
- while (toTest.length > 0) {
2381
- const type = toTest.pop();
2382
- if (!type) return result;
2383
- if (type.isUnion()) {
2384
- toTest = toTest.concat(type.types);
2385
- } else {
2386
- const assignable = typeChecker.isTypeAssignableTo(type, expectedType);
2387
- if (!assignable) {
2388
- result.push(type);
2389
- }
2390
- }
2391
- }
2392
- return result;
2393
- }
2394
- );
2395
- var CannotFindAncestorConvertibleDeclarationError = class {
2396
- constructor(node) {
2397
- this.node = node;
2398
- }
2399
- _tag = "@effect/language-service/CannotFindAncestorConvertibleDeclarationError";
2400
- };
2401
- var getAncestorConvertibleDeclaration = fn(
2402
- "TypeCheckerApi.getAncestorConvertibleDeclaration"
2403
- )(function* (node) {
2404
- const ts = yield* service(TypeScriptApi);
2405
- let current = node;
2406
- while (current) {
2407
- if (ts.isFunctionDeclaration(current) || ts.isFunctionExpression(current) || ts.isArrowFunction(current) || ts.isMethodDeclaration(current)) {
2408
- return current;
2409
- }
2410
- current = current.parent;
2411
- }
2412
- return yield* fail(new CannotFindAncestorConvertibleDeclarationError(node));
2413
- });
2414
- var CannotInferReturnTypeFromEmptyBody = class {
2415
- constructor(declaration) {
2416
- this.declaration = declaration;
2417
- }
2418
- _tag = "@effect/language-service/CannotInferReturnTypeFromEmptyBody";
2419
- };
2420
- var CannotInferReturnType = class {
2421
- constructor(declaration) {
2422
- this.declaration = declaration;
2423
- }
2424
- _tag = "@effect/language-service/CannotInferReturnType";
2425
- };
2426
- var getInferredReturnType = fn("TypeCheckerApi.getInferredReturnType")(function* (declaration) {
2427
- const typeChecker = yield* service(TypeCheckerApi);
2428
- if (!declaration.body) {
2429
- return yield* fail(
2430
- new CannotInferReturnTypeFromEmptyBody(declaration)
2431
- );
2432
- }
2433
- let returnType;
2434
- if (typeChecker.isImplementationOfOverload(declaration)) {
2435
- const signatures = typeChecker.getTypeAtLocation(declaration).getCallSignatures();
2436
- if (signatures.length > 1) {
2437
- returnType = typeChecker.getUnionType(
2438
- signatures.map((s) => s.getReturnType()).filter((_) => !!_)
2439
- );
2440
- }
2441
- }
2442
- if (!returnType) {
2443
- const signature = typeChecker.getSignatureFromDeclaration(declaration);
2444
- if (signature) {
2445
- const typePredicate = typeChecker.getTypePredicateOfSignature(signature);
2446
- if (typePredicate && typePredicate.type) {
2447
- return typePredicate.type;
2448
- } else {
2449
- returnType = typeChecker.getReturnTypeOfSignature(signature);
2450
- }
2451
- }
2452
- }
2453
- if (!returnType) {
2454
- return yield* fail(
2455
- new CannotInferReturnType(declaration)
2456
- );
2457
- }
2458
- return returnType;
2459
- });
2460
- var expectedAndRealType = fn("TypeCheckerApi.expectedAndRealType")(function* (sourceFile) {
2461
- const cache = yield* service(TypeCheckerApiCache);
2462
- const resultCached = cache.expectedAndRealType.get(sourceFile);
2463
- if (resultCached) return resultCached;
2464
- const typeChecker = yield* service(TypeCheckerApi);
2465
- const ts = yield* service(TypeScriptApi);
2466
- const result = [];
2467
- const nodeToVisit = [sourceFile];
2468
- const appendNodeToVisit = (node) => {
2469
- nodeToVisit.push(node);
2470
- return void 0;
2471
- };
2472
- while (nodeToVisit.length > 0) {
2473
- const node = nodeToVisit.shift();
2474
- if (ts.isVariableDeclaration(node) && node.initializer) {
2475
- const expectedType = typeChecker.getTypeAtLocation(node.name);
2476
- const realType = typeChecker.getTypeAtLocation(node.initializer);
2477
- result.push([node.name, expectedType, node.initializer, realType]);
2478
- appendNodeToVisit(node.initializer);
2479
- continue;
2480
- } else if (ts.isCallExpression(node)) {
2481
- const resolvedSignature = typeChecker.getResolvedSignature(node);
2482
- if (resolvedSignature) {
2483
- resolvedSignature.getParameters().map((parameter, index) => {
2484
- const expectedType = typeChecker.getTypeOfSymbolAtLocation(parameter, node);
2485
- const realType = typeChecker.getTypeAtLocation(node.arguments[index]);
2486
- result.push([
2487
- node.arguments[index],
2488
- expectedType,
2489
- node.arguments[index],
2490
- realType
2491
- ]);
2492
- });
2493
- }
2494
- ts.forEachChild(node, appendNodeToVisit);
2495
- continue;
2496
- } else if (ts.isIdentifier(node) || ts.isStringLiteral(node) || ts.isNumericLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node)) {
2497
- const parent = node.parent;
2498
- if (ts.isObjectLiteralElement(parent)) {
2499
- if (ts.isObjectLiteralExpression(parent.parent) && parent.name === node) {
2500
- const type = typeChecker.getContextualType(parent.parent);
2501
- if (type) {
2502
- const symbol3 = typeChecker.getPropertyOfType(type, node.text);
2503
- if (symbol3) {
2504
- const expectedType = typeChecker.getTypeOfSymbolAtLocation(symbol3, node);
2505
- const realType = typeChecker.getTypeAtLocation(node);
2506
- result.push([node, expectedType, node, realType]);
2507
- }
2508
- }
2509
- }
2510
- }
2511
- ts.forEachChild(node, appendNodeToVisit);
2512
- continue;
2513
- } else if (ts.isBinaryExpression(node) && node.operatorToken.kind === ts.SyntaxKind.EqualsToken) {
2514
- const expectedType = typeChecker.getTypeAtLocation(node.left);
2515
- const realType = typeChecker.getTypeAtLocation(node.right);
2516
- result.push([node.left, expectedType, node.right, realType]);
2517
- appendNodeToVisit(node.right);
2518
- continue;
2519
- } else if (ts.isReturnStatement(node) && node.expression) {
2520
- const parentDeclaration = yield* option(getAncestorConvertibleDeclaration(node));
2521
- if (isSome2(parentDeclaration)) {
2522
- const expectedType = yield* option(getInferredReturnType(parentDeclaration.value));
2523
- const realType = typeChecker.getTypeAtLocation(node.expression);
2524
- if (isSome2(expectedType)) {
2525
- result.push([node, expectedType.value, node, realType]);
2526
- }
2527
- }
2528
- ts.forEachChild(node, appendNodeToVisit);
2529
- continue;
2530
- } else if (ts.isArrowFunction(node) && (node.typeParameters || []).length === 0 && ts.isExpression(node.body)) {
2531
- const body = node.body;
2532
- const expectedType = typeChecker.getContextualType(body);
2533
- const realType = typeChecker.getTypeAtLocation(body);
2534
- if (expectedType) {
2535
- result.push([body, expectedType, body, realType]);
2536
- }
2537
- ts.forEachChild(body, appendNodeToVisit);
2538
- continue;
2539
- } else if (ts.isArrowFunction(node) && (node.typeParameters || []).length > 0 && ts.isExpression(node.body)) {
2540
- const body = node.body;
2541
- const expectedType = yield* option(getInferredReturnType(node));
2542
- const realType = typeChecker.getTypeAtLocation(body);
2543
- if (isSome2(expectedType)) {
2544
- result.push([body, expectedType.value, body, realType]);
2545
- }
2546
- ts.forEachChild(body, appendNodeToVisit);
2547
- continue;
2548
- } else if (ts.isSatisfiesExpression(node)) {
2549
- const expectedType = typeChecker.getTypeAtLocation(node.type);
2550
- const realType = typeChecker.getTypeAtLocation(node.expression);
2551
- result.push([node.expression, expectedType, node.expression, realType]);
2552
- appendNodeToVisit(node.expression);
2553
- continue;
2554
- }
2555
- ts.forEachChild(node, appendNodeToVisit);
2556
- }
2557
- cache.expectedAndRealType.set(sourceFile, result);
2558
- return result;
2559
- });
2560
- var appendToUniqueTypesMap = fn(
2561
- "TypeCheckerApi.appendToUniqueTypesMap"
2562
- )(
2563
- function* (memory, initialType, excludeNever) {
2564
- const ts = yield* service(TypeScriptApi);
2565
- const typeChecker = yield* service(TypeCheckerApi);
2566
- const newIndexes = /* @__PURE__ */ new Set();
2567
- const knownIndexes = /* @__PURE__ */ new Set();
2568
- let toTest = [initialType];
2569
- while (toTest.length > 0) {
2570
- const type = toTest.pop();
2571
- if (!type) break;
2572
- if (excludeNever && type.flags & ts.TypeFlags.Never) {
2573
- continue;
2574
- }
2575
- if (type.isUnion()) {
2576
- toTest = toTest.concat(type.types);
2577
- } else {
2578
- const foundMatch = [];
2579
- for (const [typeId, knownType] of memory.entries()) {
2580
- const areSame = typeChecker.isTypeAssignableTo(knownType, type) && typeChecker.isTypeAssignableTo(type, knownType);
2581
- if (areSame) {
2582
- foundMatch.push(typeId);
2583
- break;
2584
- }
2585
- }
2586
- if (foundMatch.length === 0) {
2587
- const newId = "t" + (memory.size + 1);
2588
- memory.set(newId, type);
2589
- newIndexes.add(newId);
2590
- } else {
2591
- knownIndexes.add(foundMatch[0]);
2592
- }
2593
- }
2594
- }
2595
- return {
2596
- newIndexes,
2597
- knownIndexes,
2598
- allIndexes: pipe(
2599
- fromIterable(newIndexes),
2600
- appendAll(fromIterable(knownIndexes))
2601
- )
2602
- };
2603
- }
2604
- );
2605
- function makeResolveExternalModuleName(typeChecker) {
2606
- if (!(hasProperty(typeChecker, "resolveExternalModuleName") && isFunction(typeChecker.resolveExternalModuleName))) {
2607
- return;
2608
- }
2609
- const _internal = typeChecker.resolveExternalModuleName;
2610
- return (moduleSpecifier) => {
2611
- return _internal(moduleSpecifier);
2612
- };
2613
- }
2614
-
2615
- // src/completions/genFunctionStar.ts
2616
- var genFunctionStar = createCompletion({
2617
- name: "genFunctionStar",
2618
- apply: fn("genFunctionStar")(function* (sourceFile, position) {
2619
- const ts = yield* service(TypeScriptApi);
2620
- const typeChecker = yield* service(TypeCheckerApi);
2621
- const maybeInfos = yield* option(
2622
- parseAccessedExpressionForCompletion(sourceFile, position)
2623
- );
2624
- if (isNone2(maybeInfos)) return [];
2625
- const { accessedObject } = maybeInfos.value;
2626
- const type = typeChecker.getTypeAtLocation(accessedObject);
2627
- const genMemberSymbol = type.getProperty("gen");
2628
- if (!genMemberSymbol) return [];
2629
- const genType = typeChecker.getTypeOfSymbolAtLocation(genMemberSymbol, accessedObject);
2630
- if (genType.getCallSignatures().length === 0) return [];
2631
- const span = ts.createTextSpan(
2632
- accessedObject.end + 1,
2633
- Math.max(0, position - accessedObject.end - 1)
2634
- );
2635
- return [{
2636
- name: `gen(function*(){})`,
2637
- kind: ts.ScriptElementKind.constElement,
2638
- insertText: `gen(function*(){${"${0}"}})`,
2639
- replacementSpan: span,
2640
- isSnippet: true
2641
- }];
2642
- })
2643
- });
2644
-
2645
- // src/completions/rpcMakeClasses.ts
2646
- var rpcMakeClasses = createCompletion({
2647
- name: "rpcMakeClasses",
2648
- apply: fn("rpcMakeClasses")(function* (sourceFile, position) {
2649
- const ts = yield* service(TypeScriptApi);
2650
- const maybeInfos = yield* option(
2651
- parseDataForExtendsClassCompletion(sourceFile, position)
2652
- );
2653
- if (isNone2(maybeInfos)) return [];
2654
- const { accessedObject, className, replacementSpan } = maybeInfos.value;
2655
- const rpcName = yield* option(
2656
- findImportedModuleIdentifierByPackageAndNameOrBarrel(
2657
- sourceFile,
2658
- "@effect/rpc",
2659
- "Rpc"
2660
- )
2661
- );
2662
- const rpcIdentifier = match(rpcName, {
2663
- onNone: () => "Rpc",
2664
- onSome: (_) => _.text
2665
- });
2666
- if (rpcIdentifier !== accessedObject.text) return [];
2667
- const name = className.text;
2668
- return [{
2669
- name: `make("${name}")`,
2670
- kind: ts.ScriptElementKind.constElement,
2671
- insertText: `${rpcIdentifier}.make("${name}", {${"${0}"}}) {}`,
2672
- replacementSpan,
2673
- isSnippet: true
2674
- }];
2675
- })
2676
- });
2677
-
2678
- // src/completions.ts
2679
- var completions = [
2680
- effectSchemaSelfInClasses,
2681
- effectSelfInClasses,
2682
- contextSelfInClasses,
2683
- rpcMakeClasses,
2684
- genFunctionStar,
2685
- fnFunctionStar,
2686
- effectDataClasses
2687
- ];
2688
-
2689
- // src/completions/middlewareNamespaceImports.ts
2690
- var importablePackagesMetadataCache = /* @__PURE__ */ new Map();
2691
- var makeImportablePackagesMetadata = fn("makeImportablePackagesMetadata")(function* (sourceFile) {
2692
- const ts = yield* service(TypeScriptApi);
2693
- const program = yield* service(TypeScriptProgram);
2694
- const languageServicePluginOptions = yield* service(LanguageServicePluginOptions);
2695
- const host = program;
2696
- const namespaceByFileName = /* @__PURE__ */ new Map();
2697
- const excludedByFileName = /* @__PURE__ */ new Map();
2698
- const unbarreledModulePathByFileName = /* @__PURE__ */ new Map();
2699
- for (const packageName of languageServicePluginOptions.namespaceImportPackages) {
2700
- const barrelModule = ts.resolveModuleName(packageName, sourceFile.fileName, program.getCompilerOptions(), host);
2701
- if (barrelModule.resolvedModule) {
2702
- const barrelPath = barrelModule.resolvedModule.resolvedFileName;
2703
- const barrelSource = program.getSourceFile(barrelPath) || ts.createSourceFile(barrelPath, host.readFile(barrelPath) || "", sourceFile.languageVersion, true);
2704
- if (barrelSource) {
2705
- for (const statement of barrelSource.statements) {
2706
- if (ts.isExportDeclaration(statement)) {
2707
- const exportClause = statement.exportClause;
2708
- const moduleSpecifier = statement.moduleSpecifier;
2709
- if (moduleSpecifier && ts.isStringLiteral(moduleSpecifier)) {
2710
- const unbarreledModulePathResolved = ts.resolveModuleName(
2711
- moduleSpecifier.text,
2712
- barrelSource.fileName,
2713
- program.getCompilerOptions(),
2714
- host
2715
- );
2716
- if (unbarreledModulePathResolved.resolvedModule) {
2717
- const unbarreledModulePath = unbarreledModulePathResolved.resolvedModule.resolvedFileName;
2718
- if (exportClause && ts.isNamespaceExport(exportClause) && ts.isIdentifier(exportClause.name)) {
2719
- namespaceByFileName.set(unbarreledModulePath, exportClause.name.text);
2720
- const existingUnbarreledModulePath = unbarreledModulePathByFileName.get(barrelSource.fileName) || [];
2721
- existingUnbarreledModulePath.push([exportClause.name.text, unbarreledModulePath]);
2722
- unbarreledModulePathByFileName.set(barrelSource.fileName, existingUnbarreledModulePath);
2723
- }
2724
- if (exportClause && ts.isNamedExports(exportClause)) {
2725
- for (const element of exportClause.elements) {
2726
- if (!ts.isIdentifier(element.name)) continue;
2727
- const methodName = element.name.text;
2728
- const excludedMethods = excludedByFileName.get(methodName) || [];
2729
- excludedMethods.push(unbarreledModulePath);
2730
- excludedByFileName.set(methodName, excludedMethods);
2731
- }
2732
- }
2733
- }
2734
- }
2735
- }
2736
- }
2737
- }
2738
- }
2739
- }
2740
- return {
2741
- getImportNamespaceByFileName: (fileName) => namespaceByFileName.get(fileName),
2742
- isExcludedFromNamespaceImport: (fileName, exportName) => (excludedByFileName.get(exportName) || []).includes(fileName),
2743
- getUnbarreledModulePath: (fileName, exportName) => unbarreledModulePathByFileName.get(fileName)?.find(([name]) => name === exportName)?.[1]
2744
- };
2745
- });
2746
- var appendEffectCompletionEntryData = fn("collectNamespaceImports")(
2747
- function* (sourceFile, applicableCompletions) {
2748
- const languageServicePluginOptions = yield* service(LanguageServicePluginOptions);
2749
- if (languageServicePluginOptions.namespaceImportPackages.length === 0) return applicableCompletions;
2750
- const packagesMetadata = importablePackagesMetadataCache.get(sourceFile.fileName) || (yield* makeImportablePackagesMetadata(sourceFile));
2751
- importablePackagesMetadataCache.set(sourceFile.fileName, packagesMetadata);
2752
- if (applicableCompletions) {
2753
- return {
2754
- ...applicableCompletions,
2755
- entries: applicableCompletions.entries.map((entry) => {
2756
- if (entry.data && entry.data.fileName && !entry.insertText && !entry.filterText && entry.data.exportName && entry.data.moduleSpecifier) {
2757
- const isExcluded = packagesMetadata.isExcludedFromNamespaceImport(
2758
- entry.data.fileName,
2759
- entry.data.exportName
2760
- );
2761
- if (isExcluded) return entry;
2762
- const unbarreledModulePath = packagesMetadata.getUnbarreledModulePath(
2763
- entry.data.fileName,
2764
- entry.data.exportName
2765
- );
2766
- const namespaceName = packagesMetadata.getImportNamespaceByFileName(
2767
- unbarreledModulePath || entry.data.fileName
2768
- );
2769
- if (namespaceName) {
2770
- return {
2771
- ...entry,
2772
- // insertText: unbarreledModulePath ? namespaceName : namespaceName + "." + entry.name,
2773
- // filterText: entry.name,
2774
- data: {
2775
- ...entry.data,
2776
- effectNamespaceName: namespaceName,
2777
- effectUnbarreledModulePath: unbarreledModulePath || "",
2778
- effectReplaceSpan: entry.replacementSpan || applicableCompletions.optionalReplacementSpan
2779
- }
2780
- };
2781
- }
2782
- }
2783
- return entry;
2784
- })
2785
- };
2786
- }
2787
- return applicableCompletions;
2788
- }
2789
- );
2790
- var postprocessCompletionEntryDetails = fn("postprocessCompletionEntryDetails")(
2791
- function* (sourceFile, data, applicableCompletionEntryDetails, formatOptions, preferences, languageServiceHost) {
2792
- const languageServicePluginOptions = yield* service(LanguageServicePluginOptions);
2793
- if (languageServicePluginOptions.namespaceImportPackages.length === 0) return applicableCompletionEntryDetails;
2794
- const ts = yield* service(TypeScriptApi);
2795
- const program = yield* service(TypeScriptProgram);
2796
- const getModuleSpecifier = makeGetModuleSpecifier(ts);
2797
- if (!getModuleSpecifier) return applicableCompletionEntryDetails;
2798
- if (!applicableCompletionEntryDetails) return applicableCompletionEntryDetails;
2799
- if (!data) return applicableCompletionEntryDetails;
2800
- if (!("effectNamespaceName" in data && "effectUnbarreledModulePath" in data && "effectReplaceSpan" in data)) {
2801
- return applicableCompletionEntryDetails;
2802
- }
2803
- const effectReplaceSpan = data.effectReplaceSpan;
2804
- const codeActions = applicableCompletionEntryDetails.codeActions;
2805
- if (codeActions && codeActions.length === 1) {
2806
- const action = codeActions[0];
2807
- if (action.changes.length === 1) {
2808
- const fileTextChanges = action.changes[0];
2809
- if (fileTextChanges.fileName === sourceFile.fileName && fileTextChanges.textChanges.length === 1) {
2810
- const change = fileTextChanges.textChanges[0];
2811
- let hasImportActions = false;
2812
- if (change.newText.trim().toLowerCase().startsWith("import") && change.newText.indexOf(data.exportName) > -1) {
2813
- hasImportActions = true;
2814
- }
2815
- if (!hasImportActions && change.newText.indexOf(data.exportName) > -1) {
2816
- const ancestorNodes = yield* getAncestorNodesInRange(sourceFile, {
2817
- pos: change.span.start,
2818
- end: change.span.start
2819
- });
2820
- const importNodes = ancestorNodes.filter((node) => ts.isImportDeclaration(node));
2821
- hasImportActions = importNodes.length > 0;
2822
- }
2823
- if (!hasImportActions) return applicableCompletionEntryDetails;
2824
- const formatContext = ts.formatting.getFormatContext(
2825
- formatOptions || {},
2826
- languageServiceHost
2827
- );
2828
- const changes = ts.textChanges.ChangeTracker.with(
2829
- {
2830
- formatContext,
2831
- host: languageServiceHost,
2832
- preferences: preferences || {}
2833
- },
2834
- (changeTracker) => {
2835
- const isBarrelRedirect = String(data.effectUnbarreledModulePath).length > 0;
2836
- const moduleSpecifier = isBarrelRedirect ? getModuleSpecifier(
2837
- program.getCompilerOptions(),
2838
- sourceFile,
2839
- sourceFile.fileName,
2840
- String(data.effectUnbarreledModulePath),
2841
- program
2842
- ) : String(data.moduleSpecifier);
2843
- ts.insertImports(
2844
- changeTracker,
2845
- sourceFile,
2846
- ts.factory.createImportDeclaration(
2847
- void 0,
2848
- ts.factory.createImportClause(
2849
- false,
2850
- void 0,
2851
- ts.factory.createNamespaceImport(ts.factory.createIdentifier(String(data.effectNamespaceName)))
2852
- ),
2853
- ts.factory.createStringLiteral(moduleSpecifier)
2854
- ),
2855
- true,
2856
- preferences || {}
2857
- );
2858
- if (!isBarrelRedirect) {
2859
- changeTracker.insertText(
2860
- sourceFile,
2861
- effectReplaceSpan.start,
2862
- String(data.effectNamespaceName) + "."
2863
- );
2864
- }
2865
- }
2866
- );
2867
- return {
2868
- ...applicableCompletionEntryDetails,
2869
- codeActions: [
2870
- {
2871
- description: "Import * as " + data.effectNamespaceName + " from " + data.effectUnbarreledModulePath,
2872
- changes
2873
- }
2874
- ]
2875
- };
2476
+ );
2477
+ const contextTag = cachedBy(
2478
+ fn("TypeParser.contextTag")(function* (type, atLocation) {
2479
+ yield* pipeableType(type, atLocation);
2480
+ const propertiesSymbols = typeChecker.getPropertiesOfType(type).filter(
2481
+ (_) => _.flags & ts.SymbolFlags.Property && !(_.flags & ts.SymbolFlags.Optional)
2482
+ );
2483
+ propertiesSymbols.sort((a, b) => b.name.indexOf("TypeId") - a.name.indexOf("TypeId"));
2484
+ for (const propertySymbol of propertiesSymbols) {
2485
+ const propertyType = typeChecker.getTypeOfSymbolAtLocation(propertySymbol, atLocation);
2486
+ const varianceArgs = yield* option(contextTagVarianceStruct(
2487
+ propertyType,
2488
+ atLocation
2489
+ ));
2490
+ if (isSome2(varianceArgs)) {
2491
+ return varianceArgs.value;
2876
2492
  }
2877
2493
  }
2878
- }
2879
- return applicableCompletionEntryDetails;
2880
- }
2881
- );
2882
-
2883
- // src/diagnostics/duplicatePackage.ts
2884
- var checkedPackagesCache = /* @__PURE__ */ new Map();
2885
- var programResolvedCacheSize = /* @__PURE__ */ new Map();
2886
- var duplicatePackage = createDiagnostic({
2887
- name: "duplicatePackage",
2888
- code: 6,
2889
- severity: "warning",
2890
- apply: fn("duplicatePackage.apply")(function* (sourceFile, report) {
2891
- const program = yield* service(TypeScriptProgram);
2892
- const options = yield* service(LanguageServicePluginOptions);
2893
- if (sourceFile.statements.length < 1) return;
2894
- let resolvedPackages = checkedPackagesCache.get(sourceFile.fileName) || {};
2895
- const newResolvedModuleSize = hasProperty(program, "resolvedModules") && hasProperty(program.resolvedModules, "size") && isNumber(program.resolvedModules.size) ? program.resolvedModules.size : 0;
2896
- const oldResolvedSize = programResolvedCacheSize.get(sourceFile.fileName) || -1;
2897
- if (newResolvedModuleSize !== oldResolvedSize) {
2898
- const seenPackages = /* @__PURE__ */ new Set();
2899
- resolvedPackages = {};
2900
- program.getSourceFiles().map((_) => {
2901
- const packageInfo = parsePackageContentNameAndVersionFromScope(_);
2902
- if (!packageInfo) return;
2903
- const packageNameAndVersion = packageInfo.name + "@" + packageInfo.version;
2904
- if (seenPackages.has(packageNameAndVersion)) return;
2905
- seenPackages.add(packageNameAndVersion);
2906
- if (!(packageInfo.name === "effect" || packageInfo.hasEffectInPeerDependencies)) return;
2907
- if (options.allowedDuplicatedPackages.indexOf(packageInfo.name) > -1) return;
2908
- resolvedPackages[packageInfo.name] = resolvedPackages[packageInfo.name] || {};
2909
- resolvedPackages[packageInfo.name][packageInfo.version] = packageInfo.packageDirectory;
2910
- });
2911
- checkedPackagesCache.set(sourceFile.fileName, resolvedPackages);
2912
- programResolvedCacheSize.set(sourceFile.fileName, newResolvedModuleSize);
2913
- }
2914
- for (const packageName of Object.keys(resolvedPackages)) {
2915
- if (Object.keys(resolvedPackages[packageName]).length > 1) {
2916
- const versions = Object.keys(resolvedPackages[packageName]);
2917
- report({
2918
- node: sourceFile.statements[0],
2919
- messageText: `Package ${packageName} is referenced multiple times with different versions (${versions.join(", ")}) and may cause unexpected type errors.
2920
- Cleanup your dependencies and your package lockfile to avoid multiple instances of this package and reload the project.
2921
- If this is intended set the LSP config "allowedDuplicatedPackages" to ${JSON.stringify(options.allowedDuplicatedPackages.concat([packageName]))}.
2922
-
2923
- ${versions.map((version) => `- found ${version} at ${resolvedPackages[packageName][version]}`).join("\n")}`,
2924
- fixes: []
2925
- });
2494
+ return yield* typeParserIssue("Type has no tag variance struct", type, atLocation);
2495
+ }),
2496
+ "TypeParser.contextTag",
2497
+ (type) => type
2498
+ );
2499
+ const pipeCall = cachedBy(
2500
+ function(node) {
2501
+ if (ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression) && ts.isIdentifier(node.expression.name) && node.expression.name.text === "pipe") {
2502
+ return succeed({ node, subject: node.expression.expression, args: Array.from(node.arguments) });
2926
2503
  }
2927
- }
2928
- })
2929
- });
2504
+ if (ts.isCallExpression(node) && ts.isIdentifier(node.expression) && node.expression.text === "pipe" && node.arguments.length > 0) {
2505
+ const [subject, ...args] = node.arguments;
2506
+ return succeed({ node, subject, args });
2507
+ }
2508
+ return typeParserIssue("Node is not a pipe call", void 0, node);
2509
+ },
2510
+ "TypeParser.pipeCall",
2511
+ (node) => node
2512
+ );
2513
+ return {
2514
+ effectType,
2515
+ strictEffectType,
2516
+ layerType,
2517
+ fiberType,
2518
+ effectSubtype,
2519
+ importedEffectModule,
2520
+ effectGen,
2521
+ effectFnUntracedGen,
2522
+ effectFnGen,
2523
+ unnecessaryEffectGen: unnecessaryEffectGen2,
2524
+ effectSchemaType,
2525
+ contextTag,
2526
+ pipeCall
2527
+ };
2528
+ }
2930
2529
 
2931
2530
  // src/diagnostics/floatingEffect.ts
2932
2531
  var floatingEffect = createDiagnostic({
@@ -2981,8 +2580,226 @@ var genericEffectServices = createDiagnostic({
2981
2580
  severity: "warning",
2982
2581
  apply: fn("genericEffectServices.apply")(function* (sourceFile, report) {
2983
2582
  const ts = yield* service(TypeScriptApi);
2984
- const typeParser = yield* service(TypeParser);
2583
+ const typeParser = yield* service(TypeParser);
2584
+ const typeChecker = yield* service(TypeCheckerApi);
2585
+ const nodeToVisit = [];
2586
+ const appendNodeToVisit = (node) => {
2587
+ nodeToVisit.push(node);
2588
+ return void 0;
2589
+ };
2590
+ ts.forEachChild(sourceFile, appendNodeToVisit);
2591
+ while (nodeToVisit.length > 0) {
2592
+ const node = nodeToVisit.shift();
2593
+ const typesToCheck = [];
2594
+ if (ts.isClassDeclaration(node) && node.name && node.typeParameters && node.heritageClauses) {
2595
+ const classSym = typeChecker.getSymbolAtLocation(node.name);
2596
+ if (classSym) {
2597
+ const type = typeChecker.getTypeOfSymbol(classSym);
2598
+ typesToCheck.push([type, node.name]);
2599
+ }
2600
+ } else {
2601
+ ts.forEachChild(node, appendNodeToVisit);
2602
+ continue;
2603
+ }
2604
+ for (const [type, reportAt] of typesToCheck) {
2605
+ yield* pipe(
2606
+ typeParser.contextTag(type, node),
2607
+ map4(() => {
2608
+ report({
2609
+ node: reportAt,
2610
+ messageText: `Effect Services with type parameters are not supported because they cannot be properly discriminated at runtime, which may cause unexpected behavior.`,
2611
+ fixes: []
2612
+ });
2613
+ }),
2614
+ orElse2(() => sync(() => ts.forEachChild(node, appendNodeToVisit))),
2615
+ ignore
2616
+ );
2617
+ }
2618
+ }
2619
+ })
2620
+ });
2621
+
2622
+ // src/diagnostics/importFromBarrel.ts
2623
+ var importFromBarrel = createDiagnostic({
2624
+ name: "importFromBarrel",
2625
+ code: 12,
2626
+ severity: "off",
2627
+ apply: fn("importFromBarrel.apply")(function* (sourceFile, report) {
2628
+ const languageServicePluginOptions = yield* service(LanguageServicePluginOptions);
2629
+ if (languageServicePluginOptions.namespaceImportPackages.length === 0) return;
2630
+ const ts = yield* service(TypeScriptApi);
2631
+ const typeChecker = yield* service(TypeCheckerApi);
2632
+ const program = yield* service(TypeScriptProgram);
2633
+ const isImportedFromBarrelExport = (element, languageServicePluginOptions2) => {
2634
+ const getModuleSpecifier = makeGetModuleSpecifier(ts);
2635
+ const resolveExternalModuleName = makeResolveExternalModuleName(typeChecker);
2636
+ if (!(getModuleSpecifier && resolveExternalModuleName)) return;
2637
+ const importDeclaration = ts.findAncestor(element, (node) => ts.isImportDeclaration(node));
2638
+ if (!importDeclaration) return;
2639
+ if (!ts.isStringLiteral(importDeclaration.moduleSpecifier)) return;
2640
+ const importClause = importDeclaration.importClause;
2641
+ if (!importClause) return;
2642
+ const namedBindings = importClause.namedBindings;
2643
+ if (!namedBindings) return;
2644
+ if (!ts.isNamedImports(namedBindings)) return;
2645
+ const barrelModuleName = importDeclaration.moduleSpecifier.text;
2646
+ if (languageServicePluginOptions2.namespaceImportPackages.indexOf(barrelModuleName.toLowerCase()) === -1) return;
2647
+ const moduleSymbol = resolveExternalModuleName(importDeclaration.moduleSpecifier);
2648
+ if (!moduleSymbol) return;
2649
+ if (!moduleSymbol.exports) return;
2650
+ const sourceFile2 = importDeclaration.getSourceFile();
2651
+ const nodeForSymbol = element.propertyName || element.name;
2652
+ if (!ts.isIdentifier(nodeForSymbol)) return;
2653
+ const importedName = nodeForSymbol.text;
2654
+ const reexportedSymbol = moduleSymbol.exports.get(ts.escapeLeadingUnderscores(importedName));
2655
+ if (!reexportedSymbol) return;
2656
+ if (!(reexportedSymbol.declarations && reexportedSymbol.declarations.length === 1)) return;
2657
+ const namespaceExport = reexportedSymbol.declarations[0];
2658
+ if (!ts.isNamespaceExport(namespaceExport)) return;
2659
+ const exportDeclaration = namespaceExport.parent;
2660
+ if (!ts.isExportDeclaration(exportDeclaration)) return;
2661
+ if (!exportDeclaration.moduleSpecifier) return;
2662
+ const originalModuleSymbol = resolveExternalModuleName(exportDeclaration.moduleSpecifier);
2663
+ if (!originalModuleSymbol) return;
2664
+ if (!originalModuleSymbol.valueDeclaration) return;
2665
+ const originalSourceFile = originalModuleSymbol.valueDeclaration.getSourceFile();
2666
+ const unbarrelledFileName = getModuleSpecifier(
2667
+ program.getCompilerOptions(),
2668
+ sourceFile2,
2669
+ sourceFile2.fileName,
2670
+ originalSourceFile.fileName,
2671
+ program
2672
+ );
2673
+ if (unbarrelledFileName.toLowerCase().indexOf(barrelModuleName.toLowerCase() + "/") === -1) return;
2674
+ return { unbarrelledFileName, importedName, barrelModuleName, importClause, namedBindings, importDeclaration };
2675
+ };
2676
+ const nodeToVisit = [];
2677
+ const appendNodeToVisit = (node) => {
2678
+ nodeToVisit.push(node);
2679
+ return void 0;
2680
+ };
2681
+ ts.forEachChild(sourceFile, appendNodeToVisit);
2682
+ while (nodeToVisit.length > 0) {
2683
+ const node = nodeToVisit.shift();
2684
+ const parent = node.parent;
2685
+ if (!(ts.isImportSpecifier(node) && ts.isNamedImports(parent))) {
2686
+ ts.forEachChild(node, appendNodeToVisit);
2687
+ continue;
2688
+ }
2689
+ const result = isImportedFromBarrelExport(node, languageServicePluginOptions);
2690
+ if (!result) continue;
2691
+ const { barrelModuleName, importClause, importDeclaration, importedName, namedBindings, unbarrelledFileName } = result;
2692
+ report({
2693
+ node,
2694
+ messageText: `Importing from barrel module ${barrelModuleName} is not allowed.`,
2695
+ fixes: [
2696
+ {
2697
+ fixName: "replaceWithUnbarrelledImport",
2698
+ description: `Import * as ${importedName} from ${unbarrelledFileName}`,
2699
+ apply: gen(function* () {
2700
+ const changeTracker = yield* service(ChangeTracker);
2701
+ const newImport = ts.factory.createImportDeclaration(
2702
+ void 0,
2703
+ ts.factory.createImportClause(
2704
+ importClause.isTypeOnly || node.isTypeOnly,
2705
+ void 0,
2706
+ ts.factory.createNamespaceImport(ts.factory.createIdentifier(importedName))
2707
+ ),
2708
+ ts.factory.createStringLiteral(unbarrelledFileName)
2709
+ );
2710
+ if (namedBindings.elements.length === 1) {
2711
+ changeTracker.replaceNode(
2712
+ sourceFile,
2713
+ importDeclaration,
2714
+ newImport
2715
+ );
2716
+ } else {
2717
+ changeTracker.insertNodeAfter(sourceFile, importDeclaration, newImport);
2718
+ changeTracker.replaceNode(
2719
+ sourceFile,
2720
+ namedBindings,
2721
+ ts.factory.updateNamedImports(
2722
+ namedBindings,
2723
+ namedBindings.elements.filter((e) => e !== node)
2724
+ )
2725
+ );
2726
+ }
2727
+ })
2728
+ }
2729
+ ]
2730
+ });
2731
+ }
2732
+ })
2733
+ });
2734
+
2735
+ // src/diagnostics/leakingRequirements.ts
2736
+ var leakingRequirements = createDiagnostic({
2737
+ name: "leakingRequirements",
2738
+ code: 8,
2739
+ severity: "suggestion",
2740
+ apply: fn("leakingRequirements.apply")(function* (sourceFile, report) {
2741
+ const ts = yield* service(TypeScriptApi);
2985
2742
  const typeChecker = yield* service(TypeCheckerApi);
2743
+ const typeParser = yield* service(TypeParser);
2744
+ const typeOrder = yield* deterministicTypeOrder;
2745
+ const parseLeakedRequirements = cachedBy(
2746
+ fn("leakingServices.checkServiceLeaking")(
2747
+ function* (service2, atLocation) {
2748
+ const properties = typeChecker.getPropertiesOfType(service2);
2749
+ if (properties.length < 1) return [];
2750
+ const memory = /* @__PURE__ */ new Map();
2751
+ let sharedRequirementsKeys = void 0;
2752
+ let effectMembers = 0;
2753
+ for (const property of properties) {
2754
+ const servicePropertyType = typeChecker.getTypeOfSymbolAtLocation(property, atLocation);
2755
+ let effectContextType = void 0;
2756
+ yield* pipe(
2757
+ typeParser.effectType(servicePropertyType, atLocation),
2758
+ map4((_) => effectContextType = _.R),
2759
+ orElse2(() => {
2760
+ const servicePropertyCallSignatures = servicePropertyType.getCallSignatures();
2761
+ if (servicePropertyCallSignatures.length === 1) {
2762
+ return pipe(
2763
+ typeParser.effectType(servicePropertyCallSignatures[0].getReturnType(), atLocation),
2764
+ map4((_) => {
2765
+ effectContextType = _.R;
2766
+ })
2767
+ );
2768
+ }
2769
+ return void_;
2770
+ }),
2771
+ ignore
2772
+ );
2773
+ if (effectContextType) {
2774
+ effectMembers++;
2775
+ const { allIndexes } = yield* appendToUniqueTypesMap(memory, effectContextType, true);
2776
+ if (!sharedRequirementsKeys) {
2777
+ sharedRequirementsKeys = allIndexes;
2778
+ } else {
2779
+ sharedRequirementsKeys = intersection(sharedRequirementsKeys, allIndexes);
2780
+ if (sharedRequirementsKeys.length === 0) return [];
2781
+ }
2782
+ }
2783
+ }
2784
+ if (sharedRequirementsKeys && sharedRequirementsKeys.length > 0 && effectMembers >= 2) {
2785
+ return sharedRequirementsKeys.map((key) => memory.get(key));
2786
+ }
2787
+ return [];
2788
+ }
2789
+ ),
2790
+ "leakingServices.checkServiceLeaking",
2791
+ (_, service2) => service2
2792
+ );
2793
+ function reportLeakingRequirements(node, requirements) {
2794
+ if (requirements.length === 0) return;
2795
+ report({
2796
+ node,
2797
+ messageText: `This Service is leaking the ${requirements.map((_) => typeChecker.typeToString(_)).join(" | ")} requirement.
2798
+ If these requirements cannot be cached and are expected to be provided per method invocation (e.g. HttpServerRequest), you can safely disable this diagnostic for this line through quickfixes.
2799
+ More info at https://effect.website/docs/requirements-management/layers/#avoiding-requirement-leakage`,
2800
+ fixes: []
2801
+ });
2802
+ }
2986
2803
  const nodeToVisit = [];
2987
2804
  const appendNodeToVisit = (node) => {
2988
2805
  nodeToVisit.push(node);
@@ -2992,7 +2809,9 @@ var genericEffectServices = createDiagnostic({
2992
2809
  while (nodeToVisit.length > 0) {
2993
2810
  const node = nodeToVisit.shift();
2994
2811
  const typesToCheck = [];
2995
- if (ts.isClassDeclaration(node) && node.name && node.typeParameters && node.heritageClauses) {
2812
+ if (ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression) && ts.isIdentifier(node.expression.name) && node.expression.name.text === "GenericTag") {
2813
+ typesToCheck.push([typeChecker.getTypeAtLocation(node), node]);
2814
+ } else if (ts.isClassDeclaration(node) && node.name && node.heritageClauses) {
2996
2815
  const classSym = typeChecker.getSymbolAtLocation(node.name);
2997
2816
  if (classSym) {
2998
2817
  const type = typeChecker.getTypeOfSymbol(classSym);
@@ -3005,13 +2824,12 @@ var genericEffectServices = createDiagnostic({
3005
2824
  for (const [type, reportAt] of typesToCheck) {
3006
2825
  yield* pipe(
3007
2826
  typeParser.contextTag(type, node),
3008
- map4(() => {
3009
- report({
3010
- node: reportAt,
3011
- messageText: `Effect Services with type parameters are not supported because they cannot be properly discriminated at runtime, which may cause unexpected behavior.`,
3012
- fixes: []
3013
- });
3014
- }),
2827
+ flatMap2(
2828
+ ({ Service }) => pipe(
2829
+ parseLeakedRequirements(Service, node),
2830
+ map4((requirements) => reportLeakingRequirements(reportAt, sort(requirements, typeOrder)))
2831
+ )
2832
+ ),
3015
2833
  orElse2(() => sync(() => ts.forEachChild(node, appendNodeToVisit))),
3016
2834
  ignore
3017
2835
  );
@@ -3020,60 +2838,176 @@ var genericEffectServices = createDiagnostic({
3020
2838
  })
3021
2839
  });
3022
2840
 
3023
- // src/diagnostics/importFromBarrel.ts
3024
- var importFromBarrel = createDiagnostic({
3025
- name: "importFromBarrel",
3026
- code: 12,
3027
- severity: "off",
3028
- apply: fn("importFromBarrel.apply")(function* (sourceFile, report) {
3029
- const languageServicePluginOptions = yield* service(LanguageServicePluginOptions);
3030
- if (languageServicePluginOptions.namespaceImportPackages.length === 0) return;
2841
+ // src/diagnostics/missingEffectContext.ts
2842
+ var missingEffectContext = createDiagnostic({
2843
+ name: "missingEffectContext",
2844
+ code: 1,
2845
+ severity: "error",
2846
+ apply: fn("missingEffectContext.apply")(function* (sourceFile, report) {
2847
+ const typeChecker = yield* service(TypeCheckerApi);
2848
+ const typeParser = yield* service(TypeParser);
2849
+ const typeOrder = yield* deterministicTypeOrder;
2850
+ const checkForMissingContextTypes = (node, expectedType, valueNode, realType) => pipe(
2851
+ all(
2852
+ typeParser.effectType(expectedType, node),
2853
+ typeParser.effectType(realType, valueNode)
2854
+ ),
2855
+ flatMap2(
2856
+ ([expectedEffect, realEffect]) => getMissingTypeEntriesInTargetType(
2857
+ realEffect.R,
2858
+ expectedEffect.R
2859
+ )
2860
+ )
2861
+ );
2862
+ const sortTypes = sort(typeOrder);
2863
+ const entries = yield* expectedAndRealType(sourceFile);
2864
+ for (const [node, expectedType, valueNode, realType] of entries) {
2865
+ if (expectedType !== realType) {
2866
+ yield* pipe(
2867
+ checkForMissingContextTypes(
2868
+ node,
2869
+ expectedType,
2870
+ valueNode,
2871
+ realType
2872
+ ),
2873
+ map4(
2874
+ (missingTypes) => missingTypes.length > 0 ? report(
2875
+ {
2876
+ node,
2877
+ messageText: `Missing '${sortTypes(missingTypes).map((_) => typeChecker.typeToString(_)).join(" | ")}' in the expected Effect context.`,
2878
+ fixes: []
2879
+ }
2880
+ ) : void 0
2881
+ ),
2882
+ ignore
2883
+ );
2884
+ }
2885
+ }
2886
+ })
2887
+ });
2888
+
2889
+ // src/diagnostics/missingEffectError.ts
2890
+ var missingEffectError = createDiagnostic({
2891
+ name: "missingEffectError",
2892
+ code: 1,
2893
+ severity: "error",
2894
+ apply: fn("missingEffectError.apply")(function* (sourceFile, report) {
2895
+ const typeChecker = yield* service(TypeCheckerApi);
2896
+ const typeParser = yield* service(TypeParser);
2897
+ const typeOrder = yield* deterministicTypeOrder;
2898
+ const checkForMissingErrorTypes = (node, expectedType, valueNode, realType) => pipe(
2899
+ all(
2900
+ typeParser.effectType(expectedType, node),
2901
+ typeParser.effectType(realType, valueNode)
2902
+ ),
2903
+ flatMap2(
2904
+ ([expectedEffect, realEffect]) => getMissingTypeEntriesInTargetType(
2905
+ realEffect.E,
2906
+ expectedEffect.E
2907
+ )
2908
+ )
2909
+ );
2910
+ const sortTypes = sort(typeOrder);
2911
+ const entries = yield* expectedAndRealType(sourceFile);
2912
+ for (const [node, expectedType, valueNode, realType] of entries) {
2913
+ if (expectedType !== realType) {
2914
+ yield* pipe(
2915
+ checkForMissingErrorTypes(
2916
+ node,
2917
+ expectedType,
2918
+ valueNode,
2919
+ realType
2920
+ ),
2921
+ map4(
2922
+ (missingTypes) => missingTypes.length > 0 ? report(
2923
+ {
2924
+ node,
2925
+ messageText: `Missing '${sortTypes(missingTypes).map((_) => typeChecker.typeToString(_)).join(" | ")}' in the expected Effect errors.`,
2926
+ fixes: []
2927
+ }
2928
+ ) : void 0
2929
+ ),
2930
+ ignore
2931
+ );
2932
+ }
2933
+ }
2934
+ })
2935
+ });
2936
+
2937
+ // src/diagnostics/missingReturnYieldStar.ts
2938
+ var missingReturnYieldStar = createDiagnostic({
2939
+ name: "missingReturnYieldStar",
2940
+ code: 7,
2941
+ severity: "error",
2942
+ apply: fn("missingReturnYieldStar.apply")(function* (sourceFile, report) {
3031
2943
  const ts = yield* service(TypeScriptApi);
3032
2944
  const typeChecker = yield* service(TypeCheckerApi);
3033
- const program = yield* service(TypeScriptProgram);
3034
- const isImportedFromBarrelExport = (element, languageServicePluginOptions2) => {
3035
- const getModuleSpecifier = makeGetModuleSpecifier(ts);
3036
- const resolveExternalModuleName = makeResolveExternalModuleName(typeChecker);
3037
- if (!(getModuleSpecifier && resolveExternalModuleName)) return;
3038
- const importDeclaration = ts.findAncestor(element, (node) => ts.isImportDeclaration(node));
3039
- if (!importDeclaration) return;
3040
- if (!ts.isStringLiteral(importDeclaration.moduleSpecifier)) return;
3041
- const importClause = importDeclaration.importClause;
3042
- if (!importClause) return;
3043
- const namedBindings = importClause.namedBindings;
3044
- if (!namedBindings) return;
3045
- if (!ts.isNamedImports(namedBindings)) return;
3046
- const barrelModuleName = importDeclaration.moduleSpecifier.text;
3047
- if (languageServicePluginOptions2.namespaceImportPackages.indexOf(barrelModuleName.toLowerCase()) === -1) return;
3048
- const moduleSymbol = resolveExternalModuleName(importDeclaration.moduleSpecifier);
3049
- if (!moduleSymbol) return;
3050
- if (!moduleSymbol.exports) return;
3051
- const sourceFile2 = importDeclaration.getSourceFile();
3052
- const nodeForSymbol = element.propertyName || element.name;
3053
- if (!ts.isIdentifier(nodeForSymbol)) return;
3054
- const importedName = nodeForSymbol.text;
3055
- const reexportedSymbol = moduleSymbol.exports.get(ts.escapeLeadingUnderscores(importedName));
3056
- if (!reexportedSymbol) return;
3057
- if (!(reexportedSymbol.declarations && reexportedSymbol.declarations.length === 1)) return;
3058
- const namespaceExport = reexportedSymbol.declarations[0];
3059
- if (!ts.isNamespaceExport(namespaceExport)) return;
3060
- const exportDeclaration = namespaceExport.parent;
3061
- if (!ts.isExportDeclaration(exportDeclaration)) return;
3062
- if (!exportDeclaration.moduleSpecifier) return;
3063
- const originalModuleSymbol = resolveExternalModuleName(exportDeclaration.moduleSpecifier);
3064
- if (!originalModuleSymbol) return;
3065
- if (!originalModuleSymbol.valueDeclaration) return;
3066
- const originalSourceFile = originalModuleSymbol.valueDeclaration.getSourceFile();
3067
- const unbarrelledFileName = getModuleSpecifier(
3068
- program.getCompilerOptions(),
3069
- sourceFile2,
3070
- sourceFile2.fileName,
3071
- originalSourceFile.fileName,
3072
- program
3073
- );
3074
- if (unbarrelledFileName.toLowerCase().indexOf(barrelModuleName.toLowerCase() + "/") === -1) return;
3075
- return { unbarrelledFileName, importedName, barrelModuleName, importClause, namedBindings, importDeclaration };
2945
+ const typeParser = yield* service(TypeParser);
2946
+ const nodeToVisit = [];
2947
+ const appendNodeToVisit = (node) => {
2948
+ nodeToVisit.push(node);
2949
+ return void 0;
3076
2950
  };
2951
+ ts.forEachChild(sourceFile, appendNodeToVisit);
2952
+ while (nodeToVisit.length > 0) {
2953
+ const node = nodeToVisit.shift();
2954
+ ts.forEachChild(node, appendNodeToVisit);
2955
+ if (ts.isYieldExpression(node) && node.expression && node.asteriskToken) {
2956
+ const type = typeChecker.getTypeAtLocation(node.expression);
2957
+ const maybeEffect = yield* option(typeParser.effectType(type, node.expression));
2958
+ if (isSome2(maybeEffect) && maybeEffect.value.A.flags & ts.TypeFlags.Never) {
2959
+ const generatorFunctionOrReturnStatement = ts.findAncestor(
2960
+ node,
2961
+ (_) => ts.isFunctionExpression(_) || ts.isFunctionDeclaration(_) || ts.isMethodDeclaration(_) || ts.isReturnStatement(_)
2962
+ );
2963
+ if (generatorFunctionOrReturnStatement && !ts.isReturnStatement(generatorFunctionOrReturnStatement)) {
2964
+ if (generatorFunctionOrReturnStatement && generatorFunctionOrReturnStatement.parent) {
2965
+ const effectGenNode = generatorFunctionOrReturnStatement.parent;
2966
+ const effectGenLike = yield* pipe(
2967
+ typeParser.effectGen(effectGenNode),
2968
+ orElse2(() => typeParser.effectFnUntracedGen(effectGenNode)),
2969
+ orElse2(() => typeParser.effectFnGen(effectGenNode)),
2970
+ option
2971
+ );
2972
+ if (isSome2(effectGenLike)) {
2973
+ const fix = node.expression ? [{
2974
+ fixName: "missingReturnYieldStar_fix",
2975
+ description: "Add return statement",
2976
+ apply: gen(function* () {
2977
+ const changeTracker = yield* service(ChangeTracker);
2978
+ changeTracker.replaceNode(
2979
+ sourceFile,
2980
+ node,
2981
+ ts.factory.createReturnStatement(
2982
+ node
2983
+ )
2984
+ );
2985
+ })
2986
+ }] : [];
2987
+ report({
2988
+ node,
2989
+ messageText: `Yielded Effect never succeeds, so it is best to use a 'return yield*' instead.`,
2990
+ fixes: fix
2991
+ });
2992
+ }
2993
+ }
2994
+ }
2995
+ }
2996
+ }
2997
+ }
2998
+ })
2999
+ });
3000
+
3001
+ // src/diagnostics/missingStarInYieldEffectGen.ts
3002
+ var missingStarInYieldEffectGen = createDiagnostic({
3003
+ name: "missingStarInYieldEffectGen",
3004
+ code: 4,
3005
+ severity: "error",
3006
+ apply: fn("missingStarInYieldEffectGen.apply")(function* (sourceFile, report) {
3007
+ const ts = yield* service(TypeScriptApi);
3008
+ const typeParser = yield* service(TypeParser);
3009
+ const brokenGenerators = /* @__PURE__ */ new Set();
3010
+ const brokenYields = /* @__PURE__ */ new Set();
3077
3011
  const nodeToVisit = [];
3078
3012
  const appendNodeToVisit = (node) => {
3079
3013
  nodeToVisit.push(node);
@@ -3082,125 +3016,136 @@ var importFromBarrel = createDiagnostic({
3082
3016
  ts.forEachChild(sourceFile, appendNodeToVisit);
3083
3017
  while (nodeToVisit.length > 0) {
3084
3018
  const node = nodeToVisit.shift();
3085
- const parent = node.parent;
3086
- if (!(ts.isImportSpecifier(node) && ts.isNamedImports(parent))) {
3087
- ts.forEachChild(node, appendNodeToVisit);
3088
- continue;
3019
+ ts.forEachChild(node, appendNodeToVisit);
3020
+ if (ts.isYieldExpression(node) && node.expression && node.asteriskToken === void 0) {
3021
+ const functionStarNode = ts.findAncestor(
3022
+ node,
3023
+ (_) => ts.isFunctionExpression(_) || ts.isFunctionDeclaration(_) || ts.isMethodDeclaration(_)
3024
+ );
3025
+ if (functionStarNode && functionStarNode.parent) {
3026
+ const effectGenNode = functionStarNode.parent;
3027
+ yield* pipe(
3028
+ typeParser.effectGen(effectGenNode),
3029
+ orElse2(() => typeParser.effectFnUntracedGen(effectGenNode)),
3030
+ orElse2(() => typeParser.effectFnGen(effectGenNode)),
3031
+ map4(({ functionStar }) => {
3032
+ if (functionStar) {
3033
+ brokenGenerators.add(functionStar);
3034
+ }
3035
+ brokenYields.add(node);
3036
+ }),
3037
+ ignore
3038
+ );
3039
+ }
3089
3040
  }
3090
- const result = isImportedFromBarrelExport(node, languageServicePluginOptions);
3091
- if (!result) continue;
3092
- const { barrelModuleName, importClause, importDeclaration, importedName, namedBindings, unbarrelledFileName } = result;
3041
+ }
3042
+ brokenGenerators.forEach(
3043
+ (node) => report({
3044
+ node,
3045
+ messageText: `Seems like you used yield instead of yield* inside this Effect.gen.`,
3046
+ fixes: []
3047
+ })
3048
+ );
3049
+ brokenYields.forEach((node) => {
3050
+ const fix = node.expression ? [{
3051
+ fixName: "missingStarInYieldEffectGen_fix",
3052
+ description: "Replace yield with yield*",
3053
+ apply: gen(function* () {
3054
+ const changeTracker = yield* service(ChangeTracker);
3055
+ changeTracker.replaceNode(
3056
+ sourceFile,
3057
+ node,
3058
+ ts.factory.createYieldExpression(
3059
+ ts.factory.createToken(ts.SyntaxKind.AsteriskToken),
3060
+ node.expression
3061
+ )
3062
+ );
3063
+ })
3064
+ }] : [];
3093
3065
  report({
3094
3066
  node,
3095
- messageText: `Importing from barrel module ${barrelModuleName} is not allowed.`,
3096
- fixes: [
3097
- {
3098
- fixName: "replaceWithUnbarrelledImport",
3099
- description: `Import * as ${importedName} from ${unbarrelledFileName}`,
3100
- apply: gen(function* () {
3101
- const changeTracker = yield* service(ChangeTracker);
3102
- const newImport = ts.factory.createImportDeclaration(
3103
- void 0,
3104
- ts.factory.createImportClause(
3105
- importClause.isTypeOnly || node.isTypeOnly,
3106
- void 0,
3107
- ts.factory.createNamespaceImport(ts.factory.createIdentifier(importedName))
3108
- ),
3109
- ts.factory.createStringLiteral(unbarrelledFileName)
3110
- );
3111
- if (namedBindings.elements.length === 1) {
3112
- changeTracker.replaceNode(
3113
- sourceFile,
3114
- importDeclaration,
3115
- newImport
3116
- );
3117
- } else {
3118
- changeTracker.insertNodeAfter(sourceFile, importDeclaration, newImport);
3119
- changeTracker.replaceNode(
3120
- sourceFile,
3121
- namedBindings,
3122
- ts.factory.updateNamedImports(
3123
- namedBindings,
3124
- namedBindings.elements.filter((e) => e !== node)
3125
- )
3126
- );
3127
- }
3128
- })
3129
- }
3130
- ]
3067
+ messageText: `When yielding Effects inside Effect.gen, you should use yield* instead of yield.`,
3068
+ fixes: fix
3131
3069
  });
3132
- }
3070
+ });
3133
3071
  })
3134
3072
  });
3135
3073
 
3136
- // src/diagnostics/leakingRequirements.ts
3137
- var leakingRequirements = createDiagnostic({
3138
- name: "leakingRequirements",
3139
- code: 8,
3074
+ // src/diagnostics/returnEffectInGen.ts
3075
+ var returnEffectInGen = createDiagnostic({
3076
+ name: "returnEffectInGen",
3077
+ code: 11,
3140
3078
  severity: "suggestion",
3141
- apply: fn("leakingRequirements.apply")(function* (sourceFile, report) {
3079
+ apply: fn("returnEffectInGen.apply")(function* (sourceFile, report) {
3142
3080
  const ts = yield* service(TypeScriptApi);
3143
3081
  const typeChecker = yield* service(TypeCheckerApi);
3144
3082
  const typeParser = yield* service(TypeParser);
3145
- const typeOrder = yield* deterministicTypeOrder;
3146
- const parseLeakedRequirements = cachedBy(
3147
- fn("leakingServices.checkServiceLeaking")(
3148
- function* (service2, atLocation) {
3149
- const properties = typeChecker.getPropertiesOfType(service2);
3150
- if (properties.length < 1) return [];
3151
- const memory = /* @__PURE__ */ new Map();
3152
- let sharedRequirementsKeys = void 0;
3153
- let effectMembers = 0;
3154
- for (const property of properties) {
3155
- const servicePropertyType = typeChecker.getTypeOfSymbolAtLocation(property, atLocation);
3156
- let effectContextType = void 0;
3083
+ const nodeToVisit = [];
3084
+ const appendNodeToVisit = (node) => {
3085
+ nodeToVisit.push(node);
3086
+ return void 0;
3087
+ };
3088
+ ts.forEachChild(sourceFile, appendNodeToVisit);
3089
+ while (nodeToVisit.length > 0) {
3090
+ const node = nodeToVisit.shift();
3091
+ ts.forEachChild(node, appendNodeToVisit);
3092
+ if (ts.isReturnStatement(node) && node.expression) {
3093
+ if (ts.isYieldExpression(node.expression)) continue;
3094
+ const generatorOrRegularFunction = ts.findAncestor(
3095
+ node,
3096
+ (_) => ts.isFunctionExpression(_) || ts.isFunctionDeclaration(_) || ts.isMethodDeclaration(_) || ts.isArrowFunction(_) || ts.isGetAccessor(_)
3097
+ );
3098
+ if (!(generatorOrRegularFunction && "asteriskToken" in generatorOrRegularFunction && generatorOrRegularFunction.asteriskToken)) continue;
3099
+ const type = typeChecker.getTypeAtLocation(node.expression);
3100
+ const maybeEffect = yield* option(typeParser.strictEffectType(type, node.expression));
3101
+ if (isSome2(maybeEffect)) {
3102
+ if (generatorOrRegularFunction && generatorOrRegularFunction.parent) {
3103
+ const effectGenNode = generatorOrRegularFunction.parent;
3157
3104
  yield* pipe(
3158
- typeParser.effectType(servicePropertyType, atLocation),
3159
- map4((_) => effectContextType = _.R),
3160
- orElse2(() => {
3161
- const servicePropertyCallSignatures = servicePropertyType.getCallSignatures();
3162
- if (servicePropertyCallSignatures.length === 1) {
3163
- return pipe(
3164
- typeParser.effectType(servicePropertyCallSignatures[0].getReturnType(), atLocation),
3165
- map4((_) => {
3166
- effectContextType = _.R;
3167
- })
3168
- );
3169
- }
3170
- return void_;
3105
+ typeParser.effectGen(effectGenNode),
3106
+ orElse2(() => typeParser.effectFnUntracedGen(effectGenNode)),
3107
+ orElse2(() => typeParser.effectFnGen(effectGenNode)),
3108
+ map4(() => {
3109
+ const fix = node.expression ? [{
3110
+ fixName: "returnEffectInGen_fix",
3111
+ description: "Add yield* statement",
3112
+ apply: gen(function* () {
3113
+ const changeTracker = yield* service(ChangeTracker);
3114
+ changeTracker.replaceNode(
3115
+ sourceFile,
3116
+ node.expression,
3117
+ ts.factory.createYieldExpression(
3118
+ ts.factory.createToken(ts.SyntaxKind.AsteriskToken),
3119
+ node.expression
3120
+ )
3121
+ );
3122
+ })
3123
+ }] : [];
3124
+ report({
3125
+ node,
3126
+ messageText: `You are returning an Effect-able type inside a generator function, and will result in nested Effect<Effect<...>>.
3127
+ Maybe you wanted to return yield* instead?
3128
+ Nested Effect-able types may be intended if you plan to later manually flatten or unwrap this Effect, if so you can safely disable this diagnostic for this line through quickfixes.`,
3129
+ fixes: fix
3130
+ });
3171
3131
  }),
3172
3132
  ignore
3173
3133
  );
3174
- if (effectContextType) {
3175
- effectMembers++;
3176
- const { allIndexes } = yield* appendToUniqueTypesMap(memory, effectContextType, true);
3177
- if (!sharedRequirementsKeys) {
3178
- sharedRequirementsKeys = allIndexes;
3179
- } else {
3180
- sharedRequirementsKeys = intersection(sharedRequirementsKeys, allIndexes);
3181
- if (sharedRequirementsKeys.length === 0) return [];
3182
- }
3183
- }
3184
- }
3185
- if (sharedRequirementsKeys && sharedRequirementsKeys.length > 0 && effectMembers >= 2) {
3186
- return sharedRequirementsKeys.map((key) => memory.get(key));
3187
3134
  }
3188
- return [];
3189
3135
  }
3190
- ),
3191
- "leakingServices.checkServiceLeaking",
3192
- (_, service2) => service2
3193
- );
3194
- function reportLeakingRequirements(node, requirements) {
3195
- if (requirements.length === 0) return;
3196
- report({
3197
- node,
3198
- messageText: `This Service is leaking the ${requirements.map((_) => typeChecker.typeToString(_)).join(" | ")} requirement.
3199
- If these requirements cannot be cached and are expected to be provided per method invocation (e.g. HttpServerRequest), you can safely disable this diagnostic for this line through quickfixes.
3200
- More info at https://effect.website/docs/requirements-management/layers/#avoiding-requirement-leakage`,
3201
- fixes: []
3202
- });
3136
+ }
3203
3137
  }
3138
+ })
3139
+ });
3140
+
3141
+ // src/diagnostics/unnecessaryEffectGen.ts
3142
+ var unnecessaryEffectGen = createDiagnostic({
3143
+ name: "unnecessaryEffectGen",
3144
+ code: 5,
3145
+ severity: "suggestion",
3146
+ apply: fn("unnecessaryEffectGen.apply")(function* (sourceFile, report) {
3147
+ const ts = yield* service(TypeScriptApi);
3148
+ const typeParser = yield* service(TypeParser);
3204
3149
  const nodeToVisit = [];
3205
3150
  const appendNodeToVisit = (node) => {
3206
3151
  nodeToVisit.push(node);
@@ -3209,29 +3154,26 @@ More info at https://effect.website/docs/requirements-management/layers/#avoidin
3209
3154
  ts.forEachChild(sourceFile, appendNodeToVisit);
3210
3155
  while (nodeToVisit.length > 0) {
3211
3156
  const node = nodeToVisit.shift();
3212
- const typesToCheck = [];
3213
- if (ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression) && ts.isIdentifier(node.expression.name) && node.expression.name.text === "GenericTag") {
3214
- typesToCheck.push([typeChecker.getTypeAtLocation(node), node]);
3215
- } else if (ts.isClassDeclaration(node) && node.name && node.heritageClauses) {
3216
- const classSym = typeChecker.getSymbolAtLocation(node.name);
3217
- if (classSym) {
3218
- const type = typeChecker.getTypeOfSymbol(classSym);
3219
- typesToCheck.push([type, node.name]);
3220
- }
3221
- } else {
3222
- ts.forEachChild(node, appendNodeToVisit);
3223
- continue;
3224
- }
3225
- for (const [type, reportAt] of typesToCheck) {
3157
+ ts.forEachChild(node, appendNodeToVisit);
3158
+ if (ts.isCallExpression(node)) {
3226
3159
  yield* pipe(
3227
- typeParser.contextTag(type, node),
3228
- flatMap2(
3229
- ({ Service }) => pipe(
3230
- parseLeakedRequirements(Service, node),
3231
- map4((requirements) => reportLeakingRequirements(reportAt, sort(requirements, typeOrder)))
3232
- )
3160
+ typeParser.unnecessaryEffectGen(node),
3161
+ map4(
3162
+ ({ replacementNode }) => report({
3163
+ node,
3164
+ messageText: `This Effect.gen contains a single return statement.`,
3165
+ fixes: [{
3166
+ fixName: "unnecessaryEffectGen_fix",
3167
+ description: "Remove the Effect.gen, and keep the body",
3168
+ apply: gen(function* () {
3169
+ const textChanges = yield* service(
3170
+ ChangeTracker
3171
+ );
3172
+ textChanges.replaceNode(sourceFile, node, yield* replacementNode);
3173
+ })
3174
+ }]
3175
+ })
3233
3176
  ),
3234
- orElse2(() => sync(() => ts.forEachChild(node, appendNodeToVisit))),
3235
3177
  ignore
3236
3178
  );
3237
3179
  }
@@ -3239,47 +3181,44 @@ More info at https://effect.website/docs/requirements-management/layers/#avoidin
3239
3181
  })
3240
3182
  });
3241
3183
 
3242
- // src/diagnostics/missingEffectContext.ts
3243
- var missingEffectContext = createDiagnostic({
3244
- name: "missingEffectContext",
3245
- code: 1,
3246
- severity: "error",
3247
- apply: fn("missingEffectContext.apply")(function* (sourceFile, report) {
3248
- const typeChecker = yield* service(TypeCheckerApi);
3184
+ // src/diagnostics/unnecessaryPipe.ts
3185
+ var unnecessaryPipe = createDiagnostic({
3186
+ name: "unnecessaryPipe",
3187
+ code: 9,
3188
+ severity: "suggestion",
3189
+ apply: fn("unnecessaryPipe.apply")(function* (sourceFile, report) {
3190
+ const ts = yield* service(TypeScriptApi);
3249
3191
  const typeParser = yield* service(TypeParser);
3250
- const typeOrder = yield* deterministicTypeOrder;
3251
- const checkForMissingContextTypes = (node, expectedType, valueNode, realType) => pipe(
3252
- all(
3253
- typeParser.effectType(expectedType, node),
3254
- typeParser.effectType(realType, valueNode)
3255
- ),
3256
- flatMap2(
3257
- ([expectedEffect, realEffect]) => getMissingTypeEntriesInTargetType(
3258
- realEffect.R,
3259
- expectedEffect.R
3260
- )
3261
- )
3262
- );
3263
- const sortTypes = sort(typeOrder);
3264
- const entries = yield* expectedAndRealType(sourceFile);
3265
- for (const [node, expectedType, valueNode, realType] of entries) {
3266
- if (expectedType !== realType) {
3192
+ const nodeToVisit = [];
3193
+ const appendNodeToVisit = (node) => {
3194
+ nodeToVisit.push(node);
3195
+ return void 0;
3196
+ };
3197
+ ts.forEachChild(sourceFile, appendNodeToVisit);
3198
+ while (nodeToVisit.length > 0) {
3199
+ const node = nodeToVisit.shift();
3200
+ ts.forEachChild(node, appendNodeToVisit);
3201
+ if (ts.isCallExpression(node)) {
3267
3202
  yield* pipe(
3268
- checkForMissingContextTypes(
3269
- node,
3270
- expectedType,
3271
- valueNode,
3272
- realType
3273
- ),
3274
- map4(
3275
- (missingTypes) => missingTypes.length > 0 ? report(
3276
- {
3203
+ typeParser.pipeCall(node),
3204
+ map4(({ args, subject }) => {
3205
+ if (args.length === 0) {
3206
+ report({
3277
3207
  node,
3278
- messageText: `Missing '${sortTypes(missingTypes).map((_) => typeChecker.typeToString(_)).join(" | ")}' in the expected Effect context.`,
3279
- fixes: []
3280
- }
3281
- ) : void 0
3282
- ),
3208
+ messageText: `This pipe call contains no arguments.`,
3209
+ fixes: [{
3210
+ fixName: "unnecessaryPipe_fix",
3211
+ description: "Remove the pipe call",
3212
+ apply: gen(function* () {
3213
+ const textChanges = yield* service(
3214
+ ChangeTracker
3215
+ );
3216
+ textChanges.replaceNode(sourceFile, node, subject);
3217
+ })
3218
+ }]
3219
+ });
3220
+ }
3221
+ }),
3283
3222
  ignore
3284
3223
  );
3285
3224
  }
@@ -3287,361 +3226,468 @@ var missingEffectContext = createDiagnostic({
3287
3226
  })
3288
3227
  });
3289
3228
 
3290
- // src/diagnostics/missingEffectError.ts
3291
- var missingEffectError = createDiagnostic({
3292
- name: "missingEffectError",
3293
- code: 1,
3294
- severity: "error",
3295
- apply: fn("missingEffectError.apply")(function* (sourceFile, report) {
3229
+ // src/diagnostics.ts
3230
+ var diagnostics = [
3231
+ duplicatePackage,
3232
+ missingEffectContext,
3233
+ missingEffectError,
3234
+ floatingEffect,
3235
+ missingStarInYieldEffectGen,
3236
+ unnecessaryEffectGen,
3237
+ missingReturnYieldStar,
3238
+ leakingRequirements,
3239
+ unnecessaryPipe,
3240
+ genericEffectServices,
3241
+ returnEffectInGen,
3242
+ importFromBarrel
3243
+ ];
3244
+
3245
+ // src/completions/effectDiagnosticsComment.ts
3246
+ var effectDiagnosticsComment = createCompletion({
3247
+ name: "effectDiagnosticsComment",
3248
+ apply: fn("effectDiagnosticsComment")(function* (sourceFile, position) {
3249
+ const ts = yield* service(TypeScriptApi);
3250
+ const sourceText = sourceFile.text;
3251
+ const match2 = /(\/\/|\/\*(?:\*?))\s*(@)\s*$/id.exec(sourceText.substring(0, position));
3252
+ if (match2 && match2.indices) {
3253
+ const lastIndex = match2.indices[2][0];
3254
+ const replacementSpan = {
3255
+ start: lastIndex,
3256
+ length: Math.max(0, position - lastIndex)
3257
+ };
3258
+ const allDiagnostics = sort(Object.values(diagnostics).map((diagnostic) => diagnostic.name), string2).join(",");
3259
+ const disableSnippet = "${1|" + allDiagnostics + "|}:${2|off,warning,error,message,suggestion|}$0";
3260
+ return [{
3261
+ name: `@effect-diagnostics`,
3262
+ kind: ts.ScriptElementKind.string,
3263
+ insertText: "@effect-diagnostics " + disableSnippet,
3264
+ isSnippet: true,
3265
+ replacementSpan
3266
+ }, {
3267
+ name: `@effect-diagnostics-next-line`,
3268
+ kind: ts.ScriptElementKind.string,
3269
+ insertText: "@effect-diagnostics-next-line " + disableSnippet,
3270
+ isSnippet: true,
3271
+ replacementSpan
3272
+ }];
3273
+ }
3274
+ return [];
3275
+ })
3276
+ });
3277
+
3278
+ // src/completions/effectSchemaSelfInClasses.ts
3279
+ var effectSchemaSelfInClasses = createCompletion({
3280
+ name: "effectSchemaSelfInClasses",
3281
+ apply: fn("effectSchemaSelfInClasses")(function* (sourceFile, position) {
3282
+ const ts = yield* service(TypeScriptApi);
3283
+ const maybeInfos = yield* option(
3284
+ parseDataForExtendsClassCompletion(sourceFile, position)
3285
+ );
3286
+ if (isNone2(maybeInfos)) return [];
3287
+ const { accessedObject, className, replacementSpan } = maybeInfos.value;
3288
+ const effectSchemaName = yield* option(
3289
+ findImportedModuleIdentifierByPackageAndNameOrBarrel(
3290
+ sourceFile,
3291
+ "effect",
3292
+ "Schema"
3293
+ )
3294
+ );
3295
+ const schemaIdentifier = match(effectSchemaName, {
3296
+ onNone: () => "Schema",
3297
+ onSome: (_) => _.text
3298
+ });
3299
+ if (schemaIdentifier !== accessedObject.text) return [];
3300
+ const name = className.text;
3301
+ return [{
3302
+ name: `Class<${name}>`,
3303
+ kind: ts.ScriptElementKind.constElement,
3304
+ insertText: `${schemaIdentifier}.Class<${name}>("${name}")({${"${0}"}}){}`,
3305
+ replacementSpan,
3306
+ isSnippet: true
3307
+ }, {
3308
+ name: `TaggedError<${name}>`,
3309
+ kind: ts.ScriptElementKind.constElement,
3310
+ insertText: `${schemaIdentifier}.TaggedError<${name}>("${name}")("${name}", {${"${0}"}}){}`,
3311
+ replacementSpan,
3312
+ isSnippet: true
3313
+ }, {
3314
+ name: `TaggedClass<${name}>`,
3315
+ kind: ts.ScriptElementKind.constElement,
3316
+ insertText: `${schemaIdentifier}.TaggedClass<${name}>("${name}")("${name}", {${"${0}"}}){}`,
3317
+ replacementSpan,
3318
+ isSnippet: true
3319
+ }, {
3320
+ name: `TaggedRequest<${name}>`,
3321
+ kind: ts.ScriptElementKind.constElement,
3322
+ insertText: `${schemaIdentifier}.TaggedRequest<${name}>("${name}")("${name}", {${"${0}"}}){}`,
3323
+ replacementSpan,
3324
+ isSnippet: true
3325
+ }];
3326
+ })
3327
+ });
3328
+
3329
+ // src/completions/effectSelfInClasses.ts
3330
+ var effectSelfInClasses = createCompletion({
3331
+ name: "effectSelfInClasses",
3332
+ apply: fn("effectSelfInClasses")(function* (sourceFile, position) {
3333
+ const ts = yield* service(TypeScriptApi);
3334
+ const maybeInfos = yield* option(
3335
+ parseDataForExtendsClassCompletion(sourceFile, position)
3336
+ );
3337
+ if (isNone2(maybeInfos)) return [];
3338
+ const { accessedObject, className, replacementSpan } = maybeInfos.value;
3339
+ const effectName = yield* option(
3340
+ findImportedModuleIdentifierByPackageAndNameOrBarrel(
3341
+ sourceFile,
3342
+ "effect",
3343
+ "Effect"
3344
+ )
3345
+ );
3346
+ const effectIdentifier = match(effectName, {
3347
+ onNone: () => "Effect",
3348
+ onSome: (_) => _.text
3349
+ });
3350
+ if (effectIdentifier !== accessedObject.text) return [];
3351
+ const name = className.text;
3352
+ return [{
3353
+ name: `Service<${name}>`,
3354
+ kind: ts.ScriptElementKind.constElement,
3355
+ insertText: `${effectIdentifier}.Service<${name}>()("${name}", {${"${0}"}}){}`,
3356
+ replacementSpan,
3357
+ isSnippet: true
3358
+ }];
3359
+ })
3360
+ });
3361
+
3362
+ // src/completions/fnFunctionStar.ts
3363
+ var fnFunctionStar = createCompletion({
3364
+ name: "fnFunctionStar",
3365
+ apply: fn("fnFunctionStar")(function* (sourceFile, position) {
3366
+ const ts = yield* service(TypeScriptApi);
3367
+ const typeParser = yield* service(TypeParser);
3368
+ const maybeInfos = yield* option(
3369
+ parseAccessedExpressionForCompletion(sourceFile, position)
3370
+ );
3371
+ if (isNone2(maybeInfos)) return [];
3372
+ const { accessedObject } = maybeInfos.value;
3373
+ const isEffectModule = yield* option(typeParser.importedEffectModule(accessedObject));
3374
+ if (isNone2(isEffectModule)) return [];
3375
+ const span = ts.createTextSpan(
3376
+ accessedObject.end + 1,
3377
+ Math.max(0, position - accessedObject.end - 1)
3378
+ );
3379
+ const maybeFnName = pipe(
3380
+ yield* getAncestorNodesInRange(sourceFile, toTextRange(accessedObject.pos)),
3381
+ filter(ts.isVariableDeclaration),
3382
+ map3((_) => _.name && ts.isIdentifier(_.name) ? _.name.text : ""),
3383
+ filter((_) => _.length > 0),
3384
+ head,
3385
+ map2((name) => [
3386
+ {
3387
+ name: `fn("${name}")`,
3388
+ kind: ts.ScriptElementKind.constElement,
3389
+ insertText: `fn("${name}")(function*(${"${1}"}){${"${0}"}})`,
3390
+ replacementSpan: span,
3391
+ isSnippet: true
3392
+ }
3393
+ ]),
3394
+ getOrElse2(() => [])
3395
+ );
3396
+ return maybeFnName.concat([{
3397
+ name: `fn(function*(){})`,
3398
+ kind: ts.ScriptElementKind.constElement,
3399
+ insertText: `fn(function*(${"${1}"}){${"${0}"}})`,
3400
+ replacementSpan: span,
3401
+ isSnippet: true
3402
+ }, {
3403
+ name: `fnUntraced(function*(){})`,
3404
+ kind: ts.ScriptElementKind.constElement,
3405
+ insertText: `fnUntraced(function*(${"${1}"}){${"${0}"}})`,
3406
+ replacementSpan: span,
3407
+ isSnippet: true
3408
+ }]);
3409
+ })
3410
+ });
3411
+
3412
+ // src/completions/genFunctionStar.ts
3413
+ var genFunctionStar = createCompletion({
3414
+ name: "genFunctionStar",
3415
+ apply: fn("genFunctionStar")(function* (sourceFile, position) {
3416
+ const ts = yield* service(TypeScriptApi);
3296
3417
  const typeChecker = yield* service(TypeCheckerApi);
3297
- const typeParser = yield* service(TypeParser);
3298
- const typeOrder = yield* deterministicTypeOrder;
3299
- const checkForMissingErrorTypes = (node, expectedType, valueNode, realType) => pipe(
3300
- all(
3301
- typeParser.effectType(expectedType, node),
3302
- typeParser.effectType(realType, valueNode)
3303
- ),
3304
- flatMap2(
3305
- ([expectedEffect, realEffect]) => getMissingTypeEntriesInTargetType(
3306
- realEffect.E,
3307
- expectedEffect.E
3308
- )
3309
- )
3418
+ const maybeInfos = yield* option(
3419
+ parseAccessedExpressionForCompletion(sourceFile, position)
3310
3420
  );
3311
- const sortTypes = sort(typeOrder);
3312
- const entries = yield* expectedAndRealType(sourceFile);
3313
- for (const [node, expectedType, valueNode, realType] of entries) {
3314
- if (expectedType !== realType) {
3315
- yield* pipe(
3316
- checkForMissingErrorTypes(
3317
- node,
3318
- expectedType,
3319
- valueNode,
3320
- realType
3321
- ),
3322
- map4(
3323
- (missingTypes) => missingTypes.length > 0 ? report(
3324
- {
3325
- node,
3326
- messageText: `Missing '${sortTypes(missingTypes).map((_) => typeChecker.typeToString(_)).join(" | ")}' in the expected Effect errors.`,
3327
- fixes: []
3328
- }
3329
- ) : void 0
3330
- ),
3331
- ignore
3332
- );
3333
- }
3334
- }
3421
+ if (isNone2(maybeInfos)) return [];
3422
+ const { accessedObject } = maybeInfos.value;
3423
+ const type = typeChecker.getTypeAtLocation(accessedObject);
3424
+ const genMemberSymbol = type.getProperty("gen");
3425
+ if (!genMemberSymbol) return [];
3426
+ const genType = typeChecker.getTypeOfSymbolAtLocation(genMemberSymbol, accessedObject);
3427
+ if (genType.getCallSignatures().length === 0) return [];
3428
+ const span = ts.createTextSpan(
3429
+ accessedObject.end + 1,
3430
+ Math.max(0, position - accessedObject.end - 1)
3431
+ );
3432
+ return [{
3433
+ name: `gen(function*(){})`,
3434
+ kind: ts.ScriptElementKind.constElement,
3435
+ insertText: `gen(function*(){${"${0}"}})`,
3436
+ replacementSpan: span,
3437
+ isSnippet: true
3438
+ }];
3335
3439
  })
3336
3440
  });
3337
3441
 
3338
- // src/diagnostics/missingReturnYieldStar.ts
3339
- var missingReturnYieldStar = createDiagnostic({
3340
- name: "missingReturnYieldStar",
3341
- code: 7,
3342
- severity: "error",
3343
- apply: fn("missingReturnYieldStar.apply")(function* (sourceFile, report) {
3442
+ // src/completions/rpcMakeClasses.ts
3443
+ var rpcMakeClasses = createCompletion({
3444
+ name: "rpcMakeClasses",
3445
+ apply: fn("rpcMakeClasses")(function* (sourceFile, position) {
3344
3446
  const ts = yield* service(TypeScriptApi);
3345
- const typeChecker = yield* service(TypeCheckerApi);
3346
- const typeParser = yield* service(TypeParser);
3347
- const nodeToVisit = [];
3348
- const appendNodeToVisit = (node) => {
3349
- nodeToVisit.push(node);
3350
- return void 0;
3351
- };
3352
- ts.forEachChild(sourceFile, appendNodeToVisit);
3353
- while (nodeToVisit.length > 0) {
3354
- const node = nodeToVisit.shift();
3355
- ts.forEachChild(node, appendNodeToVisit);
3356
- if (ts.isYieldExpression(node) && node.expression && node.asteriskToken) {
3357
- const type = typeChecker.getTypeAtLocation(node.expression);
3358
- const maybeEffect = yield* option(typeParser.effectType(type, node.expression));
3359
- if (isSome2(maybeEffect) && maybeEffect.value.A.flags & ts.TypeFlags.Never) {
3360
- const generatorFunctionOrReturnStatement = ts.findAncestor(
3361
- node,
3362
- (_) => ts.isFunctionExpression(_) || ts.isFunctionDeclaration(_) || ts.isMethodDeclaration(_) || ts.isReturnStatement(_)
3363
- );
3364
- if (generatorFunctionOrReturnStatement && !ts.isReturnStatement(generatorFunctionOrReturnStatement)) {
3365
- if (generatorFunctionOrReturnStatement && generatorFunctionOrReturnStatement.parent) {
3366
- const effectGenNode = generatorFunctionOrReturnStatement.parent;
3367
- const effectGenLike = yield* pipe(
3368
- typeParser.effectGen(effectGenNode),
3369
- orElse2(() => typeParser.effectFnUntracedGen(effectGenNode)),
3370
- orElse2(() => typeParser.effectFnGen(effectGenNode)),
3371
- option
3372
- );
3373
- if (isSome2(effectGenLike)) {
3374
- const fix = node.expression ? [{
3375
- fixName: "missingReturnYieldStar_fix",
3376
- description: "Add return statement",
3377
- apply: gen(function* () {
3378
- const changeTracker = yield* service(ChangeTracker);
3379
- changeTracker.replaceNode(
3380
- sourceFile,
3381
- node,
3382
- ts.factory.createReturnStatement(
3383
- node
3384
- )
3385
- );
3386
- })
3387
- }] : [];
3388
- report({
3389
- node,
3390
- messageText: `Yielded Effect never succeeds, so it is best to use a 'return yield*' instead.`,
3391
- fixes: fix
3392
- });
3393
- }
3394
- }
3395
- }
3396
- }
3397
- }
3398
- }
3447
+ const maybeInfos = yield* option(
3448
+ parseDataForExtendsClassCompletion(sourceFile, position)
3449
+ );
3450
+ if (isNone2(maybeInfos)) return [];
3451
+ const { accessedObject, className, replacementSpan } = maybeInfos.value;
3452
+ const rpcName = yield* option(
3453
+ findImportedModuleIdentifierByPackageAndNameOrBarrel(
3454
+ sourceFile,
3455
+ "@effect/rpc",
3456
+ "Rpc"
3457
+ )
3458
+ );
3459
+ const rpcIdentifier = match(rpcName, {
3460
+ onNone: () => "Rpc",
3461
+ onSome: (_) => _.text
3462
+ });
3463
+ if (rpcIdentifier !== accessedObject.text) return [];
3464
+ const name = className.text;
3465
+ return [{
3466
+ name: `make("${name}")`,
3467
+ kind: ts.ScriptElementKind.constElement,
3468
+ insertText: `${rpcIdentifier}.make("${name}", {${"${0}"}}) {}`,
3469
+ replacementSpan,
3470
+ isSnippet: true
3471
+ }];
3399
3472
  })
3400
3473
  });
3401
3474
 
3402
- // src/diagnostics/missingStarInYieldEffectGen.ts
3403
- var missingStarInYieldEffectGen = createDiagnostic({
3404
- name: "missingStarInYieldEffectGen",
3405
- code: 4,
3406
- severity: "error",
3407
- apply: fn("missingStarInYieldEffectGen.apply")(function* (sourceFile, report) {
3408
- const ts = yield* service(TypeScriptApi);
3409
- const typeParser = yield* service(TypeParser);
3410
- const brokenGenerators = /* @__PURE__ */ new Set();
3411
- const brokenYields = /* @__PURE__ */ new Set();
3412
- const nodeToVisit = [];
3413
- const appendNodeToVisit = (node) => {
3414
- nodeToVisit.push(node);
3415
- return void 0;
3416
- };
3417
- ts.forEachChild(sourceFile, appendNodeToVisit);
3418
- while (nodeToVisit.length > 0) {
3419
- const node = nodeToVisit.shift();
3420
- ts.forEachChild(node, appendNodeToVisit);
3421
- if (ts.isYieldExpression(node) && node.expression && node.asteriskToken === void 0) {
3422
- const functionStarNode = ts.findAncestor(
3423
- node,
3424
- (_) => ts.isFunctionExpression(_) || ts.isFunctionDeclaration(_) || ts.isMethodDeclaration(_)
3425
- );
3426
- if (functionStarNode && functionStarNode.parent) {
3427
- const effectGenNode = functionStarNode.parent;
3428
- yield* pipe(
3429
- typeParser.effectGen(effectGenNode),
3430
- orElse2(() => typeParser.effectFnUntracedGen(effectGenNode)),
3431
- orElse2(() => typeParser.effectFnGen(effectGenNode)),
3432
- map4(({ functionStar }) => {
3433
- if (functionStar) {
3434
- brokenGenerators.add(functionStar);
3475
+ // src/completions.ts
3476
+ var completions = [
3477
+ effectSchemaSelfInClasses,
3478
+ effectSelfInClasses,
3479
+ contextSelfInClasses,
3480
+ rpcMakeClasses,
3481
+ genFunctionStar,
3482
+ fnFunctionStar,
3483
+ effectDataClasses,
3484
+ effectDiagnosticsComment
3485
+ ];
3486
+
3487
+ // src/completions/middlewareNamespaceImports.ts
3488
+ var importablePackagesMetadataCache = /* @__PURE__ */ new Map();
3489
+ var makeImportablePackagesMetadata = fn("makeImportablePackagesMetadata")(function* (sourceFile) {
3490
+ const ts = yield* service(TypeScriptApi);
3491
+ const program = yield* service(TypeScriptProgram);
3492
+ const languageServicePluginOptions = yield* service(LanguageServicePluginOptions);
3493
+ const host = program;
3494
+ const namespaceByFileName = /* @__PURE__ */ new Map();
3495
+ const excludedByFileName = /* @__PURE__ */ new Map();
3496
+ const unbarreledModulePathByFileName = /* @__PURE__ */ new Map();
3497
+ const barreledModulePathByFileName = /* @__PURE__ */ new Map();
3498
+ for (const packageName of languageServicePluginOptions.namespaceImportPackages) {
3499
+ const barrelModule = ts.resolveModuleName(packageName, sourceFile.fileName, program.getCompilerOptions(), host);
3500
+ if (barrelModule.resolvedModule) {
3501
+ const barrelPath = barrelModule.resolvedModule.resolvedFileName;
3502
+ const barrelSource = program.getSourceFile(barrelPath) || ts.createSourceFile(barrelPath, host.readFile(barrelPath) || "", sourceFile.languageVersion, true);
3503
+ if (barrelSource) {
3504
+ for (const statement of barrelSource.statements) {
3505
+ if (ts.isExportDeclaration(statement)) {
3506
+ const exportClause = statement.exportClause;
3507
+ const moduleSpecifier = statement.moduleSpecifier;
3508
+ if (moduleSpecifier && ts.isStringLiteral(moduleSpecifier)) {
3509
+ const unbarreledModulePathResolved = ts.resolveModuleName(
3510
+ moduleSpecifier.text,
3511
+ barrelSource.fileName,
3512
+ program.getCompilerOptions(),
3513
+ host
3514
+ );
3515
+ if (unbarreledModulePathResolved.resolvedModule) {
3516
+ const unbarreledModulePath = unbarreledModulePathResolved.resolvedModule.resolvedFileName;
3517
+ if (exportClause && ts.isNamespaceExport(exportClause) && ts.isIdentifier(exportClause.name)) {
3518
+ namespaceByFileName.set(unbarreledModulePath, exportClause.name.text);
3519
+ const existingUnbarreledModulePath = unbarreledModulePathByFileName.get(barrelSource.fileName) || [];
3520
+ existingUnbarreledModulePath.push({
3521
+ fileName: unbarreledModulePath,
3522
+ exportName: exportClause.name.text
3523
+ });
3524
+ unbarreledModulePathByFileName.set(barrelSource.fileName, existingUnbarreledModulePath);
3525
+ barreledModulePathByFileName.set(unbarreledModulePath, {
3526
+ fileName: barrelSource.fileName,
3527
+ exportName: exportClause.name.text
3528
+ });
3529
+ }
3530
+ if (exportClause && ts.isNamedExports(exportClause)) {
3531
+ for (const element of exportClause.elements) {
3532
+ if (!ts.isIdentifier(element.name)) continue;
3533
+ const methodName = element.name.text;
3534
+ const excludedMethods = excludedByFileName.get(methodName) || [];
3535
+ excludedMethods.push(unbarreledModulePath);
3536
+ excludedByFileName.set(methodName, excludedMethods);
3537
+ }
3538
+ }
3435
3539
  }
3436
- brokenYields.add(node);
3437
- }),
3438
- ignore
3439
- );
3540
+ }
3541
+ }
3440
3542
  }
3441
3543
  }
3442
3544
  }
3443
- brokenGenerators.forEach(
3444
- (node) => report({
3445
- node,
3446
- messageText: `Seems like you used yield instead of yield* inside this Effect.gen.`,
3447
- fixes: []
3448
- })
3449
- );
3450
- brokenYields.forEach((node) => {
3451
- const fix = node.expression ? [{
3452
- fixName: "missingStarInYieldEffectGen_fix",
3453
- description: "Replace yield with yield*",
3454
- apply: gen(function* () {
3455
- const changeTracker = yield* service(ChangeTracker);
3456
- changeTracker.replaceNode(
3457
- sourceFile,
3458
- node,
3459
- ts.factory.createYieldExpression(
3460
- ts.factory.createToken(ts.SyntaxKind.AsteriskToken),
3461
- node.expression
3462
- )
3463
- );
3464
- })
3465
- }] : [];
3466
- report({
3467
- node,
3468
- messageText: `When yielding Effects inside Effect.gen, you should use yield* instead of yield.`,
3469
- fixes: fix
3470
- });
3471
- });
3472
- })
3545
+ }
3546
+ return {
3547
+ getImportNamespaceByFileName: (fileName) => namespaceByFileName.get(fileName),
3548
+ isExcludedFromNamespaceImport: (fileName, exportName) => (excludedByFileName.get(exportName) || []).includes(fileName),
3549
+ getUnbarreledModulePath: (fileName, exportName) => unbarreledModulePathByFileName.get(fileName)?.find((_) => _.exportName === exportName)?.fileName,
3550
+ getBarreledModulePath: (fileName) => barreledModulePathByFileName.get(fileName)
3551
+ };
3473
3552
  });
3474
-
3475
- // src/diagnostics/returnEffectInGen.ts
3476
- var returnEffectInGen = createDiagnostic({
3477
- name: "returnEffectInGen",
3478
- code: 11,
3479
- severity: "suggestion",
3480
- apply: fn("returnEffectInGen.apply")(function* (sourceFile, report) {
3481
- const ts = yield* service(TypeScriptApi);
3482
- const typeChecker = yield* service(TypeCheckerApi);
3483
- const typeParser = yield* service(TypeParser);
3484
- const nodeToVisit = [];
3485
- const appendNodeToVisit = (node) => {
3486
- nodeToVisit.push(node);
3487
- return void 0;
3488
- };
3489
- ts.forEachChild(sourceFile, appendNodeToVisit);
3490
- while (nodeToVisit.length > 0) {
3491
- const node = nodeToVisit.shift();
3492
- ts.forEachChild(node, appendNodeToVisit);
3493
- if (ts.isReturnStatement(node) && node.expression) {
3494
- if (ts.isYieldExpression(node.expression)) continue;
3495
- const generatorOrRegularFunction = ts.findAncestor(
3496
- node,
3497
- (_) => ts.isFunctionExpression(_) || ts.isFunctionDeclaration(_) || ts.isMethodDeclaration(_) || ts.isArrowFunction(_) || ts.isGetAccessor(_)
3498
- );
3499
- if (!(generatorOrRegularFunction && "asteriskToken" in generatorOrRegularFunction && generatorOrRegularFunction.asteriskToken)) continue;
3500
- const type = typeChecker.getTypeAtLocation(node.expression);
3501
- const maybeEffect = yield* option(typeParser.strictEffectType(type, node.expression));
3502
- if (isSome2(maybeEffect)) {
3503
- if (generatorOrRegularFunction && generatorOrRegularFunction.parent) {
3504
- const effectGenNode = generatorOrRegularFunction.parent;
3505
- yield* pipe(
3506
- typeParser.effectGen(effectGenNode),
3507
- orElse2(() => typeParser.effectFnUntracedGen(effectGenNode)),
3508
- orElse2(() => typeParser.effectFnGen(effectGenNode)),
3509
- map4(() => {
3510
- const fix = node.expression ? [{
3511
- fixName: "returnEffectInGen_fix",
3512
- description: "Add yield* statement",
3513
- apply: gen(function* () {
3514
- const changeTracker = yield* service(ChangeTracker);
3515
- changeTracker.replaceNode(
3516
- sourceFile,
3517
- node.expression,
3518
- ts.factory.createYieldExpression(
3519
- ts.factory.createToken(ts.SyntaxKind.AsteriskToken),
3520
- node.expression
3521
- )
3522
- );
3523
- })
3524
- }] : [];
3525
- report({
3526
- node,
3527
- messageText: `You are returning an Effect-able type inside a generator function, and will result in nested Effect<Effect<...>>.
3528
- Maybe you wanted to return yield* instead?
3529
- Nested Effect-able types may be intended if you plan to later manually flatten or unwrap this Effect, if so you can safely disable this diagnostic for this line through quickfixes.`,
3530
- fixes: fix
3531
- });
3532
- }),
3533
- ignore
3534
- );
3553
+ var appendEffectCompletionEntryData = fn("appendEffectCompletionEntryData")(
3554
+ function* (_sourceFile, applicableCompletions) {
3555
+ const languageServicePluginOptions = yield* service(LanguageServicePluginOptions);
3556
+ if (languageServicePluginOptions.namespaceImportPackages.length === 0) return applicableCompletions;
3557
+ if (applicableCompletions) {
3558
+ return {
3559
+ ...applicableCompletions,
3560
+ entries: applicableCompletions.entries.map(
3561
+ (entry) => entry.data ? {
3562
+ ...entry,
3563
+ data: {
3564
+ ...entry.data,
3565
+ effectReplaceSpan: entry.replacementSpan || applicableCompletions.optionalReplacementSpan
3566
+ }
3567
+ } : entry
3568
+ )
3569
+ };
3570
+ }
3571
+ return applicableCompletions;
3572
+ }
3573
+ );
3574
+ var postprocessCompletionEntryDetails = fn("postprocessCompletionEntryDetails")(
3575
+ function* (sourceFile, data, applicableCompletionEntryDetails, formatOptions, preferences, languageServiceHost) {
3576
+ const languageServicePluginOptions = yield* service(LanguageServicePluginOptions);
3577
+ if (languageServicePluginOptions.namespaceImportPackages.length === 0) return applicableCompletionEntryDetails;
3578
+ const isAutoImportOnlyCodeActions = fn("isAutoImportOnlyCodeActions")(
3579
+ function* (codeActions, exportName2) {
3580
+ if (!codeActions) return;
3581
+ if (codeActions.length !== 1) return;
3582
+ const action = codeActions[0];
3583
+ const changes2 = action.changes;
3584
+ if (changes2.length !== 1) return;
3585
+ const fileTextChanges = action.changes[0];
3586
+ if (fileTextChanges.fileName !== sourceFile.fileName) return;
3587
+ const textChanges = fileTextChanges.textChanges;
3588
+ if (textChanges.length !== 1) return;
3589
+ const change = textChanges[0];
3590
+ if (change.newText.trim().toLowerCase().startsWith("import") && change.newText.indexOf(exportName2) > -1) {
3591
+ return {
3592
+ type: "create"
3593
+ };
3594
+ }
3595
+ if (change.newText.indexOf(exportName2) > -1) {
3596
+ const ancestorNodes = yield* getAncestorNodesInRange(sourceFile, {
3597
+ pos: change.span.start,
3598
+ end: change.span.start
3599
+ });
3600
+ const importNodes = ancestorNodes.filter((node) => ts.isImportDeclaration(node));
3601
+ if (importNodes.length > 0) {
3602
+ return {
3603
+ type: "update"
3604
+ };
3535
3605
  }
3536
3606
  }
3537
3607
  }
3538
- }
3539
- })
3540
- });
3541
-
3542
- // src/diagnostics/unnecessaryEffectGen.ts
3543
- var unnecessaryEffectGen = createDiagnostic({
3544
- name: "unnecessaryEffectGen",
3545
- code: 5,
3546
- severity: "suggestion",
3547
- apply: fn("unnecessaryEffectGen.apply")(function* (sourceFile, report) {
3608
+ );
3548
3609
  const ts = yield* service(TypeScriptApi);
3549
- const typeParser = yield* service(TypeParser);
3550
- const nodeToVisit = [];
3551
- const appendNodeToVisit = (node) => {
3552
- nodeToVisit.push(node);
3553
- return void 0;
3554
- };
3555
- ts.forEachChild(sourceFile, appendNodeToVisit);
3556
- while (nodeToVisit.length > 0) {
3557
- const node = nodeToVisit.shift();
3558
- ts.forEachChild(node, appendNodeToVisit);
3559
- if (ts.isCallExpression(node)) {
3560
- yield* pipe(
3561
- typeParser.unnecessaryEffectGen(node),
3562
- map4(
3563
- ({ replacementNode }) => report({
3564
- node,
3565
- messageText: `This Effect.gen contains a single return statement.`,
3566
- fixes: [{
3567
- fixName: "unnecessaryEffectGen_fix",
3568
- description: "Remove the Effect.gen, and keep the body",
3569
- apply: gen(function* () {
3570
- const textChanges = yield* service(
3571
- ChangeTracker
3572
- );
3573
- textChanges.replaceNode(sourceFile, node, yield* replacementNode);
3574
- })
3575
- }]
3576
- })
3610
+ const program = yield* service(TypeScriptProgram);
3611
+ const getModuleSpecifier = makeGetModuleSpecifier(ts);
3612
+ if (!getModuleSpecifier) return applicableCompletionEntryDetails;
3613
+ if (!applicableCompletionEntryDetails) return applicableCompletionEntryDetails;
3614
+ if (!data) return applicableCompletionEntryDetails;
3615
+ const { exportName, fileName, moduleSpecifier } = data;
3616
+ if (!fileName) return applicableCompletionEntryDetails;
3617
+ if (!exportName) return applicableCompletionEntryDetails;
3618
+ if (!moduleSpecifier) return applicableCompletionEntryDetails;
3619
+ if (!("effectReplaceSpan" in data)) return applicableCompletionEntryDetails;
3620
+ const effectReplaceSpan = data.effectReplaceSpan;
3621
+ const result = yield* isAutoImportOnlyCodeActions(applicableCompletionEntryDetails.codeActions, exportName);
3622
+ if (!result) return applicableCompletionEntryDetails;
3623
+ const packagesMetadata = importablePackagesMetadataCache.get(sourceFile.fileName) || (yield* makeImportablePackagesMetadata(sourceFile));
3624
+ importablePackagesMetadataCache.set(sourceFile.fileName, packagesMetadata);
3625
+ const formatContext = ts.formatting.getFormatContext(
3626
+ formatOptions || {},
3627
+ languageServiceHost
3628
+ );
3629
+ const isExcluded = packagesMetadata.isExcludedFromNamespaceImport(
3630
+ fileName,
3631
+ exportName
3632
+ );
3633
+ if (isExcluded) return applicableCompletionEntryDetails;
3634
+ const effectUnbarreledModulePath = packagesMetadata.getUnbarreledModulePath(
3635
+ fileName,
3636
+ exportName
3637
+ );
3638
+ const effectNamespaceName = packagesMetadata.getImportNamespaceByFileName(
3639
+ effectUnbarreledModulePath || fileName
3640
+ );
3641
+ if (!effectNamespaceName) return applicableCompletionEntryDetails;
3642
+ const newModuleSpecifier = effectUnbarreledModulePath ? getModuleSpecifier(
3643
+ program.getCompilerOptions(),
3644
+ sourceFile,
3645
+ sourceFile.fileName,
3646
+ String(effectUnbarreledModulePath || fileName),
3647
+ program
3648
+ ) : moduleSpecifier;
3649
+ const changes = ts.textChanges.ChangeTracker.with(
3650
+ {
3651
+ formatContext,
3652
+ host: languageServiceHost,
3653
+ preferences: preferences || {}
3654
+ },
3655
+ (changeTracker) => {
3656
+ ts.insertImports(
3657
+ changeTracker,
3658
+ sourceFile,
3659
+ ts.factory.createImportDeclaration(
3660
+ void 0,
3661
+ ts.factory.createImportClause(
3662
+ false,
3663
+ void 0,
3664
+ ts.factory.createNamespaceImport(ts.factory.createIdentifier(effectNamespaceName))
3665
+ ),
3666
+ ts.factory.createStringLiteral(newModuleSpecifier)
3577
3667
  ),
3578
- ignore
3668
+ true,
3669
+ preferences || {}
3579
3670
  );
3671
+ if (!effectUnbarreledModulePath) {
3672
+ changeTracker.insertText(
3673
+ sourceFile,
3674
+ effectReplaceSpan.start,
3675
+ effectNamespaceName + "."
3676
+ );
3677
+ }
3580
3678
  }
3581
- }
3582
- })
3583
- });
3584
-
3585
- // src/diagnostics/unnecessaryPipe.ts
3586
- var unnecessaryPipe = createDiagnostic({
3587
- name: "unnecessaryPipe",
3588
- code: 9,
3589
- severity: "suggestion",
3590
- apply: fn("unnecessaryPipe.apply")(function* (sourceFile, report) {
3591
- const ts = yield* service(TypeScriptApi);
3592
- const typeParser = yield* service(TypeParser);
3593
- const nodeToVisit = [];
3594
- const appendNodeToVisit = (node) => {
3595
- nodeToVisit.push(node);
3596
- return void 0;
3679
+ );
3680
+ return {
3681
+ ...applicableCompletionEntryDetails,
3682
+ codeActions: [
3683
+ {
3684
+ description: "Import * as " + effectNamespaceName + " from " + newModuleSpecifier,
3685
+ changes
3686
+ }
3687
+ ]
3597
3688
  };
3598
- ts.forEachChild(sourceFile, appendNodeToVisit);
3599
- while (nodeToVisit.length > 0) {
3600
- const node = nodeToVisit.shift();
3601
- ts.forEachChild(node, appendNodeToVisit);
3602
- if (ts.isCallExpression(node)) {
3603
- yield* pipe(
3604
- typeParser.pipeCall(node),
3605
- map4(({ args, subject }) => {
3606
- if (args.length === 0) {
3607
- report({
3608
- node,
3609
- messageText: `This pipe call contains no arguments.`,
3610
- fixes: [{
3611
- fixName: "unnecessaryPipe_fix",
3612
- description: "Remove the pipe call",
3613
- apply: gen(function* () {
3614
- const textChanges = yield* service(
3615
- ChangeTracker
3616
- );
3617
- textChanges.replaceNode(sourceFile, node, subject);
3618
- })
3619
- }]
3620
- });
3621
- }
3622
- }),
3623
- ignore
3624
- );
3625
- }
3626
- }
3627
- })
3628
- });
3629
-
3630
- // src/diagnostics.ts
3631
- var diagnostics = [
3632
- duplicatePackage,
3633
- missingEffectContext,
3634
- missingEffectError,
3635
- floatingEffect,
3636
- missingStarInYieldEffectGen,
3637
- unnecessaryEffectGen,
3638
- missingReturnYieldStar,
3639
- leakingRequirements,
3640
- unnecessaryPipe,
3641
- genericEffectServices,
3642
- returnEffectInGen,
3643
- importFromBarrel
3644
- ];
3689
+ }
3690
+ );
3645
3691
 
3646
3692
  // src/goto/effectRpcDefinition.ts
3647
3693
  function effectRpcDefinition(applicableGotoDefinition, sourceFile, position) {