Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/fair-ducks-applaud.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@effect/language-service": patch
---

Update the Effect v4 beta dependencies to `4.0.0-beta.43` for the language service and v4 harness packages.
2 changes: 1 addition & 1 deletion packages/harness-effect-v4/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
},
"dependencies": {
"@standard-schema/spec": "^1.1.0",
"effect": "^4.0.0-beta.38"
"effect": "^4.0.0-beta.43"
},
"devDependencies": {
"@types/node": "^25.0.6"
Expand Down
4 changes: 2 additions & 2 deletions packages/language-service/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,10 @@
"perf": "tsx test/perf.ts"
},
"devDependencies": {
"@effect/platform-node": "^4.0.0-beta.38",
"@effect/platform-node": "^4.0.0-beta.43",
"@types/pako": "^2.0.4",
"@typescript-eslint/project-service": "^8.52.0",
"effect": "^4.0.0-beta.38",
"effect": "^4.0.0-beta.43",
"pako": "^2.1.0",
"ts-patch": "^3.3.0"
}
Expand Down
1 change: 1 addition & 0 deletions packages/language-service/src/cli/overview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ export function collectExportedItems(
// Check if it's a Context.Tag (has _Identifier and _Service variance)
const contextTagResult = yield* pipe(
typeParser.contextTag(type, declaration),
Nano.orElse(() => typeParser.serviceType(type, declaration)),
Nano.option
)
if (Option.isSome(contextTagResult)) {
Expand Down
46 changes: 20 additions & 26 deletions packages/language-service/src/core/TypeParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1628,40 +1628,32 @@ export function make(
([Identifier, Service]) => ({ Identifier, Service })
)

const serviceVarianceStruct = (
type: ts.Type,
atLocation: ts.Node
) =>
Nano.map(
Nano.all(
varianceStructInvariantType(type, atLocation, "_Identifier"),
varianceStructInvariantType(type, atLocation, "_Service")
),
([Identifier, Service]) => ({ Identifier, Service })
)

const serviceType = Nano.cachedBy(
Nano.fn("TypeParser.serviceType")(function*(
type: ts.Type,
atLocation: ts.Node
) {
// v4 only
if (supportedEffect() !== "v4") return yield* typeParserIssue("v4 only")
// should be pipeable
yield* pipeableType(type, atLocation)
// get the properties to check (exclude non-property and optional properties)
const propertiesSymbols = typeChecker.getPropertiesOfType(type).filter((_) =>
_.flags & ts.SymbolFlags.Property && !(_.flags & ts.SymbolFlags.Optional) && _.valueDeclaration
)
// early exit
if (propertiesSymbols.length === 0) {
return yield* typeParserIssue("Type has no tag variance struct", type, atLocation)
// Effect v4 beta.43 switched ServiceMap keys from nested variance markers
const typeIdSymbol = typeChecker.getPropertyOfType(type, "~effect/ServiceMap/Service")
if (!typeIdSymbol) {
return yield* typeParserIssue("Type has no service key type id", type, atLocation)
}
// try to put typeid first (heuristic to optimize hot path)
propertiesSymbols.sort((a, b) => ts.symbolName(b).indexOf("TypeId") - ts.symbolName(a).indexOf("TypeId"))
// has a property symbol which is a service variance struct
return yield* Nano.firstSuccessOf(propertiesSymbols.map((propertySymbol) => {
const propertyType = typeChecker.getTypeOfSymbolAtLocation(propertySymbol, atLocation)
return serviceVarianceStruct(propertyType, atLocation)
}))
const identifierSymbol = typeChecker.getPropertyOfType(type, "Identifier")
if (!identifierSymbol) {
return yield* typeParserIssue("Type has no 'Identifier' property", type, atLocation)
}
const serviceSymbol = typeChecker.getPropertyOfType(type, "Service")
if (!serviceSymbol) {
return yield* typeParserIssue("Type has no 'Service' property", type, atLocation)
}
return ({
Identifier: typeChecker.getTypeOfSymbolAtLocation(identifierSymbol, atLocation),
Service: typeChecker.getTypeOfSymbolAtLocation(serviceSymbol, atLocation)
})
}),
"TypeParser.serviceType",
(type) => type
Expand All @@ -1672,6 +1664,8 @@ export function make(
type: ts.Type,
atLocation: ts.Node
) {
// v4 only
if (supportedEffect() !== "v3") return yield* typeParserIssue("v3 only")
// should be pipeable
yield* pipeableType(type, atLocation)
// get the properties to check (exclude non-property and optional properties)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export const genericEffectServices = LSP.createDiagnostic({
for (const [type, reportAt] of typesToCheck) {
yield* pipe(
typeParser.contextTag(type, node),
Nano.orElse(() => typeParser.serviceType(type, node)),
Nano.map(() => {
report({
location: reportAt,
Expand Down
34 changes: 17 additions & 17 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading