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/fix-http-pre-response-handler-types.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"effect": patch
---

Fix the published declaration for `HttpEffect.appendPreResponseHandlerUnsafe`.
25 changes: 13 additions & 12 deletions packages/effect/src/unstable/http/HttpEffect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import { HttpServerRequest } from "./HttpServerRequest.ts"
import * as Request from "./HttpServerRequest.ts"
import type { HttpServerResponse } from "./HttpServerResponse.ts"
import * as Response from "./HttpServerResponse.ts"
import { appendPreResponseHandlerUnsafe, requestPreResponseHandlers } from "./internal/preResponseHandler.ts"
import * as preResponseHandler from "./internal/preResponseHandler.ts"

/**
* Runs an HTTP server effect, sends the produced response with the supplied handler, and converts failures into HTTP responses.
Expand All @@ -46,7 +46,7 @@ export const toHandled = <E, R, EH, RH>(
const fiber = Fiber.getCurrent()!
reportCauseUnsafe(fiber, cause)
const request = Context.getUnsafe(fiber.context, HttpServerRequest)
const handler = requestPreResponseHandlers.get(request.source)
const handler = preResponseHandler.requestPreResponseHandlers.get(request.source)
const cont = cause.reasons.length === 0 ? Effect.succeed(response) : Effect.failCause(cause)
if (handler === undefined) {
;(request as any)[handledSymbol] = true
Expand All @@ -69,7 +69,7 @@ export const toHandled = <E, R, EH, RH>(
onSuccess: (response) => {
const fiber = Fiber.getCurrent()!
const request = Context.getUnsafe(fiber.context, HttpServerRequest)
const handler = requestPreResponseHandlers.get(request.source)
const handler = preResponseHandler.requestPreResponseHandlers.get(request.source)
if (handler === undefined) {
;(request as any)[handledSymbol] = true
return Effect.mapEager(handleResponse(request, response), () => response)
Expand Down Expand Up @@ -192,15 +192,16 @@ export const appendPreResponseHandler = (handler: PreResponseHandler): Effect.Ef
return Effect.void
})

export {
/**
* Registers a pre-response handler for the supplied HTTP server request.
*
* @category fiber refs
* @since 4.0.0
*/
appendPreResponseHandlerUnsafe
}
/**
* Registers a pre-response handler for the supplied HTTP server request.
*
* @category fiber refs
* @since 4.0.0
*/
export const appendPreResponseHandlerUnsafe: (
request: HttpServerRequest,
handler: PreResponseHandler
) => void = preResponseHandler.appendPreResponseHandlerUnsafe

/**
* Runs an effect after registering a pre-response handler for the current HTTP server request.
Expand Down
72 changes: 66 additions & 6 deletions packages/tools/oxc/src/oxlint/rules/no-unused-internal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,33 @@ function getExports(
return fileInfos.get(fileName)?.internalExports.get(exportName) ?? []
}

function getReExportedInternals(
fileInfos: ReadonlyMap<string, FileInfo>,
fileInfo: FileInfo,
workspacePackages: ReadonlyMap<string, string>,
specifier: ts.ExportSpecifier
): ReadonlyArray<InternalExport> {
const exportDeclaration = specifier.parent.parent
const importedName = (specifier.propertyName ?? specifier.name).text
if (
exportDeclaration.moduleSpecifier !== undefined &&
ts.isStringLiteral(exportDeclaration.moduleSpecifier)
) {
const importedFile = resolveModule(
exportDeclaration.moduleSpecifier.text,
fileInfo.sourceFile.fileName,
workspacePackages
)
return getExports(fileInfos, importedFile, importedName)
}

const imported = fileInfo.imports.get(importedName)
return [
...(fileInfo.internalExports.get(importedName) ?? []),
...getExports(fileInfos, imported?.fileName, imported?.importedName ?? "")
]
}

function markUsed(exports: ReadonlyArray<InternalExport>, node: ts.Node) {
for (const internal of exports) {
if (!isInside(node, internal.declaration)) {
Expand Down Expand Up @@ -348,6 +375,17 @@ function collectFileInfo(
continue
}

if (
!isInternalFile &&
ts.isExportDeclaration(statement) &&
!hasInternalApiJSDoc(statement) &&
statement.exportClause !== undefined &&
ts.isNamedExports(statement.exportClause)
) {
fileInfo.publicDeclarations.push(statement)
continue
}

if (!hasExportModifier(statement)) continue

const names = getTopLevelDeclarationNameNodes(statement)
Expand Down Expand Up @@ -385,9 +423,15 @@ function markNamespaceReference(
markUsed(getExports(fileInfos, importedFile, name.text), name)
}

function scanUsage(fileInfos: ReadonlyMap<string, FileInfo>, fileInfo: FileInfo) {
function scanUsage(
fileInfos: ReadonlyMap<string, FileInfo>,
fileInfo: FileInfo,
workspacePackages: ReadonlyMap<string, string>
) {
const visit = (node: ts.Node) => {
if (ts.isPropertyAccessExpression(node) && ts.isIdentifier(node.expression) && ts.isIdentifier(node.name)) {
if (ts.isExportSpecifier(node)) {
markUsed(getReExportedInternals(fileInfos, fileInfo, workspacePackages, node), node)
} else if (ts.isPropertyAccessExpression(node) && ts.isIdentifier(node.expression) && ts.isIdentifier(node.name)) {
markNamespaceReference(fileInfos, fileInfo, node.expression, node.name)
} else if (ts.isQualifiedName(node) && ts.isIdentifier(node.left)) {
markNamespaceReference(fileInfos, fileInfo, node.left, node.right)
Expand Down Expand Up @@ -529,7 +573,8 @@ function scanPublicSignature(
fileInfos: ReadonlyMap<string, FileInfo>,
fileInfo: FileInfo,
diagnostics: Array<Diagnostic>,
node: ts.Node
node: ts.Node,
workspacePackages: ReadonlyMap<string, string>
) {
if (ts.isFunctionDeclaration(node)) {
scanFunctionLikeSignature(fileInfos, fileInfo, diagnostics, node)
Expand Down Expand Up @@ -562,6 +607,21 @@ function scanPublicSignature(
for (const member of node.members) {
scanClassMemberSignature(fileInfos, fileInfo, diagnostics, member)
}
} else if (
ts.isExportDeclaration(node) &&
node.exportClause !== undefined &&
ts.isNamedExports(node.exportClause)
) {
for (const specifier of node.exportClause.elements) {
for (const internal of getReExportedInternals(fileInfos, fileInfo, workspacePackages, specifier)) {
const name = specifier.propertyName ?? specifier.name
diagnostics.push({
fileName: fileInfo.fileName,
range: [name.getStart(), name.getEnd()],
message: `Do not re-export @internal export "${internal.name}" from a public module`
})
}
}
}
}

Expand All @@ -579,15 +639,15 @@ function analyze(cwd: string): Analysis {
}

for (const fileInfo of fileInfos.values()) {
scanUsage(fileInfos, fileInfo)
scanUsage(fileInfos, fileInfo, workspacePackages)
}

const diagnosticsByFile = new Map<string, Array<Diagnostic>>()

for (const fileInfo of fileInfos.values()) {
const diagnostics: Array<Diagnostic> = []
for (const declaration of fileInfo.publicDeclarations) {
scanPublicSignature(fileInfos, fileInfo, diagnostics, declaration)
scanPublicSignature(fileInfos, fileInfo, diagnostics, declaration, workspacePackages)
}
for (const diagnostic of diagnostics) {
addFileDiagnostic(diagnosticsByFile, diagnostic)
Expand Down Expand Up @@ -619,7 +679,7 @@ const rule: CreateRule = {
meta: {
type: "problem",
docs: {
description: "Disallow unused @internal exports and references to @internal exports from public type signatures"
description: "Disallow unused @internal exports, public re-exports, and references from public type signatures"
}
},
create(context) {
Expand Down
15 changes: 15 additions & 0 deletions packages/tools/oxc/test/no-unused-internal.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,21 @@ export interface Public {
expect(run(cwd, "packages/foo/src/Internal.ts")).toEqual([])
})

it("reports public re-exports of @internal exports", () => {
const cwd = writeFixture({
"packages/foo/src/Internal.ts": `/** @internal */
export const internal = 1
`,
"packages/foo/src/Public.ts": `export { internal } from "./Internal.ts"
`
})

expect(run(cwd, "packages/foo/src/Public.ts")).toEqual([
`Do not re-export @internal export "internal" from a public module`
])
expect(run(cwd, "packages/foo/src/Internal.ts")).toEqual([])
})

it("ignores @internal class member type signatures", () => {
const cwd = writeFixture({
"packages/foo/src/Internal.ts": `/** @internal */
Expand Down
Loading