Skip to content

Remove form-data from cli-kit/app#7729

Open
amcaplan wants to merge 1 commit into
mainfrom
remove-dep/form-data
Open

Remove form-data from cli-kit/app#7729
amcaplan wants to merge 1 commit into
mainfrom
remove-dep/form-data

Conversation

@amcaplan
Copy link
Copy Markdown
Contributor

@amcaplan amcaplan commented Jun 5, 2026

Remove form-data from @shopify/cli-kit

Why: 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 the form-data Headers type import with a structural type.
  • bundle.ts (GCS upload): kept as a raw PUT with a manually-set Content-Type boundary — wire behavior is preserved (verified against existing upload tests).

⚠️ Reviewer callouts (MEDIUM confidence — review carefully):

  • The public formData() helper now returns a native FormData, which drops .getHeaders() and .getBoundary() that the form-data instance exposed. This is a public-surface change — please confirm no external consumer relies on those methods.
  • This branch was implemented partly via a fallback agent path (the primary launch hit a transient infra timeout); the diff scope has been verified to touch only the expected files.

Validation: type-check ✅, lint ✅, vitest ✅ (93 tests, including deploy/upload and dev-session-process).

🤖 AI-generated draft — needs human review before merge.

@github-actions github-actions Bot added the Area: @shopify/cli @shopify/cli package issues label Jun 5, 2026
@amcaplan amcaplan added dependency-removal Removes a dependency to cut Dependabot churn (cleanup initiative) claudeception Pull request created by Claudeception agents labels Jun 5, 2026
Co-Authored-By: Claude <noreply@anthropic.com>
@amcaplan amcaplan force-pushed the remove-dep/form-data branch from 3498f09 to 6a36194 Compare June 7, 2026 16:10
@github-actions github-actions Bot added no-changelog This PR doesn't include a changeset entry. Is an internal only change not relevant to end users. and removed Area: @shopify/cli @shopify/cli package issues labels Jun 7, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 7, 2026

Differences in type declarations

We 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:

  • Some seemingly private modules might be re-exported through public modules.
  • If the branch is behind main you might see odd diffs, rebase main into this branch.

New type declarations

We found no new type declarations in this PR

Existing type declarations

packages/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';
 /**

@amcaplan amcaplan marked this pull request as ready for review June 7, 2026 18:11
@amcaplan amcaplan requested a review from a team as a code owner June 7, 2026 18:11
Copilot AI review requested due to automatic review settings June 7, 2026 18:11
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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-data from @shopify/cli-kit dependencies and lockfile.
  • Replace form-data’s Headers type usage in request helpers with a structural AnyHeaders type.
  • 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.

Comment on lines +18 to +22
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
}
Comment on lines 11 to 12
import nodeFetch, {RequestInfo, RequestInit, Response} from 'node-fetch'

Comment on lines 47 to +56
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---',
},
},
Comment on lines 78 to 82
describe('formData', () => {
test('make an empty form data object', () => {
const res = formData()
expect(res).toBeInstanceOf(FormData)
expect(res.getLengthSync()).toBe(0)
})
Comment thread pnpm-lock.yaml
Comment on lines 378 to 383
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

claudeception Pull request created by Claudeception agents dependency-removal Removes a dependency to cut Dependabot churn (cleanup initiative) no-changelog This PR doesn't include a changeset entry. Is an internal only change not relevant to end users.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants