Remove form-data from cli-kit/app#7729
Conversation
Co-Authored-By: Claude <noreply@anthropic.com>
3498f09 to
6a36194
Compare
Differences in type declarationsWe detected differences in the type declarations generated by Typescript for this branch compared to the baseline ('main' branch). Please, review them to ensure they are backward-compatible. Here are some important things to keep in mind:
New type declarationsWe found no new type declarations in this PR Existing type declarationspackages/cli-kit/dist/private/node/api.d.ts@@ -1,4 +1,15 @@
-import { Headers } from 'form-data';
+/**
+ * A structural type for response headers that the request helpers below can iterate.
+ *
+ * "Any" means any HTTP client's headers. Our two callers pass node-fetch's and the
+ * global/WHATWG , which aren't assignable to each other, so we can't use one concrete
+ * type. Instead we describe only the minimal surface we use — (to collect interesting
+ * headers for debug logging) and an optional — which both satisfy.
+ */
+export interface AnyHeaders {
+ forEach: (callback: (value: any, key: any) => void) => void;
+ get?: (name: string) => string | null;
+}
export type API = 'admin' | 'storefront-renderer' | 'partners' | 'business-platform' | 'app-management';
export declare const allAPIs: API[];
export type NetworkRetryBehaviour = {
@@ -38,7 +49,7 @@ export declare function isTransientNetworkError(error: unknown): boolean;
*/
export declare function isNetworkError(error: unknown): boolean;
export declare function simpleRequestWithDebugLog<T extends {
- headers: Headers;
+ headers: AnyHeaders;
status: number;
}>(requestOptions: RequestOptions<T>, errorHandler?: (error: unknown, requestId: string | undefined) => unknown): Promise<T>;
/**
@@ -59,7 +70,7 @@ export declare function simpleRequestWithDebugLog<T extends {
* @returns The response from the request
*/
export declare function retryAwareRequest<T extends {
- headers: Headers;
+ headers: AnyHeaders;
status: number;
}>(requestOptions: RequestOptions<T>, errorHandler?: (error: unknown, requestId: string | undefined) => unknown, retryOptions?: {
limitRetriesTo?: number;
packages/cli-kit/dist/public/node/http.d.ts@@ -1,5 +1,4 @@
import { NetworkRetryBehaviour } from '../../private/node/api.js';
-import FormData from 'form-data';
import { RequestInfo, RequestInit, Response } from 'node-fetch';
export { FetchError, Request, Response } from 'node-fetch';
/**
|
There was a problem hiding this comment.
Pull request overview
This pull request removes the form-data dependency from @shopify/cli-kit by switching to native Node (WHATWG) FormData and replacing the prior form-data-sourced Headers type with a structural headers interface. It also updates app upload code/tests to avoid mocking formData() and adjusts the GCS upload request to set Content-Type without form-data.
Changes:
- Drop
form-datafrom@shopify/cli-kitdependencies and lockfile. - Replace
form-data’sHeaderstype usage in request helpers with a structuralAnyHeaderstype. - Update GCS upload code path and related tests to no longer depend on
formData()mocks.
Reviewed changes
Copilot reviewed 7 out of 8 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| pnpm-lock.yaml | Removes form-data and includes broader lockfile normalization changes. |
| packages/cli-kit/src/public/node/http.ts | Removes form-data import; relies on native FormData via formData(). |
| packages/cli-kit/src/public/node/http.test.ts | Updates formData() test to no longer use form-data-specific APIs. |
| packages/cli-kit/src/private/node/api.ts | Introduces structural AnyHeaders and updates generics to use it. |
| packages/cli-kit/package.json | Removes form-data dependency entry. |
| packages/app/src/cli/services/dev/processes/dev-session/dev-session-process.test.ts | Removes formData() mocking that depended on getHeaders(). |
| packages/app/src/cli/services/deploy/upload.test.ts | Removes formData() mocking that depended on getHeaders(). |
| packages/app/src/cli/services/bundle.ts | Changes GCS upload request headers to no longer use form-data’s header generation. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| export interface AnyHeaders { | ||
| // eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
| forEach: (callback: (value: any, key: any) => void) => void | ||
| get?: (name: string) => string | null | ||
| } |
| import nodeFetch, {RequestInfo, RequestInit, Response} from 'node-fetch' | ||
|
|
| const buffer = readFileSync(filePath) | ||
| form.append('my_upload', buffer) | ||
| await fetch(signedURL, {method: 'put', body: buffer, headers: form.getHeaders()}, 'slow-request') | ||
| await fetch( | ||
| signedURL, | ||
| { | ||
| method: 'put', | ||
| body: buffer, | ||
| headers: { | ||
| 'content-type': 'multipart/form-data; boundary=---shopify-cli-upload-boundary---', | ||
| }, | ||
| }, |
| describe('formData', () => { | ||
| test('make an empty form data object', () => { | ||
| const res = formData() | ||
| expect(res).toBeInstanceOf(FormData) | ||
| expect(res.getLengthSync()).toBe(0) | ||
| }) |
| find-up: | ||
| specifier: 6.3.0 | ||
| version: 6.3.0 | ||
| form-data: | ||
| specifier: 4.0.4 | ||
| version: 4.0.4 | ||
| fs-extra: | ||
| specifier: 11.1.0 | ||
| version: 11.1.0 |
Remove
form-datafrom@shopify/cli-kitWhy: Part of an initiative to cut low-value dependency churn (14 Dependabot bumps / 24 months).
Replacement: Native
FormData(Node ≥ 22.12) + structural typing.api.ts: replaced theform-dataHeaderstype import with a structural type.bundle.ts(GCS upload): kept as a rawPUTwith a manually-setContent-Typeboundary — wire behavior is preserved (verified against existing upload tests).formData()helper now returns a nativeFormData, which drops.getHeaders()and.getBoundary()that theform-datainstance exposed. This is a public-surface change — please confirm no external consumer relies on those methods.Validation:
type-check✅,lint✅,vitest✅ (93 tests, includingdeploy/uploadanddev-session-process).🤖 AI-generated draft — needs human review before merge.