Skip to content

Commit

Permalink
Use the TypeScript v5.5+ JSDoc tag @import to import types in modules.
Browse files Browse the repository at this point in the history
See:

https://devblogs.microsoft.com/typescript/announcing-typescript-5-5/#the-jsdoc-import-tag

Note that the JSDoc type imports have to be before the real module imports to workaround this TypeScript bug where sometimes the type imports are falsely considered unused:

microsoft/TypeScript#58368 (comment)
microsoft/TypeScript#58969
  • Loading branch information
jaydenseric committed Oct 4, 2024
1 parent 240666b commit 7ae5f19
Show file tree
Hide file tree
Showing 9 changed files with 93 additions and 44 deletions.
13 changes: 7 additions & 6 deletions Upload.mjs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
// @ts-check

/** @typedef {import("./GraphQLUpload.mjs").default} GraphQLUpload */
/** @typedef {import("./processRequest.mjs").default} processRequest */
/**
* @import GraphQLUpload from "./GraphQLUpload.mjs"
* @import processRequest, { FileUpload } from "./processRequest.mjs"
*/

/**
* A file expected to be uploaded as it was declared in the `map` field of a
Expand All @@ -15,21 +17,20 @@ export default class Upload {
/**
* Promise that resolves file upload details. This should only be utilized
* by {@linkcode GraphQLUpload}.
* @type {Promise<import("./processRequest.mjs").FileUpload>}
* @type {Promise<FileUpload>}
*/
this.promise = new Promise((resolve, reject) => {
/**
* Resolves the upload promise with the file upload details. This should
* only be utilized by {@linkcode processRequest}.
* @param {import("./processRequest.mjs").FileUpload} file File upload
* details.
* @param {FileUpload} file File upload details.
*/
this.resolve = (file) => {
/**
* The file upload details, available when the
* {@linkcode Upload.promise} resolves. This should only be utilized by
* {@linkcode processRequest}.
* @type {import("./processRequest.mjs").FileUpload | undefined}
* @type {FileUpload | undefined}
*/
this.file = file;

Expand Down
18 changes: 18 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,24 @@

- Updated Node.js support to `^18.18.0 || ^20.9.0 || >=22.0.0`.
- Updated dev dependencies, some of which require newer Node.js versions than previously supported.
- Use the TypeScript v5.5+ JSDoc tag `@import` to import types in modules.
- Removed JSDoc tag `@typedef` that were unintentionally re-exporting types; to migrate import TypeScript types from the correct module:

```diff
- import type { GraphQLUpload } from "graphql-upload/Upload.mjs";
+ import type GraphQLUpload from "graphql-upload/GraphQLUpload.mjs";
```

```diff
- import type { processRequest } from "graphql-upload/Upload.mjs";
+ import type processRequest from "graphql-upload/processRequest.mjs";
```

```diff
- import type { GraphQLUpload } from "graphql-upload/processRequest.mjs";
+ import type GraphQLUpload from "graphql-upload/GraphQLUpload.mjs";
```

- Refactored tests to use the standard `AbortController`, `fetch`, `File`, and `FormData` APIs available in modern Node.js and removed the dev dependencies [`node-abort-controller`](https://npm.im/node-abort-controller) and [`node-fetch`](https://npm.im/node-fetch).
- Replaced the test utility function `streamToString` with the function `text` from `node:stream/consumers` that’s available in modern Node.js.
- Use the Node.js test runner API and remove the dev dependency [`test-director`](https://npm.im/test-director).
Expand Down
18 changes: 13 additions & 5 deletions graphqlUploadExpress.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
// @ts-check

/**
* @import { NextFunction, Request, Response } from "express"
* @import {
* ProcessRequestFunction,
* ProcessRequestOptions,
* } from "./processRequest.mjs"
*/

import defaultProcessRequest from "./processRequest.mjs";

/**
Expand All @@ -8,8 +16,8 @@ import defaultProcessRequest from "./processRequest.mjs";
* using {@linkcode processRequest}, ignoring non multipart requests. It sets
* the request `body` to be similar to a conventional GraphQL POST request for
* following GraphQL middleware to consume.
* @param {import("./processRequest.mjs").ProcessRequestOptions & {
* processRequest?: import("./processRequest.mjs").ProcessRequestFunction
* @param {ProcessRequestOptions & {
* processRequest?: ProcessRequestFunction,
* }} options Options.
* @returns Express middleware.
* @example
Expand Down Expand Up @@ -41,9 +49,9 @@ export default function graphqlUploadExpress({
* using {@linkcode processRequest}, ignoring non multipart requests. It sets
* the request `body` to be similar to a conventional GraphQL POST request for
* following GraphQL middleware to consume.
* @param {import("express").Request} request
* @param {import("express").Response} response
* @param {import("express").NextFunction} next
* @param {Request} request
* @param {Response} response
* @param {NextFunction} next
*/
function graphqlUploadExpressMiddleware(request, response, next) {
if (!request.is("multipart/form-data")) return next();
Expand Down
21 changes: 13 additions & 8 deletions graphqlUploadExpress.test.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
// @ts-check

/**
* @import { NextFunction, Request, Response } from "express"
* @import Upload from "./Upload.mjs"
*/

import "./test/polyfillFile.mjs";

import { deepStrictEqual, ok, strictEqual } from "node:assert";
Expand Down Expand Up @@ -46,7 +51,7 @@ describe(
/**
* @type {{
* variables: {
* file: import("./Upload.mjs").default,
* file: Upload,
* },
* } | undefined}
*/
Expand Down Expand Up @@ -88,7 +93,7 @@ describe(
/**
* @type {{
* variables: {
* file: import("./Upload.mjs").default,
* file: Upload,
* },
* } | undefined}
*/
Expand Down Expand Up @@ -164,9 +169,9 @@ describe(
.use(
/**
* @param {Error} error
* @param {import("express").Request} request
* @param {import("express").Response} response
* @param {import("express").NextFunction} next
* @param {Request} request
* @param {Response} response
* @param {NextFunction} next
*/
(error, request, response, next) => {
expressError = error;
Expand Down Expand Up @@ -232,9 +237,9 @@ describe(
.use(
/**
* @param {Error} error
* @param {import("express").Request} request
* @param {import("express").Response} response
* @param {import("express").NextFunction} next
* @param {Request} request
* @param {Response} response
* @param {NextFunction} next
*/
(error, request, response, next) => {
expressError = error;
Expand Down
14 changes: 11 additions & 3 deletions graphqlUploadKoa.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
// @ts-check

/**
* @import { ParameterizedContext } from "koa"
* @import {
* ProcessRequestFunction,
* ProcessRequestOptions,
* } from "./processRequest.mjs"
*/

import defaultProcessRequest from "./processRequest.mjs";

/**
Expand All @@ -8,8 +16,8 @@ import defaultProcessRequest from "./processRequest.mjs";
* using {@linkcode processRequest}, ignoring non multipart requests. It sets
* the request `body` to be similar to a conventional GraphQL POST request for
* following GraphQL middleware to consume.
* @param {import("./processRequest.mjs").ProcessRequestOptions & {
* processRequest?: import("./processRequest.mjs").ProcessRequestFunction
* @param {ProcessRequestOptions & {
* processRequest?: ProcessRequestFunction,
* }} options Options.
* @returns Koa middleware.
* @example
Expand Down Expand Up @@ -42,7 +50,7 @@ export default function graphqlUploadKoa({
* using {@linkcode processRequest}, ignoring non multipart requests. It sets
* the request `body` to be similar to a conventional GraphQL POST request for
* following GraphQL middleware to consume.
* @param {import("koa").ParameterizedContext} ctx
* @param {ParameterizedContext} ctx
* @param {() => Promise<unknown>} next
*/
async function graphqlUploadKoaMiddleware(ctx, next) {
Expand Down
6 changes: 4 additions & 2 deletions graphqlUploadKoa.test.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
// @ts-check

/** @import Upload from "./Upload.mjs" */

import "./test/polyfillFile.mjs";

import { deepStrictEqual, ok, strictEqual } from "node:assert";
Expand Down Expand Up @@ -48,7 +50,7 @@ describe(
/**
* @type {{
* variables: {
* file: import("./Upload.mjs").default,
* file: Upload,
* },
* } | undefined}
*/
Expand Down Expand Up @@ -94,7 +96,7 @@ describe(
/**
* @type {{
* variables: {
* file: import("./Upload.mjs").default,
* file: Upload,
* },
* } | undefined}
*/
Expand Down
4 changes: 3 additions & 1 deletion ignoreStream.mjs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
// @ts-check

/** @import { Readable } from "node:stream" */

/**
* Safely ignores a Node.js readable stream.
* @param {import("node:stream").Readable} stream Node.js readable stream.
* @param {Readable} stream Node.js readable stream.
*/
export default function ignoreStream(stream) {
// Prevent an unhandled error from crashing the process.
Expand Down
39 changes: 21 additions & 18 deletions processRequest.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
// @ts-check

/**
* @import { IncomingMessage, ServerResponse } from "node:http"
* @import { Readable } from "node:stream"
* @import { ReadStreamOptions } from "fs-capacitor"
* @import { ObjectPathBound } from "object-path"
* @import GraphQLUpload from "./GraphQLUpload.mjs"
*/

import busboy from "busboy";
import { WriteStream } from "fs-capacitor";
import createError from "http-errors";
Expand All @@ -9,8 +17,6 @@ import GRAPHQL_MULTIPART_REQUEST_SPEC_URL from "./GRAPHQL_MULTIPART_REQUEST_SPEC
import ignoreStream from "./ignoreStream.mjs";
import Upload from "./Upload.mjs";

/** @typedef {import("./GraphQLUpload.mjs").default} GraphQLUpload */

/**
* Processes an incoming
* [GraphQL multipart request](https://github.com/jaydenseric/graphql-multipart-request-spec).
Expand Down Expand Up @@ -46,7 +52,7 @@ export default function processRequest(
let operations;

/**
* @type {import("object-path").ObjectPathBound<
* @type {ObjectPathBound<
* { [key: string]: unknown } | Array<{ [key: string]: unknown }>
* >}
*/
Expand Down Expand Up @@ -354,9 +360,8 @@ export default function processRequest(
* @prop {string} mimetype File MIME type. Provided by the client and can’t be
* trusted.
* @prop {string} encoding File stream transfer encoding.
* @prop {import("fs-capacitor").WriteStream} capacitor A private implementation
* detail that shouldn’t be used outside
* [`graphql-upload`](https://npm.im/graphql-upload).
* @prop {WriteStream} capacitor A private implementation detail that shouldn’t
* be used outside [`graphql-upload`](https://npm.im/graphql-upload).
* @prop {FileUploadCreateReadStream} createReadStream Creates a
* [Node.js readable stream](https://nodejs.org/api/stream.html#readable-streams)
* of the file’s contents, for processing and storage.
Expand All @@ -370,7 +375,7 @@ export default function processRequest(
* all resolvers have resolved, or after an error has interrupted the request.
* @callback FileUploadCreateReadStream
* @param {FileUploadCreateReadStreamOptions} [options] Options.
* @returns {import("node:stream").Readable}
* @returns {Readable}
* [Node.js readable stream](https://nodejs.org/api/stream.html#readable-streams)
* of the file’s contents.
* @see [Node.js `Readable` stream constructor docs](https://nodejs.org/api/stream.html#new-streamreadableoptions).
Expand All @@ -380,28 +385,26 @@ export default function processRequest(
/**
* {@linkcode FileUploadCreateReadStream} options.
* @typedef {object} FileUploadCreateReadStreamOptions
* @prop {import("fs-capacitor")
* .ReadStreamOptions["encoding"]} [options.encoding] Specify an encoding for
* the [`data`](https://nodejs.org/api/stream.html#event-data) chunks to be
* strings (without splitting multi-byte characters across chunks) instead of
* Node.js [`Buffer`](https://nodejs.org/api/buffer.html#buffer) instances.
* @prop {ReadStreamOptions["encoding"]} [options.encoding] Specify an encoding
* for the [`data`](https://nodejs.org/api/stream.html#event-data) chunks to
* be strings (without splitting multi-byte characters across chunks) instead
* of Node.js [`Buffer`](https://nodejs.org/api/buffer.html#buffer) instances.
* Supported values depend on the
* [`Buffer` implementation](https://github.com/nodejs/node/blob/v18.1.0/lib/buffer.js#L590-L680)
* and include `utf8`, `ucs2`, `utf16le`, `latin1`, `ascii`, `base64`,
* `base64url`, or `hex`. Defaults to `utf8`.
* @prop {import("fs-capacitor")
* .ReadStreamOptions["highWaterMark"]} [options.highWaterMark] Maximum number
* of bytes to store in the internal buffer before ceasing to read from the
* underlying resource. Defaults to `16384`.
* @prop {ReadStreamOptions["highWaterMark"]} [options.highWaterMark] Maximum
* number of bytes to store in the internal buffer before ceasing to read from
* the underlying resource. Defaults to `16384`.
*/

/**
* Processes an incoming
* [GraphQL multipart request](https://github.com/jaydenseric/graphql-multipart-request-spec).
* @callback ProcessRequestFunction
* @param {import("node:http").IncomingMessage} request
* @param {IncomingMessage} request
* [Node.js HTTP server request instance](https://nodejs.org/api/http.html#http_class_http_incomingmessage).
* @param {import("node:http").ServerResponse} response
* @param {ServerResponse} response
* [Node.js HTTP server response instance](https://nodejs.org/api/http.html#http_class_http_serverresponse).
* @param {ProcessRequestOptions} [options] Options.
* @returns {Promise<
Expand Down
4 changes: 3 additions & 1 deletion test/CountReadableStream.mjs
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
// @ts-check

/** @import { ReadableOptions } from "node:stream" */

import { Readable } from "node:stream";

/**
* A count readable stream, for testing purposes.
* @see [Example counting stream in the Node.js docs](https://nodejs.org/api/stream.html#an-example-counting-stream).
*/
export default class CountReadableStream extends Readable {
/** @param {import("node:stream").ReadableOptions} [options] */
/** @param {ReadableOptions} [options] */
constructor(options) {
super(options);
this._max = 1000000;
Expand Down

0 comments on commit 7ae5f19

Please sign in to comment.